pageflow 17.0.5 → 17.1.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 (488) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +362 -35
  3. data/README.md +0 -1
  4. data/admins/pageflow/accounts.rb +16 -14
  5. data/admins/pageflow/entry.rb +68 -20
  6. data/admins/pageflow/entry_templates.rb +10 -9
  7. data/admins/pageflow/folder.rb +1 -1
  8. data/admins/pageflow/membership.rb +4 -4
  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/javascripts/pageflow/admin/entries.js +30 -5
  15. data/app/assets/javascripts/pageflow/dist/ui.js +2865 -257
  16. data/app/assets/stylesheets/pageflow/admin/entries/index_table.scss +2 -1
  17. data/app/assets/stylesheets/pageflow/admin/permalink_input.scss +2 -1
  18. data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +6 -0
  19. data/app/assets/stylesheets/pageflow/editor/base.scss +2 -0
  20. data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +7 -1
  21. data/app/assets/stylesheets/pageflow/editor/file_meta_data.scss +5 -1
  22. data/app/assets/stylesheets/pageflow/editor/files.scss +28 -16
  23. data/app/assets/stylesheets/pageflow/editor/filtered_files.scss +24 -0
  24. data/app/assets/stylesheets/pageflow/editor/info_box.scss +11 -3
  25. data/app/assets/stylesheets/pageflow/editor/list.scss +6 -4
  26. data/app/assets/stylesheets/pageflow/editor/list_search_field.scss +52 -0
  27. data/app/assets/stylesheets/pageflow/editor/outline.scss +0 -13
  28. data/app/assets/stylesheets/pageflow/editor/select_button.scss +1 -1
  29. data/app/assets/stylesheets/pageflow/editor/sortable.scss +12 -0
  30. data/app/assets/stylesheets/pageflow/mixins/buttons.scss +4 -4
  31. data/app/assets/stylesheets/pageflow/ui/forms.scss +4 -0
  32. data/app/assets/stylesheets/pageflow/ui/input/color_input.scss +8 -0
  33. data/app/assets/stylesheets/pageflow/ui/input/file_name_input.scss +37 -0
  34. data/app/assets/stylesheets/pageflow/ui/properties.scss +5 -0
  35. data/app/assets/stylesheets/pageflow/ui.scss +1 -0
  36. data/app/controllers/concerns/pageflow/controller_delegation.rb +1 -1
  37. data/app/controllers/concerns/pageflow/edit_locking.rb +17 -10
  38. data/app/controllers/concerns/pageflow/entry_password_protection.rb +1 -0
  39. data/app/controllers/concerns/pageflow/public_https_mode.rb +2 -1
  40. data/app/controllers/concerns/pageflow/quota_verification.rb +5 -4
  41. data/app/controllers/pageflow/admin/initial_passwords_controller.rb +1 -0
  42. data/app/controllers/pageflow/application_controller.rb +19 -19
  43. data/app/controllers/pageflow/chapters_controller.rb +3 -2
  44. data/app/controllers/pageflow/edit_locks_controller.rb +3 -2
  45. data/app/controllers/pageflow/editor/encoding_confirmations_controller.rb +1 -0
  46. data/app/controllers/pageflow/editor/entries_controller.rb +2 -1
  47. data/app/controllers/pageflow/editor/entry_publications_controller.rb +1 -0
  48. data/app/controllers/pageflow/editor/file_import_controller.rb +3 -3
  49. data/app/controllers/pageflow/editor/files_controller.rb +7 -6
  50. data/app/controllers/pageflow/editor/widgets_controller.rb +1 -0
  51. data/app/controllers/pageflow/entries_controller.rb +77 -17
  52. data/app/controllers/pageflow/feeds_controller.rb +1 -1
  53. data/app/controllers/pageflow/files_controller.rb +1 -0
  54. data/app/controllers/pageflow/pages_controller.rb +4 -2
  55. data/app/controllers/pageflow/revisions_controller.rb +2 -1
  56. data/app/controllers/pageflow/sitemaps_controller.rb +1 -1
  57. data/app/controllers/pageflow/storylines_controller.rb +1 -0
  58. data/app/helpers/pageflow/admin/cutoff_modes_helper.rb +12 -0
  59. data/app/helpers/pageflow/admin/entries_helper.rb +1 -1
  60. data/app/helpers/pageflow/admin/entry_translations_helper.rb +18 -0
  61. data/app/helpers/pageflow/admin/features_helper.rb +1 -1
  62. data/app/helpers/pageflow/admin/form_helper.rb +3 -3
  63. data/app/helpers/pageflow/admin/locales_helper.rb +10 -2
  64. data/app/helpers/pageflow/admin/memberships_helper.rb +32 -32
  65. data/app/helpers/pageflow/admin/revisions_helper.rb +1 -1
  66. data/app/helpers/pageflow/admin/sites_helper.rb +22 -0
  67. data/app/helpers/pageflow/admin/users_helper.rb +7 -5
  68. data/app/helpers/pageflow/admin/widgets_helper.rb +3 -3
  69. data/app/helpers/pageflow/asset_urls_helper.rb +1 -1
  70. data/app/helpers/pageflow/audio_files_helper.rb +5 -2
  71. data/app/helpers/pageflow/background_image_helper.rb +7 -6
  72. data/app/helpers/pageflow/common_entry_seed_helper.rb +2 -4
  73. data/app/helpers/pageflow/editor/config_helper.rb +1 -1
  74. data/app/helpers/pageflow/editor/files_helper.rb +1 -1
  75. data/app/helpers/pageflow/embed_code_helper.rb +3 -3
  76. data/app/helpers/pageflow/entries_helper.rb +58 -34
  77. data/app/helpers/pageflow/entry_json_seed_helper.rb +3 -2
  78. data/app/helpers/pageflow/feeds_helper.rb +2 -2
  79. data/app/helpers/pageflow/file_background_images_helper.rb +2 -2
  80. data/app/helpers/pageflow/file_thumbnails_helper.rb +4 -3
  81. data/app/helpers/pageflow/files_helper.rb +5 -4
  82. data/app/helpers/pageflow/folders_helper.rb +8 -5
  83. data/app/helpers/pageflow/help_entries_helper.rb +2 -2
  84. data/app/helpers/pageflow/hreflang_links_helper.rb +37 -0
  85. data/app/helpers/pageflow/info_box_helper.rb +7 -11
  86. data/app/helpers/pageflow/media_query_helper.rb +3 -3
  87. data/app/helpers/pageflow/navigation_bar_helper.rb +1 -1
  88. data/app/helpers/pageflow/overview_helper.rb +1 -1
  89. data/app/helpers/pageflow/page_types_helper.rb +5 -5
  90. data/app/helpers/pageflow/pages_helper.rb +29 -15
  91. data/app/helpers/pageflow/public_i18n_helper.rb +1 -1
  92. data/app/helpers/pageflow/quota_helper.rb +4 -4
  93. data/app/helpers/pageflow/render_json_helper.rb +6 -3
  94. data/app/helpers/pageflow/revision_file_helper.rb +2 -1
  95. data/app/helpers/pageflow/sites_helper.rb +3 -3
  96. data/app/helpers/pageflow/social_share_helper.rb +24 -10
  97. data/app/helpers/pageflow/social_share_links_helper.rb +12 -9
  98. data/app/helpers/pageflow/structured_data_helper.rb +1 -1
  99. data/app/helpers/pageflow/stub_page_configuration.rb +7 -3
  100. data/app/helpers/pageflow/text_direction_helper.rb +1 -1
  101. data/app/helpers/pageflow/themes_helper.rb +1 -1
  102. data/app/helpers/pageflow/video_files_helper.rb +29 -13
  103. data/app/helpers/pageflow/widgets_helper.rb +7 -6
  104. data/app/inputs/pageflow_permalink_input.rb +3 -2
  105. data/app/jobs/pageflow/application_job.rb +1 -0
  106. data/app/jobs/pageflow/entry_export_import/upload_and_publish_file_job.rb +1 -1
  107. data/app/jobs/pageflow/poll_meta_data_from_zencoder_job.rb +1 -0
  108. data/app/jobs/pageflow/poll_zencoder_job.rb +3 -2
  109. data/app/jobs/pageflow/process_image_or_text_track_job.rb +1 -0
  110. data/app/jobs/pageflow/prune_auto_snapshots_job.rb +1 -0
  111. data/app/jobs/pageflow/request_meta_data_from_zencoder_job.rb +1 -0
  112. data/app/jobs/pageflow/submit_file_to_zencoder_job.rb +1 -0
  113. data/app/mailers/pageflow/user_mailer.rb +1 -0
  114. data/app/models/concerns/pageflow/auto_generated_perma_id.rb +13 -1
  115. data/app/models/concerns/pageflow/entry_publication_states.rb +7 -3
  116. data/app/models/concerns/pageflow/feature_target.rb +8 -4
  117. data/app/models/concerns/pageflow/output_source.rb +3 -3
  118. data/app/models/concerns/pageflow/permalinkable.rb +2 -1
  119. data/app/models/concerns/pageflow/reusable_file.rb +23 -23
  120. data/app/models/concerns/pageflow/serialization_blacklist.rb +1 -1
  121. data/app/models/concerns/pageflow/suspendable.rb +5 -3
  122. data/app/models/concerns/pageflow/theme_referencer.rb +1 -1
  123. data/app/models/concerns/pageflow/translatable.rb +62 -0
  124. data/app/models/concerns/pageflow/uploadable_file.rb +6 -6
  125. data/app/models/pageflow/account.rb +6 -5
  126. data/app/models/pageflow/account_member_query.rb +1 -1
  127. data/app/models/pageflow/account_role_query.rb +1 -1
  128. data/app/models/pageflow/application_query.rb +1 -1
  129. data/app/models/pageflow/application_record.rb +1 -1
  130. data/app/models/pageflow/audio_file.rb +4 -4
  131. data/app/models/pageflow/audio_file_url_templates.rb +1 -1
  132. data/app/models/pageflow/authentication_token.rb +2 -2
  133. data/app/models/pageflow/chapter.rb +6 -1
  134. data/app/models/pageflow/chapter_scaffold.rb +2 -4
  135. data/app/models/pageflow/cname_site_request_scope.rb +1 -1
  136. data/app/models/pageflow/customized_theme.rb +6 -6
  137. data/app/models/pageflow/draft_entry.rb +37 -9
  138. data/app/models/pageflow/edit_lock.rb +19 -21
  139. data/app/models/pageflow/encoding_confirmation.rb +2 -2
  140. data/app/models/pageflow/entries_feed.rb +2 -2
  141. data/app/models/pageflow/entry.rb +44 -30
  142. data/app/models/pageflow/entry_at_revision.rb +5 -1
  143. data/app/models/pageflow/entry_duplicate.rb +8 -5
  144. data/app/models/pageflow/entry_publication.rb +2 -2
  145. data/app/models/pageflow/entry_role_query.rb +4 -4
  146. data/app/models/pageflow/entry_template.rb +4 -4
  147. data/app/models/pageflow/entry_title_or_account_name_query.rb +2 -2
  148. data/app/models/pageflow/entry_translation_group.rb +42 -0
  149. data/app/models/pageflow/file_reuse.rb +2 -2
  150. data/app/models/pageflow/file_usage.rb +10 -3
  151. data/app/models/pageflow/folder.rb +2 -2
  152. data/app/models/pageflow/home_button.rb +7 -7
  153. data/app/models/pageflow/image_file.rb +20 -5
  154. data/app/models/pageflow/image_file_url_templates.rb +1 -1
  155. data/app/models/pageflow/invitation_form.rb +2 -1
  156. data/app/models/pageflow/managed_user_query.rb +1 -1
  157. data/app/models/pageflow/membership.rb +5 -5
  158. data/app/models/pageflow/nested_revision_component_copy.rb +263 -0
  159. data/app/models/pageflow/null_user.rb +1 -1
  160. data/app/models/pageflow/overview_button.rb +1 -1
  161. data/app/models/pageflow/page.rb +7 -3
  162. data/app/models/pageflow/permalink.rb +23 -2
  163. data/app/models/pageflow/permalink_directory.rb +7 -0
  164. data/app/models/pageflow/permalink_redirect.rb +7 -0
  165. data/app/models/pageflow/positioned_file.rb +5 -5
  166. data/app/models/pageflow/potential_entry_translations.rb +55 -0
  167. data/app/models/pageflow/potential_memberships.rb +5 -4
  168. data/app/models/pageflow/published_entry.rb +62 -11
  169. data/app/models/pageflow/revision.rb +24 -15
  170. data/app/models/pageflow/roles.rb +14 -18
  171. data/app/models/pageflow/site.rb +36 -3
  172. data/app/models/pageflow/site_root_entry_form.rb +27 -0
  173. data/app/models/pageflow/sitemaps.rb +10 -1
  174. data/app/models/pageflow/storyline.rb +5 -2
  175. data/app/models/pageflow/storyline_scaffold.rb +1 -1
  176. data/app/models/pageflow/text_track_file.rb +1 -1
  177. data/app/models/pageflow/text_track_file_url_templates.rb +1 -1
  178. data/app/models/pageflow/theme_customization_file.rb +3 -2
  179. data/app/models/pageflow/thumbnail_file_resolver.rb +1 -1
  180. data/app/models/pageflow/url_template.rb +1 -1
  181. data/app/models/pageflow/used_file.rb +7 -3
  182. data/app/models/pageflow/user_name_query.rb +2 -2
  183. data/app/models/pageflow/video_file.rb +22 -15
  184. data/app/models/pageflow/video_file_url_templates.rb +6 -4
  185. data/app/models/pageflow/widget.rb +4 -3
  186. data/app/models/pageflow/with_file_usage_extension.rb +1 -1
  187. data/app/models/pageflow/zencoder_attachment.rb +8 -8
  188. data/app/policies/pageflow/account_policy.rb +3 -1
  189. data/app/policies/pageflow/admin/admin_only_tab_policy.rb +1 -0
  190. data/app/policies/pageflow/admin/entry_tab_policy.rb +1 -0
  191. data/app/policies/pageflow/application_policy.rb +2 -0
  192. data/app/policies/pageflow/entry_policy.rb +7 -1
  193. data/app/policies/pageflow/entry_template_policy.rb +2 -1
  194. data/app/policies/pageflow/file_policy.rb +4 -7
  195. data/app/policies/pageflow/folder_policy.rb +18 -8
  196. data/app/policies/pageflow/membership_policy.rb +6 -4
  197. data/app/policies/pageflow/site_policy.rb +10 -3
  198. data/app/policies/pageflow/user_policy.rb +6 -4
  199. data/app/state_machines/pageflow/image_and_text_track_processing_state_machine.rb +3 -2
  200. data/app/state_machines/pageflow/media_encoding_state_machine.rb +5 -4
  201. data/app/views/admin/accounts/_entry_template_details.html.arb +2 -2
  202. data/app/views/admin/entries/_attributes_table.html.arb +6 -5
  203. data/app/views/admin/entries/_form.html.erb +2 -1
  204. data/app/views/admin/entries/_permalink_inputs.html.erb +9 -2
  205. data/app/views/admin/entry_templates/_form.html.erb +1 -3
  206. data/app/views/admin/site_root_entry/choose.html.erb +19 -0
  207. data/app/views/admin/sites/_fields.html.erb +14 -1
  208. data/app/views/admin/translations/_form.html.erb +31 -0
  209. data/app/views/admin/users/_attributes_table.html.arb +13 -0
  210. data/app/views/components/pageflow/admin/add_membership_button.rb +13 -12
  211. data/app/views/components/pageflow/admin/custom_scopes_renderer.rb +1 -0
  212. data/app/views/components/pageflow/admin/embed_code_field.rb +1 -0
  213. data/app/views/components/pageflow/admin/embedded_index_table.rb +9 -10
  214. data/app/views/components/pageflow/admin/entries_tab.rb +1 -0
  215. data/app/views/components/pageflow/admin/entry_publication_state_indicator.rb +6 -5
  216. data/app/views/components/pageflow/admin/entry_templates_tab.rb +2 -1
  217. data/app/views/components/pageflow/admin/entry_translations_tab.rb +102 -0
  218. data/app/views/components/pageflow/admin/entry_user_badge_list.rb +1 -0
  219. data/app/views/components/pageflow/admin/extensible_attributes_table.rb +9 -8
  220. data/app/views/components/pageflow/admin/features_tab.rb +1 -0
  221. data/app/views/components/pageflow/admin/grouped_folder_list.rb +1 -0
  222. data/app/views/components/pageflow/admin/icon_link_to.rb +1 -0
  223. data/app/views/components/pageflow/admin/members_tab.rb +4 -3
  224. data/app/views/components/pageflow/admin/membership_role_with_tooltip.rb +3 -2
  225. data/app/views/components/pageflow/admin/revisions_tab.rb +14 -10
  226. data/app/views/components/pageflow/admin/sites_tab.rb +1 -0
  227. data/app/views/components/pageflow/admin/tabs_view.rb +1 -0
  228. data/app/views/components/pageflow/admin/timestamp.rb +6 -5
  229. data/app/views/components/pageflow/admin/user_account_badge_list.rb +2 -3
  230. data/app/views/components/pageflow/admin/user_accounts_tab.rb +1 -0
  231. data/app/views/components/pageflow/admin/user_entries_tab.rb +1 -0
  232. data/app/views/components/pageflow/admin/users_tab.rb +1 -0
  233. data/app/views/pageflow/editor/config/_seeds.json.jbuilder +1 -1
  234. data/app/views/pageflow/editor/entries/index.json.jbuilder +1 -1
  235. data/app/views/pageflow/editor/entries/show.json.jbuilder +1 -1
  236. data/app/views/pageflow/editor/entry_publications/check.json.jbuilder +2 -2
  237. data/app/views/pageflow/editor/files/_file.json.jbuilder +5 -13
  238. data/app/views/pageflow/editor/files/create.json.jbuilder +1 -1
  239. data/app/views/pageflow/editor/files/index.json.jbuilder +1 -1
  240. data/app/views/pageflow/editor/sites/_site.json.jbuilder +1 -0
  241. data/app/views/pageflow/editor/video_files/_video_file.json.jbuilder +1 -3
  242. data/app/views/pageflow/entries/share_menu/_bluesky_link.html.erb +8 -0
  243. data/app/views/pageflow/entries/share_menu/_threads_link.html.erb +8 -0
  244. data/app/views/pageflow/files/_file.json.jbuilder +3 -3
  245. data/app/views/pageflow/sitemaps/index.xml.builder +9 -1
  246. data/app/views/pageflow/social_share/_entry_meta_tags.html.erb +1 -1
  247. data/app/views/pageflow/social_share/_page_meta_tags.html.erb +1 -1
  248. data/config/initializers/active_admin_patches.rb +3 -6
  249. data/config/initializers/admin_resource_tabs.rb +3 -0
  250. data/config/initializers/paperclip.rb +1 -0
  251. data/config/initializers/symmetric_encryption.rb +1 -1
  252. data/config/initializers/zencoder.rb +24 -13
  253. data/config/locales/de.yml +110 -4
  254. data/config/locales/en.yml +109 -7
  255. data/config/routes.rb +15 -13
  256. data/config/spring.rb +2 -1
  257. data/db/migrate/20140418225525_setup_schema.rb +2 -2
  258. data/db/migrate/20190306161431_copy_file_attributes_of_failed_uploads.rb +2 -2
  259. data/db/migrate/20190820152900_drop_accounts_themes.rb +2 -1
  260. data/db/migrate/20191202145757_create_pageflow_scrolled_sections.rb +11 -11
  261. data/db/migrate/20191202150657_create_pageflow_scrolled_chapters.rb +1 -1
  262. data/db/migrate/20191202154723_create_pageflow_scrolled_content_elements.rb +9 -9
  263. data/db/migrate/20191219143450_add_position_to_content_elements.rb +2 -1
  264. data/db/migrate/20200117133200_change_revision_appearance_option_default_and_null.rb +2 -1
  265. data/db/migrate/20200122115400_create_pageflow_entry_templates.rb +25 -25
  266. data/db/migrate/20200206134400_convert_legacy_scrolled_content_element_types.rb +3 -3
  267. data/db/migrate/20221215120856_associate_entry_templates_with_sites.rb +2 -2
  268. data/db/migrate/20230120092923_create_other_files.rb +2 -1
  269. data/db/migrate/20230419083307_create_pageflow_entry_translation_group.rb +9 -0
  270. data/db/migrate/20240612110434_add_cutoff_mode_name_to_sites.rb +5 -0
  271. data/db/migrate/20240918084059_create_pageflow_permalink_redirects.rb +14 -0
  272. data/db/migrate/20250508172234_ensure_scrolled_entries_have_main_storyline.rb +14 -0
  273. data/db/migrate/20250617090048_add_custom404_entry_to_sites.rb +5 -0
  274. data/db/migrate/20250617100000_add_perma_id_counter_to_entries.rb +28 -0
  275. data/db/migrate/20250722174123_add_perma_id_to_chapters.rb +10 -0
  276. data/db/migrate/20250725080603_add_perma_id_indexes_to_revision_components.rb +13 -0
  277. data/db/migrate/20250726000000_add_display_name_to_file_usages.rb +5 -0
  278. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +3912 -1486
  279. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +468 -1161
  280. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +28125 -22
  281. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +26589 -19
  282. data/entry_types/paged/app/controllers/pageflow_paged/entries_controller.rb +1 -0
  283. data/entry_types/paged/app/helpers/pageflow_paged/page_background_asset_helper.rb +1 -1
  284. data/entry_types/paged/app/helpers/pageflow_paged/third_party_embed_consent_helper.rb +7 -7
  285. data/entry_types/paged/app/views/pageflow_paged/editor/entries/_seed.json.jbuilder +1 -0
  286. data/entry_types/paged/app/views/pageflow_paged/entries/show.html.erb +1 -0
  287. data/entry_types/paged/lib/pageflow_paged/plugin.rb +4 -0
  288. data/entry_types/paged/lib/pageflow_paged/react/page_type.rb +1 -1
  289. data/entry_types/paged/lib/pageflow_paged/react/widget_type.rb +3 -3
  290. data/entry_types/paged/lib/pageflow_paged.rb +1 -1
  291. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/chapters_controller.rb +5 -3
  292. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/content_elements_controller.rb +2 -2
  293. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/sections_controller.rb +2 -2
  294. data/entry_types/scrolled/app/controllers/pageflow_scrolled/entries_controller.rb +2 -1
  295. data/entry_types/scrolled/app/helpers/pageflow_scrolled/cache_helper.rb +2 -2
  296. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/entry_json_seed_helper.rb +1 -0
  297. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +2 -1
  298. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +60 -6
  299. data/entry_types/scrolled/app/helpers/pageflow_scrolled/packs_helper.rb +16 -2
  300. data/entry_types/scrolled/app/helpers/pageflow_scrolled/react_server_side_rendering_helper.rb +21 -23
  301. data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +16 -1
  302. data/entry_types/scrolled/app/models/pageflow_scrolled/chapter.rb +15 -14
  303. data/entry_types/scrolled/app/models/pageflow_scrolled/content_element.rb +12 -0
  304. data/entry_types/scrolled/app/models/pageflow_scrolled/section.rb +12 -1
  305. data/entry_types/scrolled/app/models/pageflow_scrolled/storyline.rb +19 -1
  306. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/entries/_head.html.erb +1 -0
  307. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/sections/_section_with_content_elements.json.jbuilder +2 -2
  308. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +2 -0
  309. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +17 -9
  310. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry_translations.json.jbuilder +14 -0
  311. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_theme.json.jbuilder +25 -5
  312. data/entry_types/scrolled/app/views/pageflow_scrolled/storylines/_storyline.json.jbuilder +7 -0
  313. data/entry_types/scrolled/config/locales/de.yml +614 -45
  314. data/entry_types/scrolled/config/locales/en.yml +586 -47
  315. data/entry_types/scrolled/config/routes.rb +18 -8
  316. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/install_generator.rb +11 -11
  317. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/logoDarkVariantDesktop.svg +56 -0
  318. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/logoDarkVariantMobile.svg +22 -0
  319. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/themes_plugin.rb.tt +26 -11
  320. data/entry_types/scrolled/lib/pageflow_scrolled/additional_packs.rb +27 -8
  321. data/entry_types/scrolled/lib/pageflow_scrolled/additional_seed_data.rb +1 -1
  322. data/entry_types/scrolled/lib/pageflow_scrolled/additional_theme_assets.rb +27 -0
  323. data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +16 -4
  324. data/entry_types/scrolled/lib/pageflow_scrolled/content_element_consent_vendors.rb +1 -1
  325. data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +24 -4
  326. data/entry_types/scrolled/lib/pageflow_scrolled/seeds.rb +24 -22
  327. data/entry_types/scrolled/lib/pageflow_scrolled.rb +1 -1
  328. data/entry_types/scrolled/lib/tasks/pageflow_scrolled/storybook.rake +6 -5
  329. data/entry_types/scrolled/package/config/webpack.js +22 -0
  330. data/entry_types/scrolled/package/contentElements/hotspots-frontend.css +1 -0
  331. data/entry_types/scrolled/package/contentElements/hotspots-frontend.js +1529 -0
  332. data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.css +1 -0
  333. data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.js +59 -0
  334. data/entry_types/scrolled/package/contentElements-editor.js +2075 -321
  335. data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
  336. data/entry_types/scrolled/package/contentElements-frontend.js +1019 -643
  337. data/entry_types/scrolled/package/editor.js +2086 -1091
  338. data/entry_types/scrolled/package/frontend/{EditableInlineText.module-ebd22921.js → FloatingPortalRootProvider-51914be7.js} +1200 -1258
  339. data/entry_types/scrolled/package/frontend/{PhonePlatformContext-4ec6b2de.js → PhonePlatformContext-9f76033e.js} +7 -8
  340. data/entry_types/scrolled/package/frontend/ThemeIcon-81f2f066.js +1932 -0
  341. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-788e9cdb.js +39 -0
  342. data/entry_types/scrolled/package/frontend/{Viewer-1bb5597c.js → Viewer-0345ce57.js} +36 -63
  343. data/entry_types/scrolled/package/frontend/{Viewer-1ecf3375.js → Viewer-cdc549cc.js} +15 -22
  344. data/entry_types/scrolled/package/frontend/{Wavesurfer-7d9cf1b7.js → Wavesurfer-1cdc3925.js} +54 -74
  345. data/entry_types/scrolled/package/frontend/{components-024a9893.js → components-96660ffd.js} +1791 -841
  346. data/entry_types/scrolled/package/frontend/{index-11f32f10.js → index-eb670c2f.js} +11 -18
  347. data/entry_types/scrolled/package/frontend/index.css +1 -1
  348. data/entry_types/scrolled/package/frontend/index.js +517 -903
  349. data/entry_types/scrolled/package/frontend/{createSuper-d0f30da3.js → inherits-539844a6.js} +40 -56
  350. data/entry_types/scrolled/package/frontend/{useContentElementEditorState-4f4c3cf6.js → useContentElementEditorState-77fe6c79.js} +7 -8
  351. data/entry_types/scrolled/package/frontend/usePhonePlatform-c2ba875d.js +8 -0
  352. data/entry_types/scrolled/package/frontend-server.js +0 -1
  353. data/entry_types/scrolled/package/package.json +18 -10
  354. data/entry_types/scrolled/package/testHelpers.js +306 -70
  355. data/entry_types/scrolled/package/values/widgets.module.css +18 -0
  356. data/entry_types/scrolled/package/widgets/consentBar.css +1 -1
  357. data/entry_types/scrolled/package/widgets/consentBar.js +47 -66
  358. data/entry_types/scrolled/package/widgets/defaultNavigation.css +2 -2
  359. data/entry_types/scrolled/package/widgets/defaultNavigation.js +177 -207
  360. data/entry_types/scrolled/package/widgets/excursionDialog.css +1 -0
  361. data/entry_types/scrolled/package/widgets/excursionDialog.js +109 -0
  362. data/entry_types/scrolled/package/widgets/excursionSheet.css +1 -0
  363. data/entry_types/scrolled/package/widgets/excursionSheet.js +262 -0
  364. data/entry_types/scrolled/package/widgets/iconInlineFileRights.css +1 -1
  365. data/entry_types/scrolled/package/widgets/iconInlineFileRights.js +6 -9
  366. data/entry_types/scrolled/package/widgets/iconScrollIndicator.css +1 -0
  367. data/entry_types/scrolled/package/widgets/iconScrollIndicator.js +48 -0
  368. data/entry_types/scrolled/package/widgets/mainStorylineSheet.css +1 -0
  369. data/entry_types/scrolled/package/widgets/mainStorylineSheet.js +144 -0
  370. data/entry_types/scrolled/package/widgets/textInlineFileRights.css +1 -1
  371. data/entry_types/scrolled/package/widgets/textInlineFileRights.js +26 -9
  372. data/entry_types/scrolled/package/widgets-server.js +1 -0
  373. data/entry_types/scrolled/spec/factories/chapters.rb +14 -1
  374. data/entry_types/scrolled/spec/factories/content_elements.rb +1 -1
  375. data/lib/generators/pageflow/active_admin_initializer/active_admin_initializer_generator.rb +5 -3
  376. data/lib/generators/pageflow/assets/assets_generator.rb +7 -5
  377. data/lib/generators/pageflow/cancan/cancan_generator.rb +2 -1
  378. data/lib/generators/pageflow/cancan/templates/ability.rb +1 -1
  379. data/lib/generators/pageflow/error_pages/error_pages_generator.rb +2 -1
  380. data/lib/generators/pageflow/initializer/initializer_generator.rb +3 -2
  381. data/lib/generators/pageflow/install/install_generator.rb +2 -2
  382. data/lib/generators/pageflow/procfile/procfile_generator.rb +2 -1
  383. data/lib/generators/pageflow/resque/resque_generator.rb +6 -4
  384. data/lib/generators/pageflow/resque/templates/resque.rake +1 -1
  385. data/lib/generators/pageflow/routes/routes_generator.rb +4 -2
  386. data/lib/generators/pageflow/seeds/seeds_generator.rb +3 -2
  387. data/lib/generators/pageflow/seeds/templates/seeds.rb +6 -6
  388. data/lib/generators/pageflow/theme/theme_generator.rb +2 -1
  389. data/lib/generators/pageflow/user/user_generator.rb +2 -1
  390. data/lib/pageflow/ability_mixin.rb +25 -15
  391. data/lib/pageflow/active_admin_patches/views/attributes_table.rb +1 -0
  392. data/lib/pageflow/active_admin_patches/views/pages/base.rb +3 -2
  393. data/lib/pageflow/active_admin_patches/views/table_for.rb +1 -0
  394. data/lib/pageflow/additional_headers.rb +27 -0
  395. data/lib/pageflow/admin/attributes_table_rows.rb +3 -3
  396. data/lib/pageflow/admin/tabs.rb +1 -1
  397. data/lib/pageflow/built_in_file_type.rb +1 -1
  398. data/lib/pageflow/built_in_page_types_plugin.rb +1 -1
  399. data/lib/pageflow/built_in_widget_type.rb +2 -2
  400. data/lib/pageflow/built_in_widget_types_plugin.rb +2 -1
  401. data/lib/pageflow/configuration.rb +141 -18
  402. data/lib/pageflow/cutoff_modes.rb +39 -0
  403. data/lib/pageflow/editor_controller.rb +6 -10
  404. data/lib/pageflow/engine.rb +4 -5
  405. data/lib/pageflow/entry_export_import/attachment_files.rb +1 -1
  406. data/lib/pageflow/entry_export_import/entry_serialization.rb +5 -4
  407. data/lib/pageflow/entry_export_import/file_mappings.rb +1 -0
  408. data/lib/pageflow/entry_export_import/revision_serialization/import.rb +1 -1
  409. data/lib/pageflow/entry_export_import.rb +3 -3
  410. data/lib/pageflow/entry_types.rb +2 -2
  411. data/lib/pageflow/feature.rb +1 -1
  412. data/lib/pageflow/features.rb +4 -6
  413. data/lib/pageflow/file_importers.rb +4 -4
  414. data/lib/pageflow/file_type.rb +3 -3
  415. data/lib/pageflow/file_types.rb +3 -3
  416. data/lib/pageflow/global_config_api.rb +5 -6
  417. data/lib/pageflow/help_entries.rb +7 -7
  418. data/lib/pageflow/help_entry.rb +1 -1
  419. data/lib/pageflow/hooks.rb +1 -1
  420. data/lib/pageflow/nested_revision_component.rb +9 -27
  421. data/lib/pageflow/news_item_api.rb +1 -1
  422. data/lib/pageflow/page_type.rb +2 -3
  423. data/lib/pageflow/page_types.rb +3 -3
  424. data/lib/pageflow/paperclip_interpolations/support.rb +1 -1
  425. data/lib/pageflow/paperclip_processors/vtt.rb +1 -0
  426. data/lib/pageflow/paperclip_processors/webp.rb +2 -2
  427. data/lib/pageflow/partial_editor_fragment_renderer.rb +2 -2
  428. data/lib/pageflow/plugin.rb +1 -2
  429. data/lib/pageflow/quota.rb +9 -5
  430. data/lib/pageflow/quotas.rb +1 -1
  431. data/lib/pageflow/rails_version.rb +1 -0
  432. data/lib/pageflow/react.rb +1 -1
  433. data/lib/pageflow/revision_component.rb +8 -40
  434. data/lib/pageflow/revision_components.rb +16 -6
  435. data/lib/pageflow/seeds.rb +12 -16
  436. data/lib/pageflow/theme.rb +1 -1
  437. data/lib/pageflow/theme_customizations.rb +10 -10
  438. data/lib/pageflow/themes.rb +3 -3
  439. data/lib/pageflow/user_mixin.rb +6 -6
  440. data/lib/pageflow/version.rb +1 -1
  441. data/lib/pageflow/widget_type.rb +4 -4
  442. data/lib/pageflow/widget_types.rb +9 -9
  443. data/lib/pageflow/zencoder_api.rb +32 -42
  444. data/lib/pageflow/zencoder_audio_output_definition.rb +14 -13
  445. data/lib/pageflow/zencoder_meta_data_output_definition.rb +3 -2
  446. data/lib/pageflow/zencoder_output_definition.rb +16 -14
  447. data/lib/pageflow/zencoder_video_output_definition.rb +140 -88
  448. data/lib/pageflow.rb +7 -2
  449. data/lib/tasks/pageflow_tasks.rake +1 -1
  450. data/package/editor.js +954 -868
  451. data/package/frontend.js +113 -387
  452. data/package/package.json +2 -1
  453. data/package/testHelpers.js +10 -56
  454. data/package/ui.js +209 -251
  455. data/spec/factories/accounts.rb +37 -22
  456. data/spec/factories/audio_files.rb +2 -2
  457. data/spec/factories/authentication_tokens.rb +4 -4
  458. data/spec/factories/chapters.rb +4 -4
  459. data/spec/factories/draft_entries.rb +14 -6
  460. data/spec/factories/edit_locks.rb +1 -1
  461. data/spec/factories/entries.rb +28 -19
  462. data/spec/factories/entry_translation_groups.rb +6 -0
  463. data/spec/factories/file_usages.rb +2 -1
  464. data/spec/factories/folders.rb +1 -1
  465. data/spec/factories/image_files.rb +2 -2
  466. data/spec/factories/pages.rb +3 -3
  467. data/spec/factories/published_entries.rb +26 -8
  468. data/spec/factories/revisions.rb +1 -1
  469. data/spec/factories/sites.rb +6 -0
  470. data/spec/factories/test_multi_attachment_files.rb +1 -1
  471. data/spec/factories/text_track_files.rb +1 -1
  472. data/spec/factories/uploadable_files.rb +1 -1
  473. data/spec/factories/users.rb +6 -6
  474. data/spec/factories/video_files.rb +2 -2
  475. data/spec/factories/widgets.rb +1 -1
  476. data/spec/fixtures/7x15_rotated.jpg +0 -0
  477. data/vendor/assets/javascripts/wysihtml-toolbar.js +19288 -0
  478. metadata +98 -45
  479. data/config/initializers/revision_components.rb +0 -5
  480. data/config/locales/twitter_to_x.de.yml +0 -6
  481. data/config/locales/twitter_to_x.en.yml +0 -6
  482. data/entry_types/scrolled/config/locales/twitter_to_x.de.yml +0 -12
  483. data/entry_types/scrolled/config/locales/twitter_to_x.en.yml +0 -12
  484. data/entry_types/scrolled/config/locales/vr_image_projection.de.yml +0 -24
  485. data/entry_types/scrolled/config/locales/vr_image_projection.en.yml +0 -24
  486. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-b79159cc.js +0 -107
  487. data/entry_types/scrolled/package/frontend/arrowRight-62998af9.js +0 -77
  488. data/entry_types/scrolled/package/frontend/i18n-ce13a8bf.js +0 -1129
data/package/editor.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import Backbone from 'backbone';
2
2
  import _ from 'underscore';
3
- import { Object as Object$1, TabsView, ConfigurationEditorView, CollectionView, tooltipContainer, SelectInputView, inputView, TextInputView, TextAreaInputView, CheckBoxGroupInputView, i18nUtils, ConfigurationEditorTabView, SeparatorView, UrlDisplayView, TableView, TextTableCellView, PresenceTableCellView, SortableCollectionView, attributeBinding, DeleteRowTableCellView, CheckBoxInputView, SliderInputView, IconTableCellView } from 'pageflow/ui';
3
+ import { Object as Object$1, ConfigurationEditorView, CollectionView, tooltipContainer, SelectInputView, TabsView, inputView, TextInputView, TextAreaInputView, CheckBoxGroupInputView, i18nUtils, ConfigurationEditorTabView, FileNameInputView, SeparatorView, UrlDisplayView, TableView, TextTableCellView, PresenceTableCellView, SortableCollectionView, attributeBinding, DeleteRowTableCellView, CheckBoxInputView, SliderInputView, IconTableCellView } from 'pageflow/ui';
4
4
  export * from 'pageflow/ui';
5
5
  import Cocktail from 'cocktail';
6
6
  import I18n$1 from 'i18n-js';
@@ -11,13 +11,11 @@ import 'jquery-ui';
11
11
 
12
12
  (function () {
13
13
  var sync = Backbone.sync;
14
-
15
14
  Backbone.sync = function (method, model, options) {
16
15
  if (model.paramRoot && !options.attrs) {
17
16
  options.attrs = options.queryParams || {};
18
17
  options.attrs[model.paramRoot] = model.toJSON(options);
19
18
  }
20
-
21
19
  return sync(method, model, options);
22
20
  };
23
21
  })();
@@ -84,7 +82,6 @@ var CommonPageConfigurationTabs = Object$1.extend({
84
82
  configureFn.call(prefixInputDecorator(name, this));
85
83
  });
86
84
  });
87
-
88
85
  function prefixInputDecorator(name, dsl) {
89
86
  return {
90
87
  input: function input(propertyName, view, options) {
@@ -95,6 +92,20 @@ var CommonPageConfigurationTabs = Object$1.extend({
95
92
  }
96
93
  });
97
94
 
95
+ var DropDownMenuItems = Object$1.extend({
96
+ initialize: function initialize() {
97
+ this.menuItems = {};
98
+ },
99
+ register: function register(menuItem, _ref) {
100
+ var menuName = _ref.menuName;
101
+ this.menuItems[menuName] = this.menuItems[menuName] || [];
102
+ this.menuItems[menuName].push(menuItem);
103
+ },
104
+ findAllByMenuName: function findAllByMenuName(menuName) {
105
+ return this.menuItems[menuName] || [];
106
+ }
107
+ });
108
+
98
109
  /**
99
110
  * Failure and subclasses are used in the failures api.
100
111
  *
@@ -104,7 +115,6 @@ var CommonPageConfigurationTabs = Object$1.extend({
104
115
  *
105
116
  * @class
106
117
  */
107
-
108
118
  var Failure = Object$1.extend({
109
119
  canRetry: true,
110
120
  type: 'Failure',
@@ -151,15 +161,11 @@ var OrderingFailure = Failure.extend({
151
161
  *
152
162
  * @alias Failures
153
163
  */
154
-
155
- var FailuresAPI = Object$1.extend(
156
- /** @lends Failures.prototype */
157
- {
164
+ var FailuresAPI = Object$1.extend( /** @lends Failures.prototype */{
158
165
  initialize: function initialize() {
159
166
  this.failures = {};
160
167
  this.length = 0;
161
168
  },
162
-
163
169
  /**
164
170
  * Listen to the `error` and `sync` events of a collection and
165
171
  * create failure objects.
@@ -181,7 +187,6 @@ var FailuresAPI = Object$1.extend(
181
187
  isEmpty: function isEmpty() {
182
188
  return _.size(this.failures) === 0;
183
189
  },
184
-
185
190
  /**
186
191
  * Record that a failure occured.
187
192
  *
@@ -200,14 +205,12 @@ var FailuresAPI = Object$1.extend(
200
205
  return this.length;
201
206
  }
202
207
  });
203
-
204
208
  _.extend(FailuresAPI.prototype, Backbone.Events);
205
209
 
206
210
  var UploadError = Object$1.extend({
207
211
  setMessage: function setMessage(options) {
208
212
  this.upload = options.upload;
209
213
  var typeTranslation;
210
-
211
214
  if (options.typeTranslation) {
212
215
  typeTranslation = options.typeTranslation;
213
216
  } else if (this.upload.type !== '') {
@@ -215,7 +218,6 @@ var UploadError = Object$1.extend({
215
218
  } else {
216
219
  typeTranslation = I18n$1.t('pageflow.editor.errors.upload.type_empty');
217
220
  }
218
-
219
221
  var interpolations = {
220
222
  name: this.upload.name,
221
223
  type: typeTranslation,
@@ -287,26 +289,21 @@ var FileTypesCollection = Object$1.extend({
287
289
  var result = this.find(function (fileType) {
288
290
  return fileType.matchUpload(upload);
289
291
  });
290
-
291
292
  if (!result) {
292
293
  throw new UnmatchedUploadError(upload);
293
294
  }
294
-
295
295
  return result;
296
296
  },
297
297
  findByCollectionName: function findByCollectionName(collectionName) {
298
298
  var result = this.find(function (fileType) {
299
299
  return fileType.collectionName === collectionName;
300
300
  });
301
-
302
301
  if (!result) {
303
302
  throw 'Could not find file type by collection name "' + collectionName + '"';
304
303
  }
305
-
306
304
  return result;
307
305
  }
308
306
  });
309
-
310
307
  _.each(['each', 'map', 'reduce', 'first', 'find', 'contains', 'filter'], function (method) {
311
308
  FileTypesCollection.prototype[method] = function () {
312
309
  var args = Array.prototype.slice.call(arguments);
@@ -315,142 +312,6 @@ _.each(['each', 'map', 'reduce', 'first', 'find', 'contains', 'filter'], functio
315
312
  };
316
313
  });
317
314
 
318
- var app = new Marionette.Application();
319
-
320
- var dialogView = {
321
- events: {
322
- 'click .close': function clickClose() {
323
- this.close();
324
- },
325
- 'click .box': function clickBox() {
326
- return false;
327
- },
328
- 'click': function click() {
329
- this.close();
330
- }
331
- }
332
- };
333
-
334
- function template(data) {
335
- var __t, __p = '';
336
- __p += '<div class="box">\n <div class="content">\n <h1 class="dialog-header"></h1>\n </div>\n\n <div class="footer">\n <a href="" class="close">\n ' +
337
- ((__t = ( I18n.t('pageflow.editor.templates.file_settings_dialog.close') )) == null ? '' : __t) +
338
- '\n </a>\n </div>\n</div>\n';
339
- return __p
340
- }
341
-
342
- var FileSettingsDialogView = Marionette.ItemView.extend({
343
- template: template,
344
- className: 'file_settings_dialog editor dialog',
345
- mixins: [dialogView],
346
- ui: {
347
- content: '.content',
348
- header: '.dialog-header'
349
- },
350
- onRender: function onRender() {
351
- this.ui.header.text(this.model.title());
352
- this.tabsView = new TabsView({
353
- model: this.model,
354
- i18n: 'pageflow.editor.files.settings_dialog_tabs',
355
- defaultTab: this.options.tabName
356
- });
357
-
358
- _.each(this.model.fileType().settingsDialogTabs, function (options) {
359
- this.tabsView.tab(options.name, _.bind(function () {
360
- return this.subview(new options.view(_.extend({
361
- model: this.model
362
- }, options.viewOptions)));
363
- }, this));
364
- }, this);
365
-
366
- this.ui.content.append(this.subview(this.tabsView).el);
367
- }
368
- });
369
-
370
- FileSettingsDialogView.open = function (options) {
371
- app.dialogRegion.show(new FileSettingsDialogView(options));
372
- };
373
-
374
- function template$1(data) {
375
- var __t, __p = '';
376
- __p += '<span class="value"></span>\n<a class="edit" href="" title="' +
377
- ((__t = ( I18n.t('pageflow.editor.templates.file_meta_data_item_value_view.edit') )) == null ? '' : __t) +
378
- '">\n</a>\n\n';
379
- return __p
380
- }
381
-
382
- /**
383
- * Base class for views used as `valueView` for file type meta data
384
- * attributes.
385
- *
386
- * @param {Object} [options]
387
- *
388
- * @param {string} [options.name]
389
- * Name of the meta data item used in translation keys.
390
- *
391
- * @param {string} [options.settingsDialogTabLink]
392
- * Dispaly a link to open the specified tab of the file settings
393
- * dialog.
394
- *
395
- * @since 12.0
396
- *
397
- * @class
398
- */
399
-
400
- var FileMetaDataItemValueView = Marionette.ItemView.extend({
401
- template: template$1,
402
- className: 'value_wrapper',
403
- ui: {
404
- value: '.value',
405
- editLink: '.edit'
406
- },
407
- events: {
408
- 'click .edit': function clickEdit() {
409
- FileSettingsDialogView.open({
410
- model: this.model,
411
- tabName: this.options.settingsDialogTabLink
412
- });
413
- }
414
- },
415
- modelEvents: {
416
- 'change': 'toggleEditLink'
417
- },
418
- onRender: function onRender() {
419
- this.listenTo(this.model, 'change:' + this.options.name, this.update);
420
- this.toggleEditLink();
421
- this.update();
422
- },
423
- update: function update() {
424
- this.ui.value.text(this.getText() || I18n$1.t('pageflow.editor.views.file_meta_data_item_value_view.blank'));
425
- },
426
- getText: function getText() {
427
- throw new Error('Not implemented');
428
- },
429
- toggleEditLink: function toggleEditLink() {
430
- this.ui.editLink.toggle(!!this.options.settingsDialogTabLink && !this.model.isNew());
431
- }
432
- });
433
-
434
- var TextFileMetaDataItemValueView = FileMetaDataItemValueView.extend({
435
- getText: function getText() {
436
- var model;
437
-
438
- if (this.options.fromConfiguration) {
439
- model = this.model.configuration;
440
- } else {
441
- model = this.model;
442
- }
443
-
444
- var value = model.get(this.options.name);
445
-
446
- if (value && this.options.formatValue) {
447
- return this.options.formatValue(value);
448
- }
449
-
450
- return value;
451
- }
452
- });
453
-
454
315
  var FileType = Object$1.extend({
455
316
  initialize: function initialize(options) {
456
317
  this.model = options.model;
@@ -470,7 +331,6 @@ var FileType = Object$1.extend({
470
331
  this.filters = options.filters || [];
471
332
  this.noExtendedFileRights = options.noExtendedFileRights;
472
333
  this.metaDataAttributes = options.metaDataAttributes || [];
473
-
474
334
  if (typeof options.matchUpload === 'function') {
475
335
  this.matchUpload = options.matchUpload;
476
336
  } else if (options.matchUpload instanceof RegExp) {
@@ -480,7 +340,6 @@ var FileType = Object$1.extend({
480
340
  } else {
481
341
  throw 'matchUpload option of FileType "' + this.collectionName + '" must either be a function or a RegExp.';
482
342
  }
483
-
484
343
  this.setupModelNaming();
485
344
  },
486
345
  setupModelNaming: function setupModelNaming() {
@@ -495,11 +354,9 @@ var FileType = Object$1.extend({
495
354
  var result = _(this.filters).find(function (filter) {
496
355
  return filter.name === name;
497
356
  });
498
-
499
357
  if (!result) {
500
358
  throw new Error('Unknown filter "' + name + '" for file type "' + this.collectionName + '".');
501
359
  }
502
-
503
360
  return result;
504
361
  }
505
362
  });
@@ -516,44 +373,34 @@ var FileTypes = Object$1.extend({
516
373
  if (this._setup) {
517
374
  throw 'File types already set up. Register file types before initializers run.';
518
375
  }
519
-
520
376
  this.clientSideConfigs[name] = config;
521
377
  },
522
378
  modify: function modify(name, config) {
523
379
  if (this._setup) {
524
380
  throw 'File types already set up. Modify file types before initializers run.';
525
381
  }
526
-
527
382
  this.clientSideConfigModifications[name] = this.clientSideConfigModifications[name] || [];
528
383
  this.clientSideConfigModifications[name].push(config);
529
384
  },
530
385
  setup: function setup(serverSideConfigs) {
531
386
  var _this = this;
532
-
533
387
  var clientSideConfigs = this.clientSideConfigs;
534
388
  this._setup = true;
535
-
536
389
  var configs = _.chain(serverSideConfigs).map(function (serverSideConfig) {
537
390
  var clientSideConfig = clientSideConfigs[serverSideConfig.collectionName];
538
-
539
391
  if (!clientSideConfig) {
540
392
  throw 'Missing client side config for file type "' + serverSideConfig.collectionName + '"';
541
393
  }
542
-
543
394
  _this.applyCommonConfig(clientSideConfig);
544
-
545
395
  _this.applyModifications(serverSideConfig, clientSideConfig);
546
-
547
396
  return _.extend({}, serverSideConfig, clientSideConfig);
548
397
  }).sortBy(function (config) {
549
398
  return config.priority || 10;
550
399
  }).value();
551
-
552
400
  this.collection = new FileTypesCollection(_.map(configs, function (config) {
553
401
  return new FileType(config);
554
402
  }));
555
403
  var those = this;
556
-
557
404
  _.map(serverSideConfigs, function (serverSideConfig) {
558
405
  var fileType = those.findByCollectionName(serverSideConfig.collectionName);
559
406
  fileType.setNestedFileTypes(new FileTypesCollection(_.map(serverSideConfig.nestedFileTypes, function (nestedFileType) {
@@ -573,7 +420,6 @@ var FileTypes = Object$1.extend({
573
420
  },
574
421
  lintModification: function lintModification(modification, collectionName) {
575
422
  var unmodifyableProperties = _.difference(_.keys(modification), this.modifyableProperties);
576
-
577
423
  if (unmodifyableProperties.length) {
578
424
  throw 'Only the following properties are allowed in FileTypes#modify: ' + this.modifyableProperties.join(', ') + '. Given in modification for ' + collectionName + ': ' + unmodifyableProperties.join(', ') + '.';
579
425
  }
@@ -584,13 +430,11 @@ var FileTypes = Object$1.extend({
584
430
  });
585
431
  }
586
432
  });
587
-
588
433
  _.each(['each', 'map', 'reduce', 'first', 'find', 'findByUpload', 'findByCollectionName', 'contains', 'filter'], function (method) {
589
434
  FileTypes.prototype[method] = function () {
590
435
  if (!this._setup) {
591
436
  throw 'File types are not yet set up.';
592
437
  }
593
-
594
438
  return this.collection[method].apply(this.collection, arguments);
595
439
  };
596
440
  });
@@ -603,7 +447,6 @@ var FileImporters = Object$1.extend({
603
447
  if (this._setup) {
604
448
  throw 'File importers setup is already finished. Register file importers before setup is finished';
605
449
  }
606
-
607
450
  this.importers[name] = config;
608
451
  config.key = name;
609
452
  },
@@ -624,7 +467,6 @@ var FileImporters = Object$1.extend({
624
467
  if (!this.importers[name]) {
625
468
  throw 'Could not find file importer with name "' + name + '"';
626
469
  }
627
-
628
470
  return this.importers[name];
629
471
  },
630
472
  keys: function keys() {
@@ -694,7 +536,6 @@ var PageTypes = Object$1.extend({
694
536
  if (this._setup) {
695
537
  throw 'Page types already set up. Register page types before initializers run.';
696
538
  }
697
-
698
539
  this.clientSideConfigs[name] = config;
699
540
  },
700
541
  setup: function setup(serverSideConfigs) {
@@ -709,38 +550,33 @@ var PageTypes = Object$1.extend({
709
550
  var result = this.find(function (pageType) {
710
551
  return pageType.name === name;
711
552
  });
712
-
713
553
  if (!result) {
714
554
  throw 'Could not find page type with name "' + name + '"';
715
555
  }
716
-
717
556
  return result;
718
557
  },
719
558
  findByPage: function findByPage(page) {
720
559
  return this.findByName(page.get('template'));
721
560
  }
722
561
  });
723
-
724
562
  _.each(['each', 'map', 'reduce', 'first', 'find', 'pluck'], function (method) {
725
563
  PageTypes.prototype[method] = function () {
726
564
  if (!this._setup) {
727
565
  throw 'Page types are not yet set up.';
728
566
  }
729
-
730
567
  var args = Array.prototype.slice.call(arguments);
731
568
  args.unshift(this.pageTypes);
732
569
  return _[method].apply(_, args);
733
570
  };
734
571
  });
735
572
 
573
+ // A partial implementation of a collection that can store records of
736
574
  // different model types. Backbone.Collection tries to merge records
737
575
  // if they have the same id.
738
-
739
576
  var MultiCollection = function MultiCollection() {
740
577
  this.records = {};
741
578
  this.length = 0;
742
579
  };
743
-
744
580
  _.extend(MultiCollection.prototype, {
745
581
  add: function add(record) {
746
582
  if (!this.records[record.cid]) {
@@ -760,9 +596,7 @@ _.extend(MultiCollection.prototype, {
760
596
  return this.length === 0;
761
597
  }
762
598
  });
763
-
764
599
  _.extend(MultiCollection.prototype, Backbone.Events);
765
-
766
600
  MultiCollection.extend = Backbone.Collection.extend;
767
601
 
768
602
  /**
@@ -770,7 +604,6 @@ MultiCollection.extend = Backbone.Collection.extend;
770
604
  * being saved. Used to update the notifications view displaying
771
605
  * saving status/failutes.
772
606
  */
773
-
774
607
  var SavingRecordsCollection = MultiCollection.extend({
775
608
  /**
776
609
  * Listen to events of models in collection to track when they are
@@ -793,7 +626,9 @@ var WidgetType = Object$1.extend({
793
626
  initialize: function initialize(serverSideConfig, clientSideConfig) {
794
627
  this.name = serverSideConfig.name;
795
628
  this.translationKey = serverSideConfig.translationKey;
629
+ this.insertPoint = serverSideConfig.insertPoint;
796
630
  this.configurationEditorView = clientSideConfig.configurationEditorView;
631
+ this.configurationEditorTabViewGroups = clientSideConfig.configurationEditorTabViewGroups || {};
797
632
  this.isOptional = clientSideConfig.isOptional;
798
633
  },
799
634
  hasConfiguration: function hasConfiguration() {
@@ -804,6 +639,16 @@ var WidgetType = Object$1.extend({
804
639
  return new constructor(_.extend({
805
640
  attributeTranslationKeyPrefixes: ['pageflow.editor.widgets.attributes.' + this.name, 'pageflow.editor.widgets.common_attributes']
806
641
  }, options));
642
+ },
643
+ defineStubConfigurationEditorTabViewGroups: function defineStubConfigurationEditorTabViewGroups(groups) {
644
+ _.each(this.configurationEditorTabViewGroups, function (fn, name) {
645
+ return groups.define(name, function () {});
646
+ });
647
+ },
648
+ defineConfigurationEditorTabViewGroups: function defineConfigurationEditorTabViewGroups(groups) {
649
+ _.each(this.configurationEditorTabViewGroups, function (fn, name) {
650
+ return groups.define(name, fn);
651
+ });
807
652
  }
808
653
  });
809
654
 
@@ -816,15 +661,12 @@ var WidgetTypes = Object$1.extend({
816
661
  if (this._setup) {
817
662
  throw 'Widget types already set up. Register widget types before initializers run.';
818
663
  }
819
-
820
664
  this._clientSideConfigs[name] = config;
821
665
  },
822
666
  setup: function setup(serverSideConfigsByRole) {
823
667
  this._setup = true;
824
668
  this._widgetTypesByName = {};
825
-
826
669
  var roles = _.keys(serverSideConfigsByRole);
827
-
828
670
  this._widgetTypesByRole = roles.reduce(_.bind(function (result, role) {
829
671
  result[role] = serverSideConfigsByRole[role].map(_.bind(function (serverSideConfig) {
830
672
  var clientSideConfig = this._clientSideConfigs[serverSideConfig.name] || {};
@@ -842,7 +684,6 @@ var WidgetTypes = Object$1.extend({
842
684
  if (!this._widgetTypesByName[name]) {
843
685
  throw 'Could not find widget type with name "' + name + '"';
844
686
  }
845
-
846
687
  return this._widgetTypesByName[name];
847
688
  },
848
689
  registerRole: function registerRole(role, options) {
@@ -850,19 +691,23 @@ var WidgetTypes = Object$1.extend({
850
691
  },
851
692
  isOptional: function isOptional(role) {
852
693
  return !!this._optionalRoles[role];
694
+ },
695
+ defineStubConfigurationEditorTabViewGroups: function defineStubConfigurationEditorTabViewGroups(groups) {
696
+ _.each(this._widgetTypesByName, function (widgetType) {
697
+ return widgetType.defineStubConfigurationEditorTabViewGroups(groups);
698
+ });
853
699
  }
854
700
  });
855
701
 
702
+ var app = new Marionette.Application();
703
+
856
704
  var state = window.pageflow || {};
857
705
 
858
706
  /**
859
707
  * Interface for engines providing editor extensions.
860
708
  * @alias editor
861
709
  */
862
-
863
- var EditorApi = Object$1.extend(
864
- /** @lends editor */
865
- {
710
+ var EditorApi = Object$1.extend( /** @lends editor */{
866
711
  initialize: function initialize(options) {
867
712
  this.router = options && options.router;
868
713
  this.entryType = {};
@@ -870,14 +715,16 @@ var EditorApi = Object$1.extend(
870
715
  this.mainMenuItems = [];
871
716
  this.initializers = [];
872
717
  this.fileSelectionHandlers = {};
718
+ this.appearanceInputsCallbacks = [];
719
+
873
720
  /**
874
721
  * Failures API
875
722
  *
876
723
  * @returns {Failures}
877
724
  * @memberof editor
878
725
  */
879
-
880
726
  this.failures = new FailuresAPI();
727
+
881
728
  /**
882
729
  * Tracking records that are currently being saved.
883
730
  *
@@ -885,40 +732,44 @@ var EditorApi = Object$1.extend(
885
732
  * @memberof editor
886
733
  * @since 15.1
887
734
  */
888
-
889
735
  this.savingRecords = new SavingRecordsCollection();
736
+
890
737
  /**
891
738
  * Set up editor integration for page types.
892
739
  * @memberof editor
893
740
  */
894
-
895
741
  this.pageTypes = new PageTypes();
742
+
896
743
  /**
897
744
  * Add tabs to the configuration editor of all pages.
898
745
  * @memberof editor
899
746
  */
900
-
901
747
  this.commonPageConfigurationTabs = new CommonPageConfigurationTabs();
748
+
902
749
  /**
903
750
  * Setup editor integration for widget types.
904
751
  * @memberof editor
905
752
  */
906
-
907
753
  this.widgetTypes = new WidgetTypes();
754
+
908
755
  /**
909
756
  * Set up editor integration for file types
910
757
  * @memberof editor
911
758
  */
912
-
913
759
  this.fileTypes = new FileTypes();
760
+
914
761
  /**
915
762
  * List of available file import plugins
916
763
  * @memberof editor
917
764
  */
918
-
919
765
  this.fileImporters = new FileImporters();
920
- },
921
766
 
767
+ /**
768
+ * List of additional menu items for dropdown menus
769
+ * @memberof editor
770
+ */
771
+ this.dropDownMenuItems = new DropDownMenuItems();
772
+ },
922
773
  /**
923
774
  * Configure editor for entry type.
924
775
  *
@@ -946,14 +797,11 @@ var EditorApi = Object$1.extend(
946
797
  },
947
798
  createEntryModel: function createEntryModel(seed, options) {
948
799
  var entry = new this.entryType.entryModel(seed.entry, options);
949
-
950
800
  if (entry.setupFromEntryTypeSeed) {
951
801
  entry.setupFromEntryTypeSeed(seed.entry_type, state);
952
802
  }
953
-
954
803
  return entry;
955
804
  },
956
-
957
805
  /**
958
806
  * Display Backbone/Marionette View inside the main panel
959
807
  * of the editor.
@@ -961,14 +809,12 @@ var EditorApi = Object$1.extend(
961
809
  showViewInMainPanel: function showViewInMainPanel(view) {
962
810
  app.mainRegion.show(view);
963
811
  },
964
-
965
812
  /**
966
813
  * Display the Pageflow-Preview inside the main panel.
967
814
  */
968
815
  showPreview: function showPreview() {
969
816
  app.mainRegion.$el.empty();
970
817
  },
971
-
972
818
  /**
973
819
  * Register additional router and controller for sidebar.
974
820
  *
@@ -979,7 +825,6 @@ var EditorApi = Object$1.extend(
979
825
  registerSideBarRouting: function registerSideBarRouting(options) {
980
826
  this.sideBarRoutings.push(options);
981
827
  },
982
-
983
828
  /**
984
829
  * Set the file that is the parent of nested files when they are
985
830
  * uploaded. This value is automatically set and unset upon
@@ -988,7 +833,6 @@ var EditorApi = Object$1.extend(
988
833
  setUploadTargetFile: function setUploadTargetFile(file) {
989
834
  this.nextUploadTargetFile = file;
990
835
  },
991
-
992
836
  /**
993
837
  * Set the name of the help entry that shall be selected by
994
838
  * default when the help view is opened. This value is
@@ -1001,7 +845,6 @@ var EditorApi = Object$1.extend(
1001
845
  this.defaultHelpEntry = this.nextDefaultHelpEntry;
1002
846
  this.nextDefaultHelpEntry = null;
1003
847
  },
1004
-
1005
848
  /**
1006
849
  * Register additional menu item to be displayed on the root sidebar
1007
850
  * view.
@@ -1014,7 +857,16 @@ var EditorApi = Object$1.extend(
1014
857
  registerMainMenuItem: function registerMainMenuItem(options) {
1015
858
  this.mainMenuItems.push(options);
1016
859
  },
1017
-
860
+ /**
861
+ * Register additional inputs to show in the appearance tab under
862
+ * title and options. Passed callback receives tabView and options
863
+ * with entry.
864
+ *
865
+ * @since edge
866
+ */
867
+ registerAppearanceInputs: function registerAppearanceInputs(callback) {
868
+ this.appearanceInputsCallbacks.push(callback);
869
+ },
1018
870
  /**
1019
871
  * Register a custom initializer which will be run before the boot
1020
872
  * initializer of the editor.
@@ -1022,7 +874,6 @@ var EditorApi = Object$1.extend(
1022
874
  addInitializer: function addInitializer(fn) {
1023
875
  this.initializers.push(fn);
1024
876
  },
1025
-
1026
877
  /**
1027
878
  * Navigate to the given path.
1028
879
  */
@@ -1030,10 +881,8 @@ var EditorApi = Object$1.extend(
1030
881
  if (!this.router) {
1031
882
  throw 'Routing has not been initialized yet.';
1032
883
  }
1033
-
1034
884
  this.router.navigate(path, options);
1035
885
  },
1036
-
1037
886
  /**
1038
887
  * Extend the interface of page configuration objects. This is
1039
888
  * especially convenient to wrap structured data from the page
@@ -1052,7 +901,6 @@ var EditorApi = Object$1.extend(
1052
901
  registerPageConfigurationMixin: function registerPageConfigurationMixin(mixin) {
1053
902
  app.trigger('mixin:configuration', mixin);
1054
903
  },
1055
-
1056
904
  /**
1057
905
  * File selection handlers let editor extensions use the files view
1058
906
  * to select files for usage in their custom models.
@@ -1076,7 +924,6 @@ var EditorApi = Object$1.extend(
1076
924
  registerFileSelectionHandler: function registerFileSelectionHandler(name, handler) {
1077
925
  this.fileSelectionHandlers[name] = handler;
1078
926
  },
1079
-
1080
927
  /**
1081
928
  * Trigger selection of the given file type with the given
1082
929
  * handler. Payload hash is passed to selection handler as options.
@@ -1111,12 +958,10 @@ var EditorApi = Object$1.extend(
1111
958
  name: fileType
1112
959
  };
1113
960
  }
1114
-
1115
961
  this.navigate('/files/' + (fileType.defaultTab ? "".concat(fileType.defaultTab, ":default") : fileType.name) + '?handler=' + handlerName + '&payload=' + encodeURIComponent(JSON.stringify(payload)) + (fileType.filter ? '&filter=' + fileType.filter : ''), {
1116
962
  trigger: true
1117
963
  });
1118
964
  },
1119
-
1120
965
  /**
1121
966
  * Returns a promise which resolves to a page selected by the
1122
967
  * user.
@@ -1134,7 +979,6 @@ var EditorApi = Object$1.extend(
1134
979
  if (!this.fileSelectionHandlers[handlerName]) {
1135
980
  throw 'Unknown FileSelectionHandler ' + handlerName;
1136
981
  }
1137
-
1138
982
  var payloadJson = JSON.parse(decodeURIComponent(encodedPayload));
1139
983
  return new this.fileSelectionHandlers[handlerName](_objectSpread2(_objectSpread2({}, payloadJson), {}, {
1140
984
  entry: state.entry
@@ -1150,7 +994,6 @@ var EditorApi = Object$1.extend(
1150
994
  ensureBrowserSupport: function ensureBrowserSupport(start) {
1151
995
  if (this.entryType.isBrowserSupported) {
1152
996
  var isBrowserSupported = this.entryType.isBrowserSupported();
1153
-
1154
997
  if (isBrowserSupported) {
1155
998
  start();
1156
999
  } else {
@@ -1188,7 +1031,6 @@ var startEditor = function startEditor(options) {
1188
1031
  * specific editor controllers registered via the `editor_app` entry
1189
1032
  * type option.
1190
1033
  */
1191
-
1192
1034
  var entryTypeEditorControllerUrls = {
1193
1035
  /**
1194
1036
  * Mixins for Backbone collections that defines `url` method.
@@ -1221,7 +1063,6 @@ var entryTypeEditorControllerUrls = {
1221
1063
  }
1222
1064
  };
1223
1065
  },
1224
-
1225
1066
  /**
1226
1067
  * Mixins for Backbone models that defines `urlRoot` method.
1227
1068
  *
@@ -1251,7 +1092,6 @@ var entryTypeEditorControllerUrls = {
1251
1092
  };
1252
1093
  }
1253
1094
  };
1254
-
1255
1095
  function entryTypeEditorControllerUrl(resources) {
1256
1096
  return [state.entry.url(), editor.entryType.name, resources].join('/');
1257
1097
  }
@@ -1275,16 +1115,13 @@ var formDataUtils = {
1275
1115
  var stylesheet = {
1276
1116
  reload: function reload(name) {
1277
1117
  var link = this.selectLink(name);
1278
-
1279
1118
  if (!link.data('originalHref')) {
1280
1119
  link.data('originalHref', link.attr('href'));
1281
1120
  }
1282
-
1283
1121
  link.attr('href', link.data('originalHref') + '&reload=' + new Date().getTime());
1284
1122
  },
1285
1123
  update: function update(name, stylesheetPath) {
1286
1124
  var link = this.selectLink(name);
1287
-
1288
1125
  if (link.attr('href') !== stylesheetPath) {
1289
1126
  link.attr('href', stylesheetPath);
1290
1127
  }
@@ -1300,11 +1137,9 @@ var SubsetCollection = Backbone.Collection.extend({
1300
1137
  var sorting = false;
1301
1138
  var parentSorting = false;
1302
1139
  options = options || {};
1303
-
1304
- this.filter = options.filter || function (item) {
1140
+ this.predicate = options.filter || function (item) {
1305
1141
  return true;
1306
1142
  };
1307
-
1308
1143
  this.parent = options.parent;
1309
1144
  this.parentModel = options.parentModel;
1310
1145
  delete options.filter;
@@ -1312,7 +1147,7 @@ var SubsetCollection = Backbone.Collection.extend({
1312
1147
  this.model = this.parent.model;
1313
1148
  this.comparator = options.comparator || this.parent.comparator;
1314
1149
  this.listenTo(this.parent, 'add', function (model, collection, options) {
1315
- if (!adding && this.filter(model)) {
1150
+ if (!adding && this.predicate(model)) {
1316
1151
  this.add(model, options);
1317
1152
  }
1318
1153
  });
@@ -1324,39 +1159,32 @@ var SubsetCollection = Backbone.Collection.extend({
1324
1159
  this.parent.add(model);
1325
1160
  adding = false;
1326
1161
  });
1327
-
1328
1162
  if (options.watchAttribute) {
1329
1163
  this.listenTo(this.parent, 'change:' + options.watchAttribute, function (model) {
1330
- if (this.filter(model)) {
1164
+ if (this.predicate(model)) {
1331
1165
  this.add(model);
1332
1166
  } else {
1333
1167
  this.remove(model);
1334
1168
  }
1335
1169
  });
1336
1170
  }
1337
-
1338
1171
  if (options.sortOnParentSort) {
1339
1172
  this.listenTo(this.parent, 'sort', function () {
1340
1173
  parentSorting = true;
1341
-
1342
1174
  if (!sorting) {
1343
1175
  this.sort();
1344
1176
  }
1345
-
1346
1177
  parentSorting = false;
1347
1178
  });
1348
1179
  }
1349
-
1350
1180
  this.listenTo(this, 'sort', function () {
1351
1181
  sorting = true;
1352
-
1353
1182
  if (!parentSorting) {
1354
1183
  this.parent.sort();
1355
1184
  }
1356
-
1357
1185
  sorting = false;
1358
1186
  });
1359
- Backbone.Collection.prototype.constructor.call(this, this.parent.filter(this.filter, this), options);
1187
+ Backbone.Collection.prototype.constructor.call(this, this.parent.filter(this.predicate, this), options);
1360
1188
  },
1361
1189
  clear: function clear() {
1362
1190
  this.parent.remove(this.models);
@@ -1368,9 +1196,33 @@ var SubsetCollection = Backbone.Collection.extend({
1368
1196
  dispose: function dispose() {
1369
1197
  this.stopListening();
1370
1198
  this.reset();
1371
- }
1372
- });
1373
-
1199
+ },
1200
+ updateFilter: function updateFilter(predicate) {
1201
+ this.predicate = predicate || function () {
1202
+ return true;
1203
+ };
1204
+ var modelsToRemove = [];
1205
+ var modelsToAdd = [];
1206
+ this.parent.each(function (model) {
1207
+ var included = !!this.get(model);
1208
+ var shouldBeIncluded = this.predicate(model);
1209
+ if (shouldBeIncluded && !included) {
1210
+ modelsToAdd.push(model);
1211
+ }
1212
+ if (!shouldBeIncluded && included) {
1213
+ modelsToRemove.push(model);
1214
+ }
1215
+ }, this);
1216
+ if (modelsToRemove.length) {
1217
+ this.remove(modelsToRemove);
1218
+ }
1219
+ if (modelsToAdd.length) {
1220
+ this.add(modelsToAdd);
1221
+ }
1222
+ return this;
1223
+ }
1224
+ });
1225
+
1374
1226
  var FilesCollection = Backbone.Collection.extend({
1375
1227
  initialize: function initialize(models, options) {
1376
1228
  options = options || {};
@@ -1434,14 +1286,12 @@ var FilesCollection = Backbone.Collection.extend({
1434
1286
  });
1435
1287
  }
1436
1288
  });
1437
-
1438
1289
  FilesCollection.createForFileTypes = function (fileTypes, files, options) {
1439
1290
  return fileTypes.reduce(function (result, fileType) {
1440
1291
  result[fileType.collectionName] = FilesCollection.createForFileType(fileType, files[fileType.collectionName], options);
1441
1292
  return result;
1442
1293
  }, {});
1443
1294
  };
1444
-
1445
1295
  FilesCollection.createForFileType = function (fileType, files, options) {
1446
1296
  return new FilesCollection(files, _.extend({
1447
1297
  fileType: fileType,
@@ -1463,7 +1313,6 @@ var OtherEntry = Backbone.Model.extend({
1463
1313
  entry: this
1464
1314
  });
1465
1315
  }
1466
-
1467
1316
  return this.files[fileType.collectionName];
1468
1317
  },
1469
1318
  titleOrSlug: function titleOrSlug() {
@@ -1494,26 +1343,21 @@ var transientReferences = {
1494
1343
  var fileType = editor.fileTypes.findByCollectionName(collection);
1495
1344
  collection = state.entry.getFileCollection(fileType);
1496
1345
  }
1497
-
1498
1346
  return this.transientReferences[attribute] || collection.getByPermaId(this.get(attribute));
1499
1347
  },
1500
1348
  setReference: function setReference(attribute, record) {
1501
1349
  this._cleanUpReference(attribute);
1502
-
1503
1350
  this._setReference(attribute, record);
1504
-
1505
1351
  this._listenForReady(attribute, record);
1506
1352
  },
1507
1353
  unsetReference: function unsetReference(attribute) {
1508
1354
  this._cleanUpReference(attribute);
1509
-
1510
1355
  this.set(attribute, null);
1511
1356
  },
1512
1357
  _setReference: function _setReference(attribute, record) {
1513
1358
  if (record.isNew()) {
1514
1359
  this.transientReferences[attribute] = record;
1515
1360
  this.set(attribute, null);
1516
-
1517
1361
  this._setPermaIdOnceSynced(attribute, record);
1518
1362
  } else {
1519
1363
  this.set(attribute, record.get('perma_id'));
@@ -1539,7 +1383,6 @@ var transientReferences = {
1539
1383
  this.listenTo(record, 'change:state', function (model, value, options) {
1540
1384
  if (record.isReady()) {
1541
1385
  this._cleanUpReadyListener(attribute);
1542
-
1543
1386
  this.trigger('change', this, options);
1544
1387
  this.trigger('change:' + attribute + ':ready');
1545
1388
  }
@@ -1548,7 +1391,6 @@ var transientReferences = {
1548
1391
  },
1549
1392
  _cleanUpReference: function _cleanUpReference(attribute) {
1550
1393
  this._cleanUpSaveListener(attribute);
1551
-
1552
1394
  this._cleanUpReadyListener(attribute);
1553
1395
  },
1554
1396
  _cleanUpSaveListener: function _cleanUpSaveListener(attribute) {
@@ -1578,7 +1420,6 @@ var Configuration = Backbone.Model.extend({
1578
1420
  hide_title: false,
1579
1421
  autoplay: true
1580
1422
  },
1581
-
1582
1423
  /**
1583
1424
  * Used by views (i.e. FileInputView) to get id which can be used in
1584
1425
  * routes to lookup configuration via its page.
@@ -1590,15 +1431,13 @@ var Configuration = Backbone.Model.extend({
1590
1431
  getImageFileUrl: function getImageFileUrl(attribute, options) {
1591
1432
  options = options || {};
1592
1433
  var file = this.getImageFile(attribute);
1593
-
1594
1434
  if (file && file.isReady()) {
1595
1435
  return file.get(options.styleGroup ? options.styleGroup + '_url' : 'url');
1596
1436
  }
1597
-
1598
1437
  return '';
1599
1438
  },
1600
1439
  getImageFile: function getImageFile(attribute) {
1601
- return this.getReference(attribute, state.imageFiles);
1440
+ return this.getReference(attribute, 'image_files');
1602
1441
  },
1603
1442
  getFilePosition: function getFilePosition(attribute, coord) {
1604
1443
  var propertyName = this.filePositionProperty(attribute, coord);
@@ -1619,50 +1458,41 @@ var Configuration = Backbone.Model.extend({
1619
1458
  },
1620
1459
  getVideoFileSources: function getVideoFileSources(attribute) {
1621
1460
  var file = this.getVideoFile(attribute);
1622
-
1623
1461
  if (file && file.isReady()) {
1624
1462
  return file.get('sources') ? this._appendSuffix(file.get('sources')) : '';
1625
1463
  }
1626
-
1627
1464
  return '';
1628
1465
  },
1629
1466
  getVideoFile: function getVideoFile(attribute) {
1630
- return this.getReference(attribute, state.videoFiles);
1467
+ return this.getReference(attribute, 'video_files');
1631
1468
  },
1632
1469
  getAudioFileSources: function getAudioFileSources(attribute) {
1633
1470
  var file = this.getAudioFile(attribute);
1634
-
1635
1471
  if (file && file.isReady()) {
1636
1472
  return file.get('sources') ? this._appendSuffix(file.get('sources')) : '';
1637
1473
  }
1638
-
1639
1474
  return '';
1640
1475
  },
1641
1476
  getAudioFile: function getAudioFile(attribute) {
1642
- return this.getReference(attribute, state.audioFiles);
1477
+ return this.getReference(attribute, 'audio_files');
1643
1478
  },
1644
1479
  getVideoPosterUrl: function getVideoPosterUrl() {
1645
- var posterFile = this.getReference('poster_image_id', state.imageFiles),
1646
- videoFile = this.getReference('video_file_id', state.videoFiles);
1647
-
1480
+ var posterFile = this.getReference('poster_image_id', 'image_files'),
1481
+ videoFile = this.getReference('video_file_id', 'video_files');
1648
1482
  if (posterFile) {
1649
1483
  return posterFile.get('url');
1650
1484
  } else if (videoFile) {
1651
1485
  return videoFile.get('poster_url');
1652
1486
  }
1653
-
1654
1487
  return null;
1655
1488
  },
1656
1489
  _appendSuffix: function _appendSuffix(sources) {
1657
1490
  var parent = this.parent;
1658
-
1659
1491
  if (!parent || !parent.id) {
1660
1492
  return sources;
1661
1493
  }
1662
-
1663
1494
  return _.map(sources, function (source) {
1664
1495
  var clone = _.clone(source);
1665
-
1666
1496
  clone.src = clone.src + '?e=' + parent.id + '&t=' + new Date().getTime();
1667
1497
  return clone;
1668
1498
  });
@@ -1678,13 +1508,11 @@ app.on('mixin:configuration', function (mixin) {
1678
1508
  * by triggering a `destroying` event and adding a `isDestroying`
1679
1509
  * method.
1680
1510
  */
1681
-
1682
1511
  var delayedDestroying = {
1683
1512
  initialize: function initialize() {
1684
1513
  this._destroying = false;
1685
1514
  this._destroyed = false;
1686
1515
  },
1687
-
1688
1516
  /**
1689
1517
  * Trigger `destroying` event and send `DELETE` request. Only remove
1690
1518
  * model from collection once the request is done.
@@ -1704,14 +1532,12 @@ var delayedDestroying = {
1704
1532
  }
1705
1533
  });
1706
1534
  },
1707
-
1708
1535
  /**
1709
1536
  * Get whether the model is currently being destroyed.
1710
1537
  */
1711
1538
  isDestroying: function isDestroying() {
1712
1539
  return this._destroying;
1713
1540
  },
1714
-
1715
1541
  /**
1716
1542
  * Get whether the model has been destroyed.
1717
1543
  */
@@ -1758,11 +1584,83 @@ var failureTracking = {
1758
1584
  } else if (xhr.statusText === 'timeout') {
1759
1585
  return 'Der Server ist nicht erreichbar.';
1760
1586
  }
1761
-
1762
1587
  return '';
1763
1588
  }
1764
1589
  };
1765
1590
 
1591
+ /**
1592
+ * Mixins for models with a nested configuration model.
1593
+ *
1594
+ * Triggers events on the parent model of the form
1595
+ * `change:configuration` and `change:configuration:<attribute>`, when
1596
+ * the configuration changes.
1597
+ *
1598
+ * @param {Object} [options]
1599
+ * @param {Function} [options.configurationModel] -
1600
+ * Backbone model to use for nested configuration model.
1601
+ * @param {Boolean} [options.autoSave] -
1602
+ * Save model when configuration changes.
1603
+ * @param {Boolean|Array<String>} [options.includeAttributesInJSON] -
1604
+ * Include all or specific attributes of the parent model in the
1605
+ * data returned by `toJSON` besides the `configuration` property.
1606
+ * @param {Function} [options.afterInitialize] -
1607
+ * Callback to invoke once configuration has been set up. The
1608
+ * initialize method of this mixin is only invoked after the
1609
+ * initialize method of the class using the mixin. The callback
1610
+ * is invoked with `this` pointing to the model object.
1611
+ * @returns {Object} - Mixin to be included in model.
1612
+ *
1613
+ * @example
1614
+ *
1615
+ * import {configurationContainer} from 'pageflow/editor';
1616
+ *
1617
+ * const Section = Backbone.Model.extend({
1618
+ * mixins: [configurationContainer({autoSave: true})]
1619
+ * });
1620
+ *
1621
+ * const section = new Section({configuration: {some: 'value'}});
1622
+ * section.configuration.get('some') // => 'value';
1623
+ */
1624
+ function configurationContainer() {
1625
+ var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
1626
+ configurationModel = _ref.configurationModel,
1627
+ autoSave = _ref.autoSave,
1628
+ includeAttributesInJSON = _ref.includeAttributesInJSON,
1629
+ afterInitialize = _ref.afterInitialize;
1630
+ configurationModel = configurationModel || Configuration.extend({
1631
+ defaults: {}
1632
+ });
1633
+ return {
1634
+ initialize: function initialize() {
1635
+ this.configuration = new configurationModel(this.get('configuration'));
1636
+ this.configuration.parent = this;
1637
+ this.listenTo(this.configuration, 'change', function (model, options) {
1638
+ if (!this.isNew() && (!this.isDestroying || !this.isDestroying()) && (!this.isDestroyed || !this.isDestroyed()) && autoSave && options.autoSave !== false) {
1639
+ this.save();
1640
+ }
1641
+ this.trigger('change:configuration', this, undefined, options);
1642
+ _.chain(this.configuration.changed).keys().each(function (name) {
1643
+ this.trigger('change:configuration:' + name, this, this.configuration.get(name));
1644
+ }, this);
1645
+ });
1646
+ if (afterInitialize) {
1647
+ afterInitialize.call(this);
1648
+ }
1649
+ },
1650
+ toJSON: function toJSON() {
1651
+ var attributes = {};
1652
+ if (includeAttributesInJSON === true) {
1653
+ attributes = _.clone(this.attributes);
1654
+ } else if (includeAttributesInJSON) {
1655
+ attributes = _.pick(this.attributes, includeAttributesInJSON);
1656
+ }
1657
+ return _.extend(attributes, {
1658
+ configuration: this.configuration.toJSON()
1659
+ });
1660
+ }
1661
+ };
1662
+ }
1663
+
1766
1664
  var Page = Backbone.Model.extend({
1767
1665
  modelName: 'page',
1768
1666
  paramRoot: 'page',
@@ -1775,19 +1673,18 @@ var Page = Backbone.Model.extend({
1775
1673
  perma_id: ''
1776
1674
  };
1777
1675
  },
1778
- mixins: [failureTracking, delayedDestroying],
1676
+ mixins: [configurationContainer({
1677
+ autoSave: true,
1678
+ includeAttributesInJSON: true,
1679
+ configurationModel: Configuration,
1680
+ afterInitialize: function afterInitialize() {
1681
+ this.configuration.page = this;
1682
+ this.listenTo(this.configuration, 'change:title', function () {
1683
+ this.trigger('change:title');
1684
+ });
1685
+ }
1686
+ }), failureTracking, delayedDestroying],
1779
1687
  initialize: function initialize() {
1780
- this.configuration = new Configuration(this.get('configuration') || {});
1781
- this.configuration.parent = this.configuration.page = this;
1782
- this.listenTo(this.configuration, 'change', function () {
1783
- this.trigger('change:configuration', this);
1784
- });
1785
- this.listenTo(this.configuration, 'change:title', function () {
1786
- this.trigger('change:title');
1787
- });
1788
- this.listenTo(this.configuration, 'change', function () {
1789
- this.save();
1790
- });
1791
1688
  this.listenTo(this, 'change:template', function () {
1792
1689
  this.save();
1793
1690
  });
@@ -1816,7 +1713,6 @@ var Page = Backbone.Model.extend({
1816
1713
  if (candidate.condition && !conditionMet(candidate.condition, configuration)) {
1817
1714
  return result;
1818
1715
  }
1819
-
1820
1716
  return result || configuration.getReference(candidate.attribute, candidate.file_collection);
1821
1717
  }, null);
1822
1718
  },
@@ -1826,16 +1722,10 @@ var Page = Backbone.Model.extend({
1826
1722
  pageType: function pageType() {
1827
1723
  return editor.pageTypes.findByName(this.get('template'));
1828
1724
  },
1829
- toJSON: function toJSON() {
1830
- return _.extend(_.clone(this.attributes), {
1831
- configuration: this.configuration.toJSON()
1832
- });
1833
- },
1834
1725
  destroy: function destroy() {
1835
1726
  this.destroyWithDelay();
1836
1727
  }
1837
1728
  });
1838
-
1839
1729
  function conditionMet(condition, configuration) {
1840
1730
  if (condition.negated) {
1841
1731
  return configuration.get(condition.attribute) != condition.value;
@@ -1843,7 +1733,6 @@ function conditionMet(condition, configuration) {
1843
1733
  return configuration.get(condition.attribute) == condition.value;
1844
1734
  }
1845
1735
  }
1846
-
1847
1736
  Page.linkedPagesLayouts = ['default', 'hero_top_left', 'hero_top_right'];
1848
1737
  Page.textPositions = ['left', 'center', 'right'];
1849
1738
  Page.textPositionsWithoutCenterOption = ['left', 'right'];
@@ -1876,18 +1765,15 @@ var StorylineScaffold = Scaffold.extend({
1876
1765
  build: function build() {
1877
1766
  this.storyline = this.parent.buildStoryline(this.options.storylineAttributes);
1878
1767
  this.chapter = this.storyline.buildChapter();
1879
-
1880
1768
  if (this.options.depth === 'page') {
1881
1769
  this.page = this.chapter.buildPage();
1882
1770
  }
1883
-
1884
1771
  editor.trigger('scaffold:storyline', this.storyline);
1885
1772
  return this.storyline;
1886
1773
  },
1887
1774
  load: function load(response) {
1888
1775
  this.storyline.set(response.storyline);
1889
1776
  this.chapter.set(response.chapter);
1890
-
1891
1777
  if (this.page) {
1892
1778
  this.page.set(response.page);
1893
1779
  }
@@ -1905,7 +1791,6 @@ var FileReuse = Backbone.Model.extend({
1905
1791
  return '/editor/entries/' + this.entry.get('id') + '/files/' + this.collectionName + '/reuse';
1906
1792
  }
1907
1793
  });
1908
-
1909
1794
  FileReuse.submit = function (otherEntry, file, options) {
1910
1795
  new FileReuse({
1911
1796
  other_entry_id: otherEntry.get('id'),
@@ -1939,7 +1824,6 @@ var NestedFilesCollection = SubsetCollection.extend({
1939
1824
  },
1940
1825
  comparator: nestedFilesOrder && nestedFilesOrder.comparator
1941
1826
  });
1942
-
1943
1827
  if (nestedFilesOrder) {
1944
1828
  this.listenTo(this, 'change:configuration:' + nestedFilesOrder.binding, this.sort);
1945
1829
  }
@@ -1956,16 +1840,13 @@ var retryable = {
1956
1840
  options = options ? _.clone(options) : {};
1957
1841
  if (options.parse === void 0) options.parse = true;
1958
1842
  var model = this;
1959
-
1960
1843
  options.success = function (resp) {
1961
1844
  if (!model.set(model.parse(resp, options), options)) return false;
1962
1845
  model.trigger('sync', model, resp, options);
1963
1846
  };
1964
-
1965
1847
  options.error = function (resp) {
1966
1848
  model.trigger('error', model, resp, options);
1967
1849
  };
1968
-
1969
1850
  options.url = this.url() + '/retry';
1970
1851
  return this.sync('create', this, options);
1971
1852
  }
@@ -1995,7 +1876,6 @@ var FileStage = Backbone.Model.extend({
1995
1876
  this.set('finished', this.finishedStates.indexOf(state) >= 0);
1996
1877
  this.set('failed', this.failedStates.indexOf(state) >= 0);
1997
1878
  this.set('action_required', this.actionRequiredStates.indexOf(state) >= 0);
1998
-
1999
1879
  if (this.get('active')) {
2000
1880
  this.set('state', 'active');
2001
1881
  } else if (this.get('finished')) {
@@ -2054,28 +1934,28 @@ var stageProvider = {
2054
1934
  };
2055
1935
 
2056
1936
  var ReusableFile = Backbone.Model.extend({
2057
- mixins: [stageProvider, retryable],
1937
+ mixins: [configurationContainer({
1938
+ autoSave: true,
1939
+ configurationModel: FileConfiguration,
1940
+ includeAttributesInJSON: ['file_name', 'display_name', 'rights', 'parent_file_id', 'parent_file_model_type', 'content_type', 'file_size'],
1941
+ afterInitialize: function afterInitialize() {
1942
+ this.configuration.i18nKey = this.i18nKey;
1943
+ }
1944
+ }), stageProvider, retryable],
2058
1945
  initialize: function initialize(attributes, options) {
2059
1946
  this.options = options || {};
2060
- this.configuration = new FileConfiguration(this.get('configuration') || {});
2061
- this.configuration.i18nKey = this.i18nKey;
2062
- this.configuration.parent = this;
2063
- this.listenTo(this.configuration, 'change', function () {
2064
- this.trigger('change:configuration', this);
2065
-
2066
- _.chain(this.configuration.changed).keys().each(function (name) {
2067
- this.trigger('change:configuration:' + name, this, this.configuration.get(name));
2068
- }, this);
2069
-
1947
+ this.listenTo(this, 'change:rights', function () {
2070
1948
  if (!this.isNew()) {
2071
1949
  this.save();
2072
1950
  }
2073
1951
  });
2074
- this.listenTo(this, 'change:rights', function () {
1952
+ this.listenTo(this, 'change:display_name', function () {
2075
1953
  if (!this.isNew()) {
2076
1954
  this.save();
2077
1955
  }
2078
1956
  });
1957
+ this.listenTo(this, 'change:original_url change:display_name', this.updateDownloadUrl);
1958
+ this.updateDownloadUrl();
2079
1959
  this.listenTo(this, 'change', function (model, options) {
2080
1960
  if (options.applyConfigurationUpdaters) {
2081
1961
  this.configuration.applyUpdaters(this.fileType().configurationUpdaters, this.attributes.configuration);
@@ -2089,7 +1969,7 @@ var ReusableFile = Backbone.Model.extend({
2089
1969
  return this.options.fileType;
2090
1970
  },
2091
1971
  title: function title() {
2092
- return this.get('file_name');
1972
+ return this.get('display_name') || this.get('file_name');
2093
1973
  },
2094
1974
  thumbnailFile: function thumbnailFile() {
2095
1975
  return this;
@@ -2098,7 +1978,6 @@ var ReusableFile = Backbone.Model.extend({
2098
1978
  if (typeof supersetCollection === 'function') {
2099
1979
  supersetCollection = supersetCollection();
2100
1980
  }
2101
-
2102
1981
  var collectionName = supersetCollection.fileType.collectionName;
2103
1982
  this.nestedFilesCollections = this.nestedFilesCollections || {};
2104
1983
  this.nestedFilesCollections[collectionName] = this.nestedFilesCollections[collectionName] || new NestedFilesCollection({
@@ -2131,11 +2010,6 @@ var ReusableFile = Backbone.Model.extend({
2131
2010
  isPositionable: function isPositionable() {
2132
2011
  return false;
2133
2012
  },
2134
- toJSON: function toJSON() {
2135
- return _.extend(_.pick(this.attributes, 'file_name', 'rights', 'parent_file_id', 'parent_file_model_type', 'content_type', 'file_size'), {
2136
- configuration: this.configuration.toJSON()
2137
- });
2138
- },
2139
2013
  cancelUpload: function cancelUpload() {
2140
2014
  if (this.get('state') === 'uploading') {
2141
2015
  this.trigger('uploadCancelled');
@@ -2151,6 +2025,16 @@ var ReusableFile = Backbone.Model.extend({
2151
2025
  this.save({}, {
2152
2026
  url: this.url() + '/publish'
2153
2027
  });
2028
+ },
2029
+ updateDownloadUrl: function updateDownloadUrl() {
2030
+ var originalUrl = this.get('original_url');
2031
+ var displayName = this.get('display_name');
2032
+ if (originalUrl && displayName) {
2033
+ var separator = originalUrl.includes('?') ? '&' : '?';
2034
+ this.set('download_url', "".concat(originalUrl).concat(separator, "download=").concat(encodeURIComponent(displayName)));
2035
+ } else {
2036
+ this.unset('download_url');
2037
+ }
2154
2038
  }
2155
2039
  });
2156
2040
 
@@ -2169,7 +2053,6 @@ var UploadableFile = ReusableFile.extend({
2169
2053
  var EncodedFile = UploadableFile.extend({
2170
2054
  processingStages: function processingStages() {
2171
2055
  var stages = [];
2172
-
2173
2056
  if (state.config.confirmEncodingJobs) {
2174
2057
  stages.push({
2175
2058
  name: 'fetching_meta_data',
@@ -2177,7 +2060,6 @@ var EncodedFile = UploadableFile.extend({
2177
2060
  failedStates: ['fetching_meta_data_failed']
2178
2061
  });
2179
2062
  }
2180
-
2181
2063
  stages.push({
2182
2064
  name: 'encoding',
2183
2065
  actionRequiredStates: ['waiting_for_confirmation'],
@@ -2206,11 +2088,9 @@ var VideoFile = EncodedFile.extend({
2206
2088
 
2207
2089
  var WidgetConfigurationFileSelectionHandler = function WidgetConfigurationFileSelectionHandler(options) {
2208
2090
  var widget = state.entry.widgets.get(options.id);
2209
-
2210
2091
  this.call = function (file) {
2211
2092
  widget.configuration.setReference(options.attributeName, file);
2212
2093
  };
2213
-
2214
2094
  this.getReferer = function () {
2215
2095
  return '/widgets/' + widget.id;
2216
2096
  };
@@ -2268,10 +2148,8 @@ var EncodingConfirmation = Backbone.Model.extend({
2268
2148
  };
2269
2149
  }
2270
2150
  });
2271
-
2272
2151
  EncodingConfirmation.createWithPreselection = function (options) {
2273
2152
  var model = new EncodingConfirmation();
2274
-
2275
2153
  if (options.fileId) {
2276
2154
  if (options.fileType === 'video_file') {
2277
2155
  model.videoFiles.add(state.videoFiles.get(options.fileId));
@@ -2279,7 +2157,6 @@ EncodingConfirmation.createWithPreselection = function (options) {
2279
2157
  model.audioFiles.add(state.audioFiles.get(options.fileId));
2280
2158
  }
2281
2159
  }
2282
-
2283
2160
  return model;
2284
2161
  };
2285
2162
 
@@ -2318,7 +2195,6 @@ var AudioFile = EncodedFile.extend({
2318
2195
  if (this.isReady()) {
2319
2196
  return this.get('sources') ? this.get('sources') : '';
2320
2197
  }
2321
-
2322
2198
  return '';
2323
2199
  }
2324
2200
  });
@@ -2338,6 +2214,7 @@ var EntryMetadata = Configuration.extend({
2338
2214
  this.configuration = new EntryMetadataConfiguration(_.clone(attributes.configuration) || {});
2339
2215
  this.listenTo(this.configuration, 'change', function (model, options) {
2340
2216
  this.trigger('change', model, options);
2217
+ this.trigger('change:configuration', this, undefined, options);
2341
2218
  this.parent.save();
2342
2219
  });
2343
2220
  },
@@ -2374,7 +2251,6 @@ var TextTrackFile = UploadableFile.extend({
2374
2251
  readyState: 'processed',
2375
2252
  initialize: function initialize(attributes, options) {
2376
2253
  ReusableFile.prototype.initialize.apply(this, arguments);
2377
-
2378
2254
  if (this.isNew() && !this.configuration.get('srclang')) {
2379
2255
  this.configuration.set('srclang', this.extractLanguageCodeFromFilename());
2380
2256
  }
@@ -2384,7 +2260,6 @@ var TextTrackFile = UploadableFile.extend({
2384
2260
  },
2385
2261
  inferredLabel: function inferredLabel() {
2386
2262
  var srclang = this.configuration.get('srclang');
2387
-
2388
2263
  if (srclang) {
2389
2264
  return I18n$1.t('pageflow.languages.' + srclang, {
2390
2265
  defaultValue: ''
@@ -2400,7 +2275,6 @@ TextTrackFile.displayLabelBinding = 'srclang';
2400
2275
 
2401
2276
  var StorylineOrdering = function StorylineOrdering(storylines, pages) {
2402
2277
  var storylinesByParent;
2403
-
2404
2278
  this.watch = function () {
2405
2279
  storylines.on('add change:configuration', function () {
2406
2280
  this.sort();
@@ -2409,13 +2283,11 @@ var StorylineOrdering = function StorylineOrdering(storylines, pages) {
2409
2283
  this.sort();
2410
2284
  }, this);
2411
2285
  };
2412
-
2413
2286
  this.sort = function (options) {
2414
2287
  prepare();
2415
2288
  visit(storylinesWithoutParent(), 1, 0);
2416
2289
  storylines.sort(options);
2417
2290
  };
2418
-
2419
2291
  function visit(storylines, offset, level) {
2420
2292
  return _(storylines).reduce(function (position, storyline, index) {
2421
2293
  storyline.set('position', position);
@@ -2423,53 +2295,42 @@ var StorylineOrdering = function StorylineOrdering(storylines, pages) {
2423
2295
  return visit(children(storyline), position + 1, level + 1);
2424
2296
  }, offset);
2425
2297
  }
2426
-
2427
2298
  function storylinesWithoutParent() {
2428
2299
  return storylinesByParent[-1];
2429
2300
  }
2430
-
2431
2301
  function children(storyline) {
2432
2302
  return storylinesByParent[storyline.cid] || [];
2433
2303
  }
2434
-
2435
2304
  function prepare() {
2436
2305
  storylinesByParent = _(groupStorylinesByParentStoryline()).reduce(function (result, storylines, key) {
2437
2306
  result[key] = storylines.sort(compareStorylines);
2438
2307
  return result;
2439
2308
  }, {});
2440
2309
  }
2441
-
2442
2310
  function groupStorylinesByParentStoryline() {
2443
2311
  return storylines.groupBy(function (storyline) {
2444
2312
  var parentPage = getParentPage(storyline);
2445
2313
  return parentPage && parentPage.chapter ? parentPage.chapter.storyline.cid : -1;
2446
2314
  });
2447
2315
  }
2448
-
2449
2316
  function compareStorylines(storylineA, storylineB) {
2450
2317
  return compareByMainFlag(storylineA, storylineB) || compareByParentPagePosition(storylineA, storylineB) || compareByLane(storylineA, storylineB) || compareByRow(storylineA, storylineB) || compareByTitle(storylineA, storylineB);
2451
2318
  }
2452
-
2453
2319
  function compareByMainFlag(storylineA, storylineB) {
2454
2320
  return compare(storylineA.isMain() ? -1 : 1, storylineB.isMain() ? -1 : 1);
2455
2321
  }
2456
-
2457
2322
  function compareByParentPagePosition(storylineA, storylineB) {
2458
2323
  return compare(getParentPagePosition(storylineA), getParentPagePosition(storylineB));
2459
2324
  }
2460
-
2461
2325
  function compareByLane(storylineA, storylineB) {
2462
2326
  return compare(storylineA.lane(), storylineB.lane());
2463
2327
  }
2464
-
2465
2328
  function compareByRow(storylineA, storylineB) {
2466
2329
  return compare(storylineA.row(), storylineB.row());
2467
2330
  }
2468
-
2469
2331
  function compareByTitle(storylineA, storylineB) {
2470
2332
  return compare(storylineA.title(), storylineB.title());
2471
2333
  }
2472
-
2473
2334
  function compare(a, b) {
2474
2335
  if (a > b) {
2475
2336
  return 1;
@@ -2479,12 +2340,10 @@ var StorylineOrdering = function StorylineOrdering(storylines, pages) {
2479
2340
  return 0;
2480
2341
  }
2481
2342
  }
2482
-
2483
2343
  function getParentPagePosition(storyline) {
2484
2344
  var parentPage = getParentPage(storyline);
2485
2345
  return parentPage && parentPage.get('position');
2486
2346
  }
2487
-
2488
2347
  function getParentPage(storyline) {
2489
2348
  return pages.getByPermaId(storyline.parentPagePermaId());
2490
2349
  }
@@ -2492,11 +2351,9 @@ var StorylineOrdering = function StorylineOrdering(storylines, pages) {
2492
2351
 
2493
2352
  var PageConfigurationFileSelectionHandler = function PageConfigurationFileSelectionHandler(options) {
2494
2353
  var page = state.pages.get(options.id);
2495
-
2496
2354
  this.call = function (file) {
2497
2355
  page.configuration.setReference(options.attributeName, file);
2498
2356
  };
2499
-
2500
2357
  this.getReferer = function () {
2501
2358
  return '/pages/' + page.id + '/' + (options.returnToTab || 'files');
2502
2359
  };
@@ -2527,7 +2384,6 @@ var EntryMetadataFileSelectionHandler = function EntryMetadataFileSelectionHandl
2527
2384
  this.call = function (file) {
2528
2385
  state.entry.metadata.setReference(options.attributeName, file);
2529
2386
  };
2530
-
2531
2387
  this.getReferer = function () {
2532
2388
  return '/meta_data/' + (options.returnToTab || 'general');
2533
2389
  };
@@ -2627,12 +2483,10 @@ var EditLockContainer = Backbone.Model.extend({
2627
2483
  case 409:
2628
2484
  container.handleConflict(xhr, settings);
2629
2485
  break;
2630
-
2631
2486
  case 401:
2632
2487
  case 422:
2633
2488
  container.handleUnauthenticated();
2634
2489
  break;
2635
-
2636
2490
  default:
2637
2491
  container.handleError();
2638
2492
  }
@@ -2686,6 +2540,9 @@ var Widget = Backbone.Model.extend({
2686
2540
  widgetType: function widgetType() {
2687
2541
  return this.get('type_name') && this.widgetTypes.findByName(this.get('type_name'));
2688
2542
  },
2543
+ defineConfigurationEditorTabViewGroups: function defineConfigurationEditorTabViewGroups(groups) {
2544
+ this.widgetType() && this.widgetType().defineConfigurationEditorTabViewGroups(groups);
2545
+ },
2689
2546
  hasConfiguration: function hasConfiguration() {
2690
2547
  return !!(this.widgetType() && this.widgetType().hasConfiguration());
2691
2548
  },
@@ -2704,34 +2561,138 @@ var Widget = Backbone.Model.extend({
2704
2561
  }
2705
2562
  });
2706
2563
 
2564
+ var Search = Backbone.Model.extend({
2565
+ defaults: {
2566
+ term: '',
2567
+ order: 'alphabetical'
2568
+ },
2569
+ initialize: function initialize(attrs) {
2570
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2571
+ this.attribute = options.attribute;
2572
+ this.storageKey = options.storageKey;
2573
+ this.comparators = {
2574
+ alphabetical: function alphabetical(file) {
2575
+ var fileName = file.get('display_name');
2576
+ return fileName && fileName.toLowerCase ? fileName.toLowerCase() : fileName;
2577
+ },
2578
+ most_recent: function most_recent(file) {
2579
+ var date = file.get('created_at');
2580
+ return date ? -new Date(date).getTime() : -file.id;
2581
+ }
2582
+ };
2583
+ this.orderAttributes = {
2584
+ alphabetical: 'display_name',
2585
+ most_recent: 'created_at'
2586
+ };
2587
+ if (this.storageKey) {
2588
+ var storage = this.getLocalStorage();
2589
+ if (storage && storage[this.storageKey]) {
2590
+ this.set('order', storage[this.storageKey]);
2591
+ }
2592
+ this.on('change:order', function () {
2593
+ var storage = this.getLocalStorage();
2594
+ if (storage) {
2595
+ storage[this.storageKey] = this.get('order');
2596
+ }
2597
+ });
2598
+ }
2599
+ },
2600
+ getLocalStorage: function getLocalStorage() {
2601
+ try {
2602
+ return window.localStorage;
2603
+ } catch (e) {
2604
+ return null;
2605
+ }
2606
+ },
2607
+ matches: function matches(model) {
2608
+ var term = (this.get('term') || '').toLowerCase();
2609
+ var value = (model.get(this.attribute) || '').toLowerCase();
2610
+ return value.indexOf(term) >= 0;
2611
+ },
2612
+ applyTo: function applyTo(collection) {
2613
+ var subset = new SubsetCollection({
2614
+ parent: collection,
2615
+ parentModel: collection.parentModel,
2616
+ watchAttribute: this.attribute,
2617
+ filter: this.matches.bind(this),
2618
+ comparator: this.comparators[this.get('order')]
2619
+ });
2620
+ this.listenTo(this, 'change:term', function () {
2621
+ subset.updateFilter(this.matches.bind(this));
2622
+ });
2623
+ this.listenTo(this, 'change:order', function () {
2624
+ subset.comparator = this.comparators[this.get('order')];
2625
+ subset.sort();
2626
+ });
2627
+ this.listenTo(subset, 'change', function (model) {
2628
+ var attribute = this.orderAttributes[this.get('order')];
2629
+ if (model.hasChanged(attribute)) {
2630
+ subset.sort();
2631
+ }
2632
+ });
2633
+ return subset;
2634
+ }
2635
+ });
2636
+
2637
+ var ListHighlight = Backbone.Model.extend({
2638
+ defaults: {
2639
+ active: false
2640
+ },
2641
+ initialize: function initialize(attrs) {
2642
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2643
+ this.collection = options.collection;
2644
+ },
2645
+ next: function next() {
2646
+ this._move(1);
2647
+ },
2648
+ previous: function previous() {
2649
+ this._move(-1);
2650
+ },
2651
+ triggerSelect: function triggerSelect() {
2652
+ var id = this.get('currentId');
2653
+ if (id != null) {
2654
+ this.trigger("selected:".concat(id));
2655
+ }
2656
+ },
2657
+ _move: function _move(delta) {
2658
+ var collection = this.collection;
2659
+ var length = collection.length;
2660
+ if (!length) {
2661
+ return;
2662
+ }
2663
+ var currentId = this.get('currentId');
2664
+ var currentModel = currentId != null ? collection.get(currentId) : null;
2665
+ var index = currentModel ? collection.indexOf(currentModel) : -1;
2666
+ if (index === -1) {
2667
+ index = delta > 0 ? 0 : length - 1;
2668
+ } else {
2669
+ index = (index + delta + length) % length;
2670
+ }
2671
+ this.set('currentId', collection.at(index).id);
2672
+ }
2673
+ });
2674
+
2707
2675
  var StorylineTransitiveChildPages = function StorylineTransitiveChildPages(storyline, storylines, pages) {
2708
2676
  var isTranstiveChildStoryline;
2709
-
2710
2677
  this.contain = function (page) {
2711
2678
  if (!isTranstiveChildStoryline) {
2712
2679
  search();
2713
2680
  }
2714
-
2715
2681
  return !!isTranstiveChildStoryline[page.chapter.storyline.id];
2716
2682
  };
2717
-
2718
2683
  function search() {
2719
2684
  isTranstiveChildStoryline = storylines.reduce(function (memo, other) {
2720
2685
  var current = other;
2721
-
2722
2686
  while (current) {
2723
2687
  if (current === storyline || memo[current.id]) {
2724
2688
  memo[other.id] = true;
2725
2689
  return memo;
2726
2690
  }
2727
-
2728
2691
  current = parentStoryline(current);
2729
2692
  }
2730
-
2731
2693
  return memo;
2732
2694
  }, {});
2733
2695
  }
2734
-
2735
2696
  function parentStoryline(storyline) {
2736
2697
  var parentPage = pages.getByPermaId(storyline.parentPagePermaId());
2737
2698
  return parentPage && parentPage.chapter && parentPage.chapter.storyline;
@@ -2751,13 +2712,13 @@ var FileUploader = Object$1.extend({
2751
2712
  var file = new fileType.model({
2752
2713
  state: 'uploadable',
2753
2714
  file_name: upload.name,
2715
+ display_name: upload.name,
2754
2716
  content_type: upload.type,
2755
2717
  file_size: upload.size
2756
2718
  }, {
2757
2719
  fileType: fileType
2758
2720
  });
2759
2721
  var setTargetFile = editor$1.nextUploadTargetFile;
2760
-
2761
2722
  if (setTargetFile) {
2762
2723
  if (fileType.topLevelType || !setTargetFile.fileType().nestedFileTypes.contains(fileType)) {
2763
2724
  throw new InvalidNestedTypeError(upload, {
@@ -2765,7 +2726,6 @@ var FileUploader = Object$1.extend({
2765
2726
  fileType: fileType
2766
2727
  });
2767
2728
  }
2768
-
2769
2729
  file.set({
2770
2730
  parent_file_id: setTargetFile.get('id'),
2771
2731
  parent_file_model_type: setTargetFile.fileType().typeName
@@ -2776,20 +2736,16 @@ var FileUploader = Object$1.extend({
2776
2736
  fileTypes: this.fileTypes
2777
2737
  });
2778
2738
  }
2779
-
2780
2739
  this.entry.getFileCollection(fileType).add(file);
2781
2740
  var deferred = new $.Deferred();
2782
-
2783
2741
  if (setTargetFile) {
2784
2742
  deferred.resolve();
2785
2743
  } else {
2786
2744
  this.deferreds.push(deferred);
2787
-
2788
2745
  if (this.deferreds.length == 1) {
2789
2746
  this.trigger('new:batch');
2790
2747
  }
2791
2748
  }
2792
-
2793
2749
  return deferred.promise().then(function () {
2794
2750
  file.set('state', 'uploading');
2795
2751
  return file;
@@ -2799,12 +2755,10 @@ var FileUploader = Object$1.extend({
2799
2755
  },
2800
2756
  submit: function submit() {
2801
2757
  _(this.deferreds).invoke('resolve');
2802
-
2803
2758
  this.deferreds = [];
2804
2759
  },
2805
2760
  abort: function abort() {
2806
2761
  _(this.deferreds).invoke('reject');
2807
-
2808
2762
  this.deferreds = [];
2809
2763
  }
2810
2764
  });
@@ -2826,11 +2780,9 @@ var orderedCollection = {
2826
2780
  saveOrder: function saveOrder() {
2827
2781
  var parentModel = this.parentModel;
2828
2782
  var collection = this;
2829
-
2830
2783
  if (collection.isEmpty()) {
2831
2784
  return $.Deferred().resolve().promise();
2832
2785
  }
2833
-
2834
2786
  return Backbone.sync('update', parentModel, {
2835
2787
  url: collection.url() + '/order',
2836
2788
  attrs: {
@@ -2882,7 +2834,11 @@ var Chapter = Backbone.Model.extend({
2882
2834
  modelName: 'chapter',
2883
2835
  paramRoot: 'chapter',
2884
2836
  i18nKey: 'pageflow/chapter',
2885
- mixins: [failureTracking, delayedDestroying],
2837
+ mixins: [configurationContainer({
2838
+ autoSave: true,
2839
+ includeAttributesInJSON: true,
2840
+ configurationModel: ChapterConfiguration
2841
+ }), failureTracking, delayedDestroying],
2886
2842
  initialize: function initialize(attributes, options) {
2887
2843
  this.pages = new ChapterPagesCollection({
2888
2844
  pages: options.pages || state.pages,
@@ -2891,12 +2847,6 @@ var Chapter = Backbone.Model.extend({
2891
2847
  this.listenTo(this, 'change:title', function () {
2892
2848
  this.save();
2893
2849
  });
2894
- this.configuration = new ChapterConfiguration(this.get('configuration') || {});
2895
- this.listenTo(this.configuration, 'change', function () {
2896
- this.save();
2897
- this.trigger('change:configuration', this);
2898
- });
2899
- return attributes;
2900
2850
  },
2901
2851
  urlRoot: function urlRoot() {
2902
2852
  return this.isNew() ? this.collection.url() : '/chapters';
@@ -2958,21 +2908,16 @@ var Storyline = Backbone.Model.extend({
2958
2908
  modelName: 'storyline',
2959
2909
  paramRoot: 'storyline',
2960
2910
  i18nKey: 'pageflow/storyline',
2961
- mixins: [failureTracking, delayedDestroying],
2911
+ mixins: [configurationContainer({
2912
+ autoSave: true,
2913
+ configurationModel: StorylineConfiguration
2914
+ }), failureTracking, delayedDestroying],
2962
2915
  initialize: function initialize(attributes, options) {
2963
2916
  this.chapters = new StorylineChaptersCollection({
2964
2917
  chapters: options.chapters || state.chapters,
2965
2918
  storyline: this
2966
2919
  });
2967
- this.configuration = new StorylineConfiguration(this.get('configuration') || {});
2968
- this.listenTo(this.configuration, 'change', function () {
2969
- if (!this.isNew()) {
2970
- this.save();
2971
- }
2972
-
2973
- this.trigger('change:configuration', this);
2974
- });
2975
- this.listenTo(this.configuration, 'change:main', function (model, value) {
2920
+ this.listenTo(this, 'change:configuration:main', function (model, value) {
2976
2921
  this.trigger('change:main', this, value);
2977
2922
  });
2978
2923
  },
@@ -3021,11 +2966,6 @@ var Storyline = Backbone.Model.extend({
3021
2966
  scaffold.create();
3022
2967
  return scaffold;
3023
2968
  },
3024
- toJSON: function toJSON() {
3025
- return {
3026
- configuration: this.configuration.toJSON()
3027
- };
3028
- },
3029
2969
  destroy: function destroy() {
3030
2970
  this.destroyWithDelay();
3031
2971
  }
@@ -3063,87 +3003,15 @@ var PageLink = Backbone.Model.extend({
3063
3003
  var PageLinkFileSelectionHandler = function PageLinkFileSelectionHandler(options) {
3064
3004
  var page = state.pages.getByPermaId(options.id.split(':')[0]);
3065
3005
  var pageLink = page.pageLinks().get(options.id);
3066
-
3067
3006
  this.call = function (file) {
3068
3007
  pageLink.setReference(options.attributeName, file);
3069
3008
  };
3070
-
3071
3009
  this.getReferer = function () {
3072
3010
  return '/page_links/' + pageLink.id;
3073
3011
  };
3074
3012
  };
3075
3013
  editor.registerFileSelectionHandler('pageLink', PageLinkFileSelectionHandler);
3076
3014
 
3077
- /**
3078
- * Mixins for models with a nested configuration model.
3079
- *
3080
- * Triggers events on the parent model of the form
3081
- * `change:configuration` and `change:configuration:<attribute>`, when
3082
- * the configuration changes.
3083
- *
3084
- * @param {Object} [options]
3085
- * @param {Function} [options.configurationModel] -
3086
- * Backbone model to use for nested configuration model.
3087
- * @param {Boolean} [options.autoSave] -
3088
- * Save model when configuration changes.
3089
- * @param {Boolean|Array<String>} [options.includeAttributesInJSON] -
3090
- * Include all or specific attributes of the parent model in the
3091
- * data returned by `toJSON` besides the `configuration` property.
3092
- * @returns {Object} - Mixin to be included in model.
3093
- *
3094
- * @example
3095
- *
3096
- * import {configurationContainer} from 'pageflow/editor';
3097
- *
3098
- * const Section = Backbone.Model.extend({
3099
- * mixins: [configurationContainer({autoSave: true})]
3100
- * });
3101
- *
3102
- * const section = new Section({configuration: {some: 'value'}});
3103
- * section.configuration.get('some') // => 'value';
3104
- */
3105
-
3106
- function configurationContainer() {
3107
- var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
3108
- configurationModel = _ref.configurationModel,
3109
- autoSave = _ref.autoSave,
3110
- includeAttributesInJSON = _ref.includeAttributesInJSON;
3111
-
3112
- configurationModel = configurationModel || Configuration.extend({
3113
- defaults: {}
3114
- });
3115
- return {
3116
- initialize: function initialize() {
3117
- this.configuration = new configurationModel(this.get('configuration'));
3118
- this.configuration.parent = this;
3119
- this.listenTo(this.configuration, 'change', function (model, options) {
3120
- if (!this.isNew() && (!this.isDestroying || !this.isDestroying()) && (!this.isDestroyed || !this.isDestroyed()) && autoSave && options.autoSave !== false) {
3121
- this.save();
3122
- }
3123
-
3124
- this.trigger('change:configuration', this, undefined, options);
3125
-
3126
- _.chain(this.configuration.changed).keys().each(function (name) {
3127
- this.trigger('change:configuration:' + name, this, this.configuration.get(name));
3128
- }, this);
3129
- });
3130
- },
3131
- toJSON: function toJSON() {
3132
- var attributes = {};
3133
-
3134
- if (includeAttributesInJSON === true) {
3135
- attributes = _.clone(this.attributes);
3136
- } else if (includeAttributesInJSON) {
3137
- attributes = _.pick(this.attributes, includeAttributesInJSON);
3138
- }
3139
-
3140
- return _.extend(attributes, {
3141
- configuration: this.configuration.toJSON()
3142
- });
3143
- }
3144
- };
3145
- }
3146
-
3147
3015
  var persistedPromise = {
3148
3016
  persisted: function persisted() {
3149
3017
  var model = this;
@@ -3228,7 +3096,9 @@ var Entry = Backbone.Model.extend({
3228
3096
  initialize: function initialize(attributes, options) {
3229
3097
  options = options || {};
3230
3098
  this.metadata = new EntryMetadata(this.get('metadata') || {});
3231
- this.metadata.parent = this; // In 15.1 `entry.configuration` was turned into a new `Metadata`
3099
+ this.metadata.parent = this;
3100
+
3101
+ // In 15.1 `entry.configuration` was turned into a new `Metadata`
3232
3102
  // model. Some of the entry type specific data (like
3233
3103
  // `home_button_enabled`) was extraced into
3234
3104
  // `entry.metadata.configuration`. Attributes like `title` or `locale`
@@ -3236,9 +3106,9 @@ var Entry = Backbone.Model.extend({
3236
3106
  // entry.metadata. Since some plugins (e.g. `pageflow-vr`) depend on
3237
3107
  // reading the locale from `entry.configuration`, this `configuration`
3238
3108
  // keeps backwards compatibility.
3239
-
3240
3109
  this.configuration = this.metadata;
3241
3110
  this.themes = options.themes || state.themes;
3111
+ this.site = options.site || state.site;
3242
3112
  this.files = options.files || state.files;
3243
3113
  this.fileTypes = options.fileTypes || editor.fileTypes;
3244
3114
  this.storylines = options.storylines || state.storylines;
@@ -3312,7 +3182,6 @@ var Entry = Backbone.Model.extend({
3312
3182
  merge: false,
3313
3183
  remove: false
3314
3184
  });
3315
-
3316
3185
  entry.trigger('use:files');
3317
3186
  }
3318
3187
  });
@@ -3329,28 +3198,22 @@ var Entry = Backbone.Model.extend({
3329
3198
  parse: function parse(response, options) {
3330
3199
  if (response) {
3331
3200
  this.set(_.pick(response, 'published', 'published_until', 'password_protected', 'last_published_with_noindex'));
3332
-
3333
3201
  this._setFiles(response, {
3334
3202
  add: false,
3335
3203
  remove: false,
3336
3204
  applyConfigurationUpdaters: true
3337
3205
  });
3338
3206
  }
3339
-
3340
3207
  return response;
3341
3208
  },
3342
3209
  _setFiles: function _setFiles(response, options) {
3343
3210
  this.fileTypes.each(function (fileType) {
3344
- var filesAttributes = response[fileType.collectionName]; // Temporary solution until rights attributes is moved to
3345
- // configuration hash. If we are polling, prevent overwriting
3346
- // the rights attribute.
3347
-
3211
+ var filesAttributes = response[fileType.collectionName];
3348
3212
  if (options.merge !== false) {
3349
3213
  filesAttributes = _.map(filesAttributes, function (fileAttributes) {
3350
- return _.omit(fileAttributes, 'rights');
3214
+ return _.omit(fileAttributes, 'display_name', 'rights');
3351
3215
  });
3352
3216
  }
3353
-
3354
3217
  this.getFileCollection(fileType).set(filesAttributes, _.extend({
3355
3218
  fileType: fileType
3356
3219
  }, options));
@@ -3372,10 +3235,10 @@ var AuthenticationProvider = Object$1.extend({
3372
3235
  },
3373
3236
  authenticationPopup: function authenticationPopup(linkUrl, width, height) {
3374
3237
  var sep = linkUrl.indexOf('?') !== -1 ? '&' : '?',
3375
- url = linkUrl + sep + 'popup=true',
3376
- left = (screen.width - width) / 2 - 16,
3377
- top = (screen.height - height) / 2 - 50,
3378
- windowFeatures = 'menubar=no,toolbar=no,status=no,width=' + width + ',height=' + height + ',left=' + left + ',top=' + top;
3238
+ url = linkUrl + sep + 'popup=true',
3239
+ left = (screen.width - width) / 2 - 16,
3240
+ top = (screen.height - height) / 2 - 50,
3241
+ windowFeatures = 'menubar=no,toolbar=no,status=no,width=' + width + ',height=' + height + ',left=' + left + ',top=' + top;
3379
3242
  return window.open(url, 'authPopup', windowFeatures);
3380
3243
  },
3381
3244
  authenticateCallback: function authenticateCallback() {
@@ -3444,11 +3307,9 @@ var FileImport = Backbone.Model.extend({
3444
3307
  getFilesMetaData: function getFilesMetaData(options) {
3445
3308
  this.action = 'files_meta_data';
3446
3309
  var selectedFiles = this.get('selectedFiles');
3447
-
3448
3310
  for (var i = 0; i < selectedFiles.length; i++) {
3449
3311
  selectedFiles[i] = selectedFiles[i].toJSON();
3450
3312
  }
3451
-
3452
3313
  return this.fetch({
3453
3314
  data: {
3454
3315
  files: selectedFiles
@@ -3489,7 +3350,6 @@ var FileImport = Backbone.Model.extend({
3489
3350
  var file = selections.find(function (file) {
3490
3351
  return file.get('source_url') == item.source_url;
3491
3352
  });
3492
-
3493
3353
  if (file) {
3494
3354
  file.set(item.attributes);
3495
3355
  }
@@ -3524,7 +3384,6 @@ var ChaptersCollection = Backbone.Collection.extend({
3524
3384
  *
3525
3385
  * @since 15.1
3526
3386
  */
3527
-
3528
3387
  var ForeignKeySubsetCollection = SubsetCollection.extend({
3529
3388
  mixins: [orderedCollection],
3530
3389
  constructor: function constructor(options) {
@@ -3535,7 +3394,6 @@ var ForeignKeySubsetCollection = SubsetCollection.extend({
3535
3394
  if (options.parentReferenceAttribute) {
3536
3395
  model[options.parentReferenceAttribute] = parentModel;
3537
3396
  }
3538
-
3539
3397
  model.set(options.foreignKeyAttribute, parentModel.id);
3540
3398
  });
3541
3399
  SubsetCollection.prototype.constructor.call(this, {
@@ -3552,7 +3410,6 @@ var ForeignKeySubsetCollection = SubsetCollection.extend({
3552
3410
  this.invoke('trigger', 'dependentDestroy');
3553
3411
  this.clear();
3554
3412
  });
3555
-
3556
3413
  if (options.parentReferenceAttribute) {
3557
3414
  this.each(function (model) {
3558
3415
  return model[options.parentReferenceAttribute] = parentModel;
@@ -3619,7 +3476,6 @@ var PageLinksCollection = Backbone.Collection.extend({
3619
3476
  position: position
3620
3477
  };
3621
3478
  },
3622
-
3623
3479
  /** @private */
3624
3480
  getUniqueId: function getUniqueId() {
3625
3481
  var maxId = Math.max(0, _.max(this.map(function (pageLink) {
@@ -3640,10 +3496,9 @@ var OtherEntriesCollection = Backbone.Collection.extend({
3640
3496
  // of the "other" entries, after all.
3641
3497
  parse: function parse(response) {
3642
3498
  var excludeEntry = this.getExcludeEntry(),
3643
- filteredResponse = _.filter(response, function (entry) {
3644
- return entry.id != excludeEntry.id;
3645
- });
3646
-
3499
+ filteredResponse = _.filter(response, function (entry) {
3500
+ return entry.id != excludeEntry.id;
3501
+ });
3647
3502
  return Backbone.Collection.prototype.parse.call(this, filteredResponse);
3648
3503
  },
3649
3504
  getExcludeEntry: function getExcludeEntry() {
@@ -3726,7 +3581,6 @@ var PagesCollection = Backbone.Collection.extend({
3726
3581
  }, this), 0);
3727
3582
  });
3728
3583
  }
3729
-
3730
3584
  return this._persisted;
3731
3585
  }
3732
3586
  });
@@ -3737,18 +3591,17 @@ var ThemesCollection = Backbone.Collection.extend({
3737
3591
  var theme = this.findWhere({
3738
3592
  name: name
3739
3593
  });
3740
-
3741
3594
  if (!theme) {
3742
3595
  throw new Error('Found no theme by name ' + name);
3743
3596
  }
3744
-
3745
3597
  return theme;
3746
3598
  }
3747
3599
  });
3748
3600
 
3749
3601
  var WidgetsCollection = Backbone.Collection.extend({
3750
3602
  model: Widget,
3751
- initialize: function initialize() {
3603
+ initialize: function initialize(widgets, options) {
3604
+ this.widgetTypes = options.widgetTypes;
3752
3605
  this.listenTo(this, 'change:type_name change:configuration', function () {
3753
3606
  this.batchSave();
3754
3607
  });
@@ -3769,6 +3622,28 @@ var WidgetsCollection = Backbone.Collection.extend({
3769
3622
  subject.trigger('sync:widgets', subject, response, {});
3770
3623
  }
3771
3624
  }));
3625
+ },
3626
+ setupConfigurationEditorTabViewGroups: function setupConfigurationEditorTabViewGroups(groups) {
3627
+ var _this = this;
3628
+ this.defineConfigurationEditorTabViewGroups(groups);
3629
+ this.listenTo(this, 'change:type_name', function () {
3630
+ return _this.defineConfigurationEditorTabViewGroups(groups);
3631
+ });
3632
+ },
3633
+ defineConfigurationEditorTabViewGroups: function defineConfigurationEditorTabViewGroups(groups) {
3634
+ this.widgetTypes.defineStubConfigurationEditorTabViewGroups(groups);
3635
+ this.each(function (widget) {
3636
+ return widget.defineConfigurationEditorTabViewGroups(groups);
3637
+ });
3638
+ },
3639
+ withInsertPoint: function withInsertPoint(insertPoint) {
3640
+ return new SubsetCollection({
3641
+ parent: this,
3642
+ watchAttribute: 'type_name',
3643
+ filter: function filter(widget) {
3644
+ return widget.widgetType() && widget.widgetType().insertPoint === insertPoint;
3645
+ }
3646
+ });
3772
3647
  }
3773
3648
  });
3774
3649
 
@@ -3858,7 +3733,7 @@ function _slicedToArray(arr, i) {
3858
3733
  return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
3859
3734
  }
3860
3735
 
3861
- function template$2(data) {
3736
+ function template(data) {
3862
3737
  var __t, __p = '';
3863
3738
  __p += '<a class="back">' +
3864
3739
  ((__t = ( I18n.t('pageflow.editor.templates.back_button_decorator.outline') )) == null ? '' : __t) +
@@ -3867,7 +3742,7 @@ return __p
3867
3742
  }
3868
3743
 
3869
3744
  var BackButtonDecoratorView = Marionette.Layout.extend({
3870
- template: template$2,
3745
+ template: template,
3871
3746
  className: 'back_button_decorator',
3872
3747
  events: {
3873
3748
  'click a.back': 'goBack'
@@ -3886,7 +3761,7 @@ var BackButtonDecoratorView = Marionette.Layout.extend({
3886
3761
  }
3887
3762
  });
3888
3763
 
3889
- function template$3(data) {
3764
+ function template$1(data) {
3890
3765
  var __t, __p = '';
3891
3766
  __p += '<input type="checkbox">\n<label class="file_name"></label>\n<span class="duration"></span>\n\n<div class="actions">\n <a class="remove" title="' +
3892
3767
  ((__t = ( I18n.t('pageflow.editor.templates.confirmable_file_item.remove') )) == null ? '' : __t) +
@@ -3896,7 +3771,7 @@ return __p
3896
3771
 
3897
3772
  var ConfirmableFileItemView = Marionette.ItemView.extend({
3898
3773
  tagName: 'li',
3899
- template: template$3,
3774
+ template: template$1,
3900
3775
  ui: {
3901
3776
  fileName: '.file_name',
3902
3777
  duration: '.duration',
@@ -3912,7 +3787,7 @@ var ConfirmableFileItemView = Marionette.ItemView.extend({
3912
3787
  this.ui.label.attr('for', this.cid);
3913
3788
  this.ui.checkBox.attr('id', this.cid);
3914
3789
  this.ui.checkBox.prop('checked', this.options.selectedFiles.contains(this.model));
3915
- this.ui.fileName.text(this.model.get('file_name') || '(Unbekannt)');
3790
+ this.ui.fileName.text(this.model.title());
3916
3791
  this.ui.duration.text(this.model.get('duration') || '-');
3917
3792
  },
3918
3793
  destroy: function destroy() {
@@ -3929,7 +3804,7 @@ var ConfirmableFileItemView = Marionette.ItemView.extend({
3929
3804
  }
3930
3805
  });
3931
3806
 
3932
- function template$4(data) {
3807
+ function template$2(data) {
3933
3808
  var __t, __p = '';
3934
3809
  __p += '<div class="blank_slate">\n <p>\n ' +
3935
3810
  ((__t = ( I18n.t('pageflow.editor.templates.confirm_encoding.all_released') )) == null ? '' : __t) +
@@ -3948,7 +3823,7 @@ return __p
3948
3823
  }
3949
3824
 
3950
3825
  var ConfirmEncodingView = Marionette.ItemView.extend({
3951
- template: template$4,
3826
+ template: template$2,
3952
3827
  className: 'confirm_encoding',
3953
3828
  ui: {
3954
3829
  blankSlate: '.blank_slate',
@@ -4005,7 +3880,6 @@ var ConfirmEncodingView = Marionette.ItemView.extend({
4005
3880
  this.ui.intro.html(this.model.get('intro_html'));
4006
3881
  this.ui.summary.html(this.model.get('summary_html'));
4007
3882
  this.ui.confirmButton.toggleClass('checking', !!this.model.get('checking'));
4008
-
4009
3883
  if (this.model.get('empty') || this.model.get('exceeding') || this.model.get('checking')) {
4010
3884
  this.ui.confirmButton.attr('disabled', true);
4011
3885
  } else {
@@ -4013,7 +3887,6 @@ var ConfirmEncodingView = Marionette.ItemView.extend({
4013
3887
  }
4014
3888
  }
4015
3889
  });
4016
-
4017
3890
  ConfirmEncodingView.create = function (options) {
4018
3891
  return new BackButtonDecoratorView({
4019
3892
  view: new ConfirmEncodingView(options)
@@ -4039,7 +3912,6 @@ ConfirmEncodingView.create = function (options) {
4039
3912
  * @param {String} options.classNames.retryButton -
4040
3913
  * Class name of the element that shall act as a retry button.
4041
3914
  */
4042
-
4043
3915
  function modelLifecycleTrackingView(_ref) {
4044
3916
  var classNames = _ref.classNames;
4045
3917
  return {
@@ -4049,13 +3921,11 @@ function modelLifecycleTrackingView(_ref) {
4049
3921
  }),
4050
3922
  initialize: function initialize() {
4051
3923
  var _this = this;
4052
-
4053
3924
  if (classNames.creating) {
4054
3925
  this.listenTo(this.model, 'change:id', function () {
4055
3926
  this.$el.removeClass(classNames.creating);
4056
3927
  });
4057
3928
  }
4058
-
4059
3929
  if (classNames.destroying) {
4060
3930
  this.listenTo(this.model, 'destroying', function () {
4061
3931
  this.$el.addClass(classNames.destroying);
@@ -4064,7 +3934,6 @@ function modelLifecycleTrackingView(_ref) {
4064
3934
  this.$el.removeClass(classNames.destroying);
4065
3935
  });
4066
3936
  }
4067
-
4068
3937
  if (classNames.failed || classNames.failureMessage) {
4069
3938
  this.listenTo(this.model, 'change:failed', function () {
4070
3939
  return _this.updateFailIndicator();
@@ -4075,18 +3944,15 @@ function modelLifecycleTrackingView(_ref) {
4075
3944
  if (this.model.isNew()) {
4076
3945
  this.$el.addClass(classNames.creating);
4077
3946
  }
4078
-
4079
3947
  if (this.model.isDestroying && this.model.isDestroying()) {
4080
3948
  this.$el.addClass(classNames.destroying);
4081
3949
  }
4082
-
4083
3950
  this.updateFailIndicator();
4084
3951
  },
4085
3952
  updateFailIndicator: function updateFailIndicator() {
4086
3953
  if (classNames.failed) {
4087
3954
  this.$el.toggleClass(classNames.failed, this.model.isFailed());
4088
3955
  }
4089
-
4090
3956
  if (classNames.failureMessage) {
4091
3957
  this.$el.find(".".concat(classNames.failureMessage)).text(this.model.getFailureMessage());
4092
3958
  }
@@ -4102,7 +3968,7 @@ var failureIndicatingView = modelLifecycleTrackingView({
4102
3968
  }
4103
3969
  });
4104
3970
 
4105
- function template$5(data) {
3971
+ function template$3(data) {
4106
3972
  var __t, __p = '';
4107
3973
  __p += '<a class="close" href="#">' +
4108
3974
  ((__t = ( I18n.t('pageflow.editor.templates.edit_entry.close') )) == null ? '' : __t) +
@@ -4119,7 +3985,7 @@ return __p
4119
3985
  }
4120
3986
 
4121
3987
  var EditEntryView = Marionette.Layout.extend({
4122
- template: template$5,
3988
+ template: template$3,
4123
3989
  mixins: [failureIndicatingView, tooltipContainer],
4124
3990
  ui: {
4125
3991
  publishButton: 'a.publish',
@@ -4141,7 +4007,6 @@ var EditEntryView = Marionette.Layout.extend({
4141
4007
  trigger: true
4142
4008
  });
4143
4009
  }
4144
-
4145
4010
  return false;
4146
4011
  },
4147
4012
  'click .menu a': function clickMenuA(event) {
@@ -4153,9 +4018,7 @@ var EditEntryView = Marionette.Layout.extend({
4153
4018
  },
4154
4019
  onRender: function onRender() {
4155
4020
  this._addMenuItems();
4156
-
4157
4021
  this._updatePublishButton();
4158
-
4159
4022
  this.outlineRegion.show(new editor.entryType.outlineView({
4160
4023
  entry: state.entry,
4161
4024
  navigatable: true,
@@ -4168,7 +4031,6 @@ var EditEntryView = Marionette.Layout.extend({
4168
4031
  _updatePublishButton: function _updatePublishButton() {
4169
4032
  var disabled = !this.model.get('publishable');
4170
4033
  this.ui.publishButton.toggleClass('disabled', disabled);
4171
-
4172
4034
  if (disabled) {
4173
4035
  this.ui.publishButton.attr('data-tooltip', 'pageflow.editor.views.edit_entry_view.cannot_publish');
4174
4036
  } else {
@@ -4177,31 +4039,25 @@ var EditEntryView = Marionette.Layout.extend({
4177
4039
  },
4178
4040
  _addMenuItems: function _addMenuItems() {
4179
4041
  var view = this;
4180
-
4181
4042
  _.each(editor.mainMenuItems, function (options) {
4182
4043
  var item = $('<li><a href="#"></a></li>');
4183
4044
  var link = item.find('a');
4184
-
4185
4045
  if (options.path) {
4186
4046
  link.data('path', options.path);
4187
4047
  }
4188
-
4189
4048
  if (options.id) {
4190
4049
  link.attr('data-main-menu-item', options.id);
4191
4050
  }
4192
-
4193
4051
  link.text(I18n$1.t(options.translationKey));
4194
-
4195
4052
  if (options.click) {
4196
4053
  $(link).click(options.click);
4197
4054
  }
4198
-
4199
4055
  view.ui.menu.append(item);
4200
4056
  });
4201
4057
  }
4202
4058
  });
4203
4059
 
4204
- function template$6(data) {
4060
+ function template$4(data) {
4205
4061
  var __t, __p = '';
4206
4062
  __p += '<div class="widget_type">\n</div>\n<a class="settings" title="' +
4207
4063
  ((__t = ( I18n.t('pageflow.editor.templates.widget_item.settings') )) == null ? '' : __t) +
@@ -4210,7 +4066,7 @@ return __p
4210
4066
  }
4211
4067
 
4212
4068
  var WidgetItemView = Marionette.Layout.extend({
4213
- template: template$6,
4069
+ template: template$4,
4214
4070
  tagName: 'li',
4215
4071
  className: 'widget_item',
4216
4072
  regions: {
@@ -4247,14 +4103,14 @@ var WidgetItemView = Marionette.Layout.extend({
4247
4103
  }
4248
4104
  });
4249
4105
 
4250
- function template$7(data) {
4106
+ function template$5(data) {
4251
4107
  var __p = '';
4252
4108
  __p += '<ol class="widgets">\n</ol>\n';
4253
4109
  return __p
4254
4110
  }
4255
4111
 
4256
4112
  var EditWidgetsView = Marionette.Layout.extend({
4257
- template: template$7,
4113
+ template: template$5,
4258
4114
  ui: {
4259
4115
  widgets: '.widgets'
4260
4116
  },
@@ -4270,14 +4126,14 @@ var EditWidgetsView = Marionette.Layout.extend({
4270
4126
  }
4271
4127
  });
4272
4128
 
4273
- function template$8(data) {
4129
+ function template$6(data) {
4274
4130
  var __p = '';
4275
4131
  __p += '<div class="image"></div>\n<div class="label"></div>\n';
4276
4132
  return __p
4277
4133
  }
4278
4134
 
4279
4135
  var BackgroundPositioningPreviewView = Marionette.ItemView.extend({
4280
- template: template$8,
4136
+ template: template$6,
4281
4137
  className: 'preview',
4282
4138
  modelEvents: {
4283
4139
  change: 'update'
@@ -4308,14 +4164,14 @@ var BackgroundPositioningPreviewView = Marionette.ItemView.extend({
4308
4164
  }
4309
4165
  });
4310
4166
 
4311
- function template$9(data) {
4167
+ function template$7(data) {
4312
4168
  var __p = '';
4313
4169
  __p += '<div class="container">\n <div class="slider horizontal">\n </div>\n <div class="slider vertical">\n </div>\n <div class="percent horizontal">\n <input type="number" min="0" max="100">\n %\n </div>\n <div class="percent vertical">\n <input type="number" min="0" max="100">\n %\n </div>\n</div>\n';
4314
4170
  return __p
4315
4171
  }
4316
4172
 
4317
4173
  var BackgroundPositioningSlidersView = Marionette.ItemView.extend({
4318
- template: template$9,
4174
+ template: template$7,
4319
4175
  className: '',
4320
4176
  ui: {
4321
4177
  container: '.container',
@@ -4328,15 +4184,12 @@ var BackgroundPositioningSlidersView = Marionette.ItemView.extend({
4328
4184
  'mousedown img': function mousedownImg(event) {
4329
4185
  var view = this;
4330
4186
  view.saveFromEvent(event);
4331
-
4332
4187
  function onMove(event) {
4333
4188
  view.saveFromEvent(event);
4334
4189
  }
4335
-
4336
4190
  function onUp() {
4337
4191
  $('.background_positioning.dialog').off('mousemove', onMove).off('mouseup', onUp);
4338
4192
  }
4339
-
4340
4193
  $('.background_positioning.dialog').on('mousemove', onMove).on('mouseup', onUp);
4341
4194
  },
4342
4195
  'dragstart img': function dragstartImg(event) {
@@ -4349,7 +4202,7 @@ var BackgroundPositioningSlidersView = Marionette.ItemView.extend({
4349
4202
  onRender: function onRender() {
4350
4203
  var view = this;
4351
4204
  var file = this.model.getReference(this.options.propertyName, this.options.filesCollection),
4352
- image = $('<img />').attr('src', file.getBackgroundPositioningImageUrl());
4205
+ image = $('<img />').attr('src', file.getBackgroundPositioningImageUrl());
4353
4206
  this.ui.container.append(image);
4354
4207
  this.ui.sliderVertical.slider({
4355
4208
  orientation: 'vertical',
@@ -4396,7 +4249,23 @@ var BackgroundPositioningSlidersView = Marionette.ItemView.extend({
4396
4249
  }
4397
4250
  });
4398
4251
 
4399
- function template$a(data) {
4252
+ var dialogView = {
4253
+ events: {
4254
+ 'mousedown': function mousedown(event) {
4255
+ if (!event.target.closest(".box")) {
4256
+ this.close();
4257
+ }
4258
+ },
4259
+ 'click .close': function clickClose() {
4260
+ this.close();
4261
+ },
4262
+ 'click .box': function clickBox() {
4263
+ return false;
4264
+ }
4265
+ }
4266
+ };
4267
+
4268
+ function template$8(data) {
4400
4269
  var __t, __p = '';
4401
4270
  __p += '<div class="box">\n <div class="content">\n <h2 class="dialog-header">' +
4402
4271
  ((__t = ( I18n.t('pageflow.editor.templates.background_positioning.title') )) == null ? '' : __t) +
@@ -4413,7 +4282,7 @@ return __p
4413
4282
  }
4414
4283
 
4415
4284
  var BackgroundPositioningView = Marionette.ItemView.extend({
4416
- template: template$a,
4285
+ template: template$8,
4417
4286
  className: 'background_positioning editor dialog',
4418
4287
  mixins: [dialogView],
4419
4288
  ui: {
@@ -4449,32 +4318,35 @@ var BackgroundPositioningView = Marionette.ItemView.extend({
4449
4318
  },
4450
4319
  createPreviews: function createPreviews() {
4451
4320
  var view = this;
4452
-
4453
- _.each(view.previews, function (ratio, name) {
4321
+ var previews = this.options.preview ? {
4322
+ preview: this.options.preview
4323
+ } : this.previews;
4324
+ _.each(previews, function (ratio, name) {
4454
4325
  view.ui.previews.append(view.subview(new BackgroundPositioningPreviewView({
4455
4326
  model: view.transientModel,
4456
4327
  propertyName: view.options.propertyName,
4457
4328
  filesCollection: view.options.filesCollection,
4458
4329
  ratio: ratio,
4459
4330
  maxSize: 200,
4460
- label: I18n$1.t('pageflow.editor.templates.background_positioning.previews.' + name)
4331
+ label: I18n$1.t('pageflow.editor.templates.background_positioning.previews.' + name, {
4332
+ defaultValue: ''
4333
+ })
4461
4334
  })).el);
4462
4335
  });
4463
4336
  }
4464
4337
  });
4465
-
4466
4338
  BackgroundPositioningView.open = function (options) {
4467
4339
  app.dialogRegion.show(new BackgroundPositioningView(options));
4468
4340
  };
4469
4341
 
4470
- function template$b(data) {
4342
+ function template$9(data) {
4471
4343
  var __p = '';
4472
4344
  __p += '<div class="label"></div>\n<a href="#"></a>\n';
4473
4345
  return __p
4474
4346
  }
4475
4347
 
4476
4348
  var DropDownButtonItemView = Marionette.ItemView.extend({
4477
- template: template$b,
4349
+ template: template$9,
4478
4350
  tagName: 'li',
4479
4351
  className: 'drop_down_button_item',
4480
4352
  ui: {
@@ -4486,9 +4358,7 @@ var DropDownButtonItemView = Marionette.ItemView.extend({
4486
4358
  if (!this.model.get('disabled')) {
4487
4359
  this.model.selected();
4488
4360
  }
4489
-
4490
4361
  event.preventDefault();
4491
-
4492
4362
  if (this.model.get('kind') === 'checkBox' || this.model.get('kind') === 'radio') {
4493
4363
  event.stopPropagation();
4494
4364
  }
@@ -4499,7 +4369,6 @@ var DropDownButtonItemView = Marionette.ItemView.extend({
4499
4369
  },
4500
4370
  onRender: function onRender() {
4501
4371
  this.update();
4502
-
4503
4372
  if (this.model.get('items')) {
4504
4373
  this.appendSubview(new this.options.listView({
4505
4374
  items: this.model.get('items')
@@ -4532,7 +4401,7 @@ var DropDownButtonItemListView = function DropDownButtonItemListView(options) {
4532
4401
  });
4533
4402
  };
4534
4403
 
4535
- function template$c(data) {
4404
+ function template$a(data) {
4536
4405
  var __p = '';
4537
4406
  __p += '<button></button>\n\n<div class="drop_down_button_menu">\n</div>\n';
4538
4407
  return __p
@@ -4579,23 +4448,20 @@ return __p
4579
4448
  *
4580
4449
  * @class
4581
4450
  */
4582
-
4583
4451
  var DropDownButtonView = Marionette.ItemView.extend({
4584
- template: template$c,
4452
+ template: template$a,
4585
4453
  className: 'drop_down_button',
4586
4454
  ui: {
4587
4455
  button: '> button',
4588
4456
  menu: '.drop_down_button_menu'
4589
4457
  },
4590
4458
  events: function events() {
4591
- var _ref;
4592
-
4593
- return _ref = {}, _defineProperty(_ref, this.options.openOnClick ? 'click' : 'mouseenter', function () {
4459
+ return _defineProperty(_defineProperty({}, this.options.openOnClick ? 'click' : 'mouseenter', function () {
4594
4460
  this.positionMenu();
4595
4461
  this.showMenu();
4596
- }), _defineProperty(_ref, 'mouseleave', function mouseleave() {
4462
+ }), 'mouseleave', function mouseleave() {
4597
4463
  this.scheduleHideMenu();
4598
- }), _ref;
4464
+ });
4599
4465
  },
4600
4466
  onRender: function onRender() {
4601
4467
  var view = this;
@@ -4603,7 +4469,9 @@ var DropDownButtonView = Marionette.ItemView.extend({
4603
4469
  this.ui.button.toggleClass('has_icon_and_text', !!this.options.label);
4604
4470
  this.ui.button.toggleClass('has_icon_only', !this.options.label);
4605
4471
  this.ui.button.toggleClass('ellipsis_icon', !!this.options.ellipsisIcon);
4472
+ this.ui.button.toggleClass('borderless', !!this.options.borderless);
4606
4473
  this.ui.button.text(this.options.label);
4474
+ this.ui.button.attr('title', this.options.title);
4607
4475
  this.ui.button.addClass(this.options.buttonClassName);
4608
4476
  this.ui.menu.append(this.subview(new DropDownButtonItemListView({
4609
4477
  items: this.options.items
@@ -4642,12 +4510,10 @@ var DropDownButtonView = Marionette.ItemView.extend({
4642
4510
  if (DropDownButtonView.currentlyShowingMenu) {
4643
4511
  DropDownButtonView.currentlyShowingMenu.hideMenu();
4644
4512
  }
4645
-
4646
4513
  DropDownButtonView.currentlyShowingMenu = this;
4647
4514
  },
4648
4515
  hideMenu: function hideMenu() {
4649
4516
  clearTimeout(this.hideMenuTimeout);
4650
-
4651
4517
  if (!this.isClosed) {
4652
4518
  this.ui.button.removeClass('hover');
4653
4519
  this.ui.menu.removeClass('is_visible');
@@ -4658,7 +4524,44 @@ var DropDownButtonView = Marionette.ItemView.extend({
4658
4524
  }
4659
4525
  });
4660
4526
 
4661
- function template$d(data) {
4527
+ function template$b(data) {
4528
+ var __t, __p = '';
4529
+ __p += '<div class="box">\n <div class="content">\n <h1 class="dialog-header"></h1>\n </div>\n\n <div class="footer">\n <a href="" class="close">\n ' +
4530
+ ((__t = ( I18n.t('pageflow.editor.templates.file_settings_dialog.close') )) == null ? '' : __t) +
4531
+ '\n </a>\n </div>\n</div>\n';
4532
+ return __p
4533
+ }
4534
+
4535
+ var FileSettingsDialogView = Marionette.ItemView.extend({
4536
+ template: template$b,
4537
+ className: 'file_settings_dialog editor dialog',
4538
+ mixins: [dialogView],
4539
+ ui: {
4540
+ content: '.content',
4541
+ header: '.dialog-header'
4542
+ },
4543
+ onRender: function onRender() {
4544
+ this.ui.header.text(this.model.title());
4545
+ this.tabsView = new TabsView({
4546
+ model: this.model,
4547
+ i18n: 'pageflow.editor.files.settings_dialog_tabs',
4548
+ defaultTab: this.options.tabName
4549
+ });
4550
+ _.each(this.model.fileType().settingsDialogTabs, function (options) {
4551
+ this.tabsView.tab(options.name, _.bind(function () {
4552
+ return this.subview(new options.view(_.extend({
4553
+ model: this.model
4554
+ }, options.viewOptions)));
4555
+ }, this));
4556
+ }, this);
4557
+ this.ui.content.append(this.subview(this.tabsView).el);
4558
+ }
4559
+ });
4560
+ FileSettingsDialogView.open = function (options) {
4561
+ app.dialogRegion.show(new FileSettingsDialogView(options));
4562
+ };
4563
+
4564
+ function template$c(data) {
4662
4565
  var __p = '';
4663
4566
  __p += '<div class="pictogram"></div>\n';
4664
4567
  return __p
@@ -4666,7 +4569,7 @@ return __p
4666
4569
 
4667
4570
  var FileThumbnailView = Marionette.ItemView.extend({
4668
4571
  className: 'file_thumbnail',
4669
- template: template$d,
4572
+ template: template$c,
4670
4573
  modelEvents: {
4671
4574
  'change:state': 'update'
4672
4575
  },
@@ -4679,7 +4582,6 @@ var FileThumbnailView = Marionette.ItemView.extend({
4679
4582
  update: function update() {
4680
4583
  if (this.model) {
4681
4584
  var stage = this.model.currentStage();
4682
-
4683
4585
  if (stage) {
4684
4586
  this.setStageClassName(stage.get('name'));
4685
4587
  this.ui.pictogram.toggleClass('action_required', stage.get('action_required'));
@@ -4687,7 +4589,6 @@ var FileThumbnailView = Marionette.ItemView.extend({
4687
4589
  } else {
4688
4590
  this.ui.pictogram.removeClass(this.model.stages.pluck('name').join(' '));
4689
4591
  }
4690
-
4691
4592
  this.ui.pictogram.addClass(this.model.thumbnailPictogram);
4692
4593
  this.$el.css('background-image', this._imageUrl() ? 'url(' + this._imageUrl() + ')' : '');
4693
4594
  this.$el.removeClass('empty').toggleClass('always_picogram', !!this.model.thumbnailPictogram).toggleClass('ready', this.model.isReady());
@@ -4714,7 +4615,6 @@ var FileThumbnailView = Marionette.ItemView.extend({
4714
4615
  *
4715
4616
  * @class
4716
4617
  */
4717
-
4718
4618
  var FileInputView = Marionette.ItemView.extend({
4719
4619
  mixins: [inputView],
4720
4620
  template: function template() {
@@ -4747,19 +4647,15 @@ var FileInputView = Marionette.ItemView.extend({
4747
4647
  positioning: true,
4748
4648
  textTrackFiles: state.textTrackFiles
4749
4649
  }, this.options);
4750
-
4751
4650
  if (typeof this.options.collection === 'string') {
4752
4651
  this.options.collection = state.entry.getFileCollection(editor.fileTypes.findByCollectionName(this.options.collection));
4753
4652
  }
4754
-
4755
4653
  this.textTrackMenuItems = new Backbone.Collection();
4756
4654
  },
4757
4655
  onRender: function onRender() {
4758
4656
  this.update();
4759
4657
  this.listenTo(this.model, 'change:' + this.options.propertyName, this.update);
4760
-
4761
4658
  var dropDownMenuItems = this._dropDownMenuItems();
4762
-
4763
4659
  if (dropDownMenuItems.length) {
4764
4660
  this.appendSubview(new DropDownButtonView({
4765
4661
  items: dropDownMenuItems,
@@ -4767,26 +4663,27 @@ var FileInputView = Marionette.ItemView.extend({
4767
4663
  openOnClick: true
4768
4664
  }));
4769
4665
  }
4666
+ this.setupBooleanAttributeBinding('positioning', this._updatePositioning);
4770
4667
  },
4771
4668
  update: function update() {
4772
4669
  var file = this._getFile();
4773
-
4774
4670
  this._listenToNestedTextTrackFiles(file);
4775
-
4776
4671
  this.$el.toggleClass('is_unset', !file);
4777
- this.ui.fileName.text(file ? file.get('file_name') : I18n$1.t('pageflow.ui.views.inputs.file_input_view.none'));
4672
+ this.ui.fileName.text(file ? file.title() : I18n$1.t('pageflow.ui.views.inputs.file_input_view.none'));
4778
4673
  this.subview(new FileThumbnailView({
4779
4674
  el: this.ui.thumbnail,
4780
4675
  model: file
4781
4676
  }));
4782
4677
  },
4678
+ _updatePositioning: function _updatePositioning(positioning) {
4679
+ if (this.positioningMenuItem) {
4680
+ this.positioningMenuItem.set('hidden', !positioning);
4681
+ }
4682
+ },
4783
4683
  _dropDownMenuItems: function _dropDownMenuItems() {
4784
4684
  var _this = this;
4785
-
4786
4685
  var file = this._getFile(file);
4787
-
4788
4686
  var items = new Backbone.Collection();
4789
-
4790
4687
  if (this.options.defaultTextTrackFilePropertyName && file) {
4791
4688
  items.add({
4792
4689
  name: 'default_text_track',
@@ -4794,23 +4691,28 @@ var FileInputView = Marionette.ItemView.extend({
4794
4691
  items: this.textTrackMenuItems
4795
4692
  });
4796
4693
  }
4797
-
4798
- if (this.options.positioning && file && file.isPositionable()) {
4799
- items.add(new FileInputView.EditBackgroundPositioningMenuItem({
4694
+ if (file && file.isPositionable()) {
4695
+ this.positioningMenuItem = new FileInputView.EditBackgroundPositioningMenuItem({
4800
4696
  name: 'edit_background_positioning',
4801
4697
  label: I18n$1.t('pageflow.editor.views.inputs.file_input.edit_background_positioning')
4802
4698
  }, {
4803
4699
  inputModel: this.model,
4804
4700
  propertyName: this.options.propertyName,
4805
- filesCollection: this.options.collection
4806
- }));
4701
+ filesCollection: this.options.collection,
4702
+ positioningOptions: this.options.positioningOptions
4703
+ });
4704
+ items.add(this.positioningMenuItem);
4807
4705
  }
4808
-
4809
4706
  if (file) {
4810
4707
  _.each(this.options.dropDownMenuItems, function (item) {
4811
4708
  items.add(_this._createCustomMenuItem(file, item));
4812
4709
  });
4813
-
4710
+ if (this.options.dropDownMenuName) {
4711
+ var customItems = editor.dropDownMenuItems.findAllByMenuName(this.options.dropDownMenuName);
4712
+ _.each(customItems, function (item) {
4713
+ items.add(_this._createCustomMenuItem(file, item));
4714
+ });
4715
+ }
4814
4716
  items.add(new FileInputView.EditFileSettingsMenuItem({
4815
4717
  name: 'edit_file_settings',
4816
4718
  label: I18n$1.t('pageflow.editor.views.inputs.file_input.edit_file_settings')
@@ -4818,18 +4720,15 @@ var FileInputView = Marionette.ItemView.extend({
4818
4720
  file: file
4819
4721
  }));
4820
4722
  }
4821
-
4822
4723
  return items;
4823
4724
  },
4824
4725
  _createCustomMenuItem: function _createCustomMenuItem(file, item) {
4825
4726
  var _this2 = this;
4826
-
4827
4727
  var options = {
4828
4728
  inputModel: this.model,
4829
4729
  propertyName: this.options.propertyName,
4830
4730
  file: file
4831
4731
  };
4832
-
4833
4732
  if (typeof item === 'function') {
4834
4733
  return new item({}, options);
4835
4734
  } else {
@@ -4851,11 +4750,9 @@ var FileInputView = Marionette.ItemView.extend({
4851
4750
  this.stopListening(this.textTrackFiles);
4852
4751
  this.textTrackFiles = null;
4853
4752
  }
4854
-
4855
4753
  if (file && this.options.defaultTextTrackFilePropertyName) {
4856
4754
  this.textTrackFiles = file.nestedFiles(this.options.textTrackFiles);
4857
4755
  this.listenTo(this.textTrackFiles, 'add remove', this._updateTextTrackMenuItems);
4858
-
4859
4756
  this._updateTextTrackMenuItems();
4860
4757
  }
4861
4758
  },
@@ -4879,11 +4776,15 @@ FileInputView.EditBackgroundPositioningMenuItem = Backbone.Model.extend({
4879
4776
  this.options = options;
4880
4777
  },
4881
4778
  selected: function selected() {
4882
- BackgroundPositioningView.open({
4779
+ var positioningOptions = this.options.positioningOptions;
4780
+ if (typeof positioningOptions === 'function') {
4781
+ positioningOptions = positioningOptions();
4782
+ }
4783
+ BackgroundPositioningView.open(_objectSpread2({
4883
4784
  model: this.options.inputModel,
4884
4785
  propertyName: this.options.propertyName,
4885
4786
  filesCollection: this.options.filesCollection
4886
- });
4787
+ }, positioningOptions));
4887
4788
  }
4888
4789
  });
4889
4790
  FileInputView.CustomMenuItem = Backbone.Model.extend({
@@ -4912,11 +4813,9 @@ FileInputView.DefaultTextTrackFileMenuItem = Backbone.Model.extend({
4912
4813
  initialize: function initialize(attributes, options) {
4913
4814
  this.options = options;
4914
4815
  this.listenTo(this.options.inputModel, 'change:' + this.options.propertyName, this.update);
4915
-
4916
4816
  if (this.options.textTrackFile) {
4917
4817
  this.listenTo(this.options.textTrackFile, 'change:configuration', this.update);
4918
4818
  }
4919
-
4920
4819
  this.update();
4921
4820
  },
4922
4821
  update: function update() {
@@ -4937,14 +4836,14 @@ FileInputView.DefaultTextTrackFileMenuItem = Backbone.Model.extend({
4937
4836
  }
4938
4837
  });
4939
4838
 
4940
- function template$e(data) {
4839
+ function template$d(data) {
4941
4840
  var __p = '';
4942
4841
  __p += '<div class="spinner">\n <div class="rect1"></div>\n <div class="rect2"></div>\n <div class="rect3"></div>\n <div class="rect4"></div>\n <div class="rect5"></div>\n</div>\n';
4943
4842
  return __p
4944
4843
  }
4945
4844
 
4946
4845
  var LoadingView = Marionette.ItemView.extend({
4947
- template: template$e,
4846
+ template: template$d,
4948
4847
  className: 'loading',
4949
4848
  tagName: 'li'
4950
4849
  });
@@ -4967,7 +4866,7 @@ var selectableView = {
4967
4866
  }
4968
4867
  };
4969
4868
 
4970
- function template$f(data) {
4869
+ function template$e(data) {
4971
4870
  var __t, __p = '';
4972
4871
  __p += '<span class="theme_name"></span>\n<span class="button_or_checkmark">\n <p class="theme_in_use"></p>\n <a class="use_theme">' +
4973
4872
  ((__t = ( I18n.t('pageflow.editor.templates.theme.use') )) == null ? '' : __t) +
@@ -4977,7 +4876,7 @@ return __p
4977
4876
 
4978
4877
  var ThemeItemView = Marionette.ItemView.extend({
4979
4878
  tagName: 'li',
4980
- template: template$f,
4879
+ template: template$e,
4981
4880
  className: 'theme_item',
4982
4881
  mixins: [selectableView],
4983
4882
  selectionAttribute: 'theme',
@@ -4996,11 +4895,9 @@ var ThemeItemView = Marionette.ItemView.extend({
4996
4895
  onRender: function onRender() {
4997
4896
  this.$el.data('themeName', this.model.get('name'));
4998
4897
  this.ui.themeName.text(this.model.title());
4999
-
5000
4898
  if (this.inUse()) {
5001
4899
  this.ui.inUseRegion.text('✓');
5002
4900
  }
5003
-
5004
4901
  this.ui.useButton.toggle(!this.inUse());
5005
4902
  },
5006
4903
  inUse: function inUse() {
@@ -5008,7 +4905,7 @@ var ThemeItemView = Marionette.ItemView.extend({
5008
4905
  }
5009
4906
  });
5010
4907
 
5011
- function template$g(data) {
4908
+ function template$f(data) {
5012
4909
  var __t, __p = '';
5013
4910
  __p += '<div class="box">\n <h1 class="dialog-header">\n ' +
5014
4911
  ((__t = ( I18n.t('pageflow.editor.templates.change_theme_dialog.header') )) == null ? '' : __t) +
@@ -5023,7 +4920,7 @@ return __p
5023
4920
  }
5024
4921
 
5025
4922
  var ChangeThemeDialogView = Marionette.ItemView.extend({
5026
- template: template$g,
4923
+ template: template$f,
5027
4924
  className: 'change_theme dialog editor',
5028
4925
  mixins: [dialogView],
5029
4926
  ui: {
@@ -5041,7 +4938,6 @@ var ChangeThemeDialogView = Marionette.ItemView.extend({
5041
4938
  if (!this.selection.get('theme')) {
5042
4939
  this.selection.set('theme', themeInUse);
5043
4940
  }
5044
-
5045
4941
  this.update();
5046
4942
  });
5047
4943
  },
@@ -5075,14 +4971,12 @@ var ChangeThemeDialogView = Marionette.ItemView.extend({
5075
4971
  this.ui.previewHeaderThemeName.text(selectedTheme.title());
5076
4972
  }
5077
4973
  });
5078
-
5079
4974
  ChangeThemeDialogView.changeTheme = function (options) {
5080
4975
  return $.Deferred(function (deferred) {
5081
4976
  options.onUse = function (theme) {
5082
4977
  deferred.resolve(theme);
5083
4978
  view.close();
5084
4979
  };
5085
-
5086
4980
  var view = new ChangeThemeDialogView(options);
5087
4981
  view.on('close', function () {
5088
4982
  deferred.reject();
@@ -5091,14 +4985,14 @@ ChangeThemeDialogView.changeTheme = function (options) {
5091
4985
  }).promise();
5092
4986
  };
5093
4987
 
5094
- function template$h(data) {
4988
+ function template$g(data) {
5095
4989
  var __p = '';
5096
4990
  __p += '\n';
5097
4991
  return __p
5098
4992
  }
5099
4993
 
5100
4994
  var StaticThumbnailView = Marionette.ItemView.extend({
5101
- template: template$h,
4995
+ template: template$g,
5102
4996
  className: 'static_thumbnail',
5103
4997
  modelEvents: {
5104
4998
  'change:configuration': 'update'
@@ -5119,7 +5013,6 @@ var StaticThumbnailView = Marionette.ItemView.extend({
5119
5013
  *
5120
5014
  * @class
5121
5015
  */
5122
-
5123
5016
  var ModelThumbnailView = Marionette.View.extend({
5124
5017
  className: 'model_thumbnail',
5125
5018
  modelEvents: {
@@ -5133,11 +5026,9 @@ var ModelThumbnailView = Marionette.View.extend({
5133
5026
  if (this.model) {
5134
5027
  if (_.isFunction(this.model.thumbnailFile)) {
5135
5028
  var file = this.model && this.model.thumbnailFile();
5136
-
5137
5029
  if (this.thumbnailView && this.currentFileThumbnail == file) {
5138
5030
  return;
5139
5031
  }
5140
-
5141
5032
  this.currentFileThumbnail = file;
5142
5033
  this.newThumbnailView = new FileThumbnailView({
5143
5034
  model: file,
@@ -5150,11 +5041,9 @@ var ModelThumbnailView = Marionette.View.extend({
5150
5041
  });
5151
5042
  }
5152
5043
  }
5153
-
5154
5044
  if (this.thumbnailView) {
5155
5045
  this.thumbnailView.close();
5156
5046
  }
5157
-
5158
5047
  if (this.model) {
5159
5048
  this.thumbnailView = this.subview(this.newThumbnailView);
5160
5049
  this.$el.append(this.thumbnailView.el);
@@ -5162,7 +5051,7 @@ var ModelThumbnailView = Marionette.View.extend({
5162
5051
  }
5163
5052
  });
5164
5053
 
5165
- function template$i(data) {
5054
+ function template$h(data) {
5166
5055
  var __p = '';
5167
5056
  __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<div class="title"></div>\n<button class="unset"></button>\n<button class="choose"></button>\n';
5168
5057
  return __p
@@ -5173,12 +5062,9 @@ return __p
5173
5062
  *
5174
5063
  * @class
5175
5064
  */
5176
-
5177
- var ReferenceInputView = Marionette.ItemView.extend(
5178
- /** @lends ReferenceInputView.prototype */
5179
- {
5065
+ var ReferenceInputView = Marionette.ItemView.extend( /** @lends ReferenceInputView.prototype */{
5180
5066
  mixins: [inputView],
5181
- template: template$i,
5067
+ template: template$h,
5182
5068
  className: 'reference_input',
5183
5069
  ui: {
5184
5070
  title: '.title',
@@ -5206,7 +5092,6 @@ var ReferenceInputView = Marionette.ItemView.extend(
5206
5092
  this.update();
5207
5093
  this.listenTo(this.model, 'change:' + this.options.propertyName, this.update);
5208
5094
  },
5209
-
5210
5095
  /**
5211
5096
  * Returns a promise for some identifying attribute.
5212
5097
  *
@@ -5236,18 +5121,15 @@ var ReferenceInputView = Marionette.ItemView.extend(
5236
5121
  if (this.isClosed) {
5237
5122
  return;
5238
5123
  }
5239
-
5240
5124
  var target = this.getTarget(this.model.get(this.options.propertyName));
5241
5125
  this.ui.title.text(target ? target.title() : I18n$1.t('pageflow.editor.views.inputs.reference_input_view.none'));
5242
5126
  this.ui.unsetButton.toggle(!!target && !this.options.hideUnsetButton);
5243
5127
  this.ui.unsetButton.attr('title', this.options.unsetButtonTitle || I18n$1.t('pageflow.editor.views.inputs.reference_input_view.unset'));
5244
5128
  this.ui.chooseButton.attr('title', this.options.chooseButtonTitle || I18n$1.t('pageflow.editor.views.inputs.reference_input_view.choose'));
5245
5129
  this.updateDisabledAttribute(this.ui.buttons);
5246
-
5247
5130
  if (this.thumbnailView) {
5248
5131
  this.thumbnailView.close();
5249
5132
  }
5250
-
5251
5133
  this.thumbnailView = this.subview(this.createThumbnailView(target));
5252
5134
  this.ui.title.before(this.thumbnailView.el);
5253
5135
  }
@@ -5277,7 +5159,7 @@ var ThemeInputView = ReferenceInputView.extend({
5277
5159
  }
5278
5160
  });
5279
5161
 
5280
- function template$j(data) {
5162
+ function template$i(data) {
5281
5163
  var __t, __p = '';
5282
5164
  __p += '<a class="back">' +
5283
5165
  ((__t = ( I18n.t('pageflow.editor.templates.edit_meta_data.outline') )) == null ? '' : __t) +
@@ -5290,7 +5172,7 @@ return __p
5290
5172
  }
5291
5173
 
5292
5174
  var EditMetaDataView = Marionette.Layout.extend({
5293
- template: template$j,
5175
+ template: template$i,
5294
5176
  className: 'edit_meta_data',
5295
5177
  mixins: [failureIndicatingView],
5296
5178
  regions: {
@@ -5340,15 +5222,20 @@ var EditMetaDataView = Marionette.Layout.extend({
5340
5222
  });
5341
5223
  });
5342
5224
  configurationEditor.tab('widgets', function () {
5225
+ var _this = this;
5343
5226
  editor.entryType.appearanceInputs && editor.entryType.appearanceInputs(this, {
5344
5227
  entry: entry,
5345
5228
  site: state.site
5346
5229
  });
5230
+ editor.appearanceInputsCallbacks.forEach(function (callback) {
5231
+ callback(_this, {
5232
+ entry: entry
5233
+ });
5234
+ });
5347
5235
  entry.widgets && this.view(EditWidgetsView, {
5348
5236
  model: entry,
5349
5237
  widgetTypes: editor.widgetTypes
5350
5238
  });
5351
-
5352
5239
  if (features.isEnabled && features.isEnabled('selectable_themes') && state.themes.length > 1) {
5353
5240
  this.view(ThemeInputView, {
5354
5241
  themes: state.themes,
@@ -5390,7 +5277,7 @@ var EditMetaDataView = Marionette.Layout.extend({
5390
5277
  }
5391
5278
  });
5392
5279
 
5393
- function template$k(data) {
5280
+ function template$j(data) {
5394
5281
  var __t, __p = '';
5395
5282
  __p += '<a class="back">' +
5396
5283
  ((__t = ( I18n.t('pageflow.editor.templates.edit_widget.back') )) == null ? '' : __t) +
@@ -5399,7 +5286,7 @@ return __p
5399
5286
  }
5400
5287
 
5401
5288
  var EditWidgetView = Marionette.ItemView.extend({
5402
- template: template$k,
5289
+ template: template$j,
5403
5290
  className: 'edit_widget',
5404
5291
  events: {
5405
5292
  'click a.back': function clickABack() {
@@ -5418,6 +5305,7 @@ var EditWidgetView = Marionette.ItemView.extend({
5418
5305
  onRender: function onRender() {
5419
5306
  var configurationEditor = this.model.widgetType().createConfigurationEditorView({
5420
5307
  model: this.model.configuration,
5308
+ entry: this.options.entry,
5421
5309
  tab: this.options.tab
5422
5310
  });
5423
5311
  this.appendSubview(configurationEditor);
@@ -5431,7 +5319,7 @@ var loadable = modelLifecycleTrackingView({
5431
5319
  }
5432
5320
  });
5433
5321
 
5434
- function template$l(data) {
5322
+ function template$k(data) {
5435
5323
  var __p = '';
5436
5324
  __p += '<span class="file_thumbnail"></span>\n\n<span class="file_name"></span>\n';
5437
5325
  return __p
@@ -5439,7 +5327,7 @@ return __p
5439
5327
 
5440
5328
  var ExplorerFileItemView = Marionette.ItemView.extend({
5441
5329
  tagName: 'li',
5442
- template: template$l,
5330
+ template: template$k,
5443
5331
  mixins: [loadable, selectableView],
5444
5332
  selectionAttribute: 'file',
5445
5333
  ui: {
@@ -5467,23 +5355,22 @@ var ExplorerFileItemView = Marionette.ItemView.extend({
5467
5355
  if (this.isDisabled()) {
5468
5356
  this.$el.addClass('disabled');
5469
5357
  }
5470
-
5471
5358
  this.$el.attr('data-id', this.model.id);
5472
- this.ui.fileName.text(this.model.get('file_name') || '(Unbekannt)');
5359
+ this.ui.fileName.text(this.model.title());
5473
5360
  },
5474
5361
  isDisabled: function isDisabled() {
5475
5362
  return this.options.disabledIds && _.contains(this.options.disabledIds, this.model.get('id'));
5476
5363
  }
5477
5364
  });
5478
5365
 
5479
- function template$m(data) {
5366
+ function template$l(data) {
5480
5367
  var __p = '';
5481
5368
  __p += '<a href="">\n <span class="title"></span>\n</a>\n';
5482
5369
  return __p
5483
5370
  }
5484
5371
 
5485
5372
  var OtherEntryItemView = Marionette.ItemView.extend({
5486
- template: template$m,
5373
+ template: template$l,
5487
5374
  className: 'other_entry_item',
5488
5375
  tagName: 'li',
5489
5376
  mixins: [selectableView],
@@ -5498,7 +5385,7 @@ var OtherEntryItemView = Marionette.ItemView.extend({
5498
5385
  }
5499
5386
  });
5500
5387
 
5501
- function template$n(data) {
5388
+ function template$m(data) {
5502
5389
  var __t, __p = '';
5503
5390
  __p +=
5504
5391
  ((__t = ( I18n.t('pageflow.editor.templates.other_entries_blank_slate.none_available') )) == null ? '' : __t) +
@@ -5524,7 +5411,7 @@ var OtherEntriesCollectionView = Marionette.View.extend({
5524
5411
  selection: this.options.selection
5525
5412
  },
5526
5413
  blankSlateViewConstructor: Marionette.ItemView.extend({
5527
- template: template$n,
5414
+ template: template$m,
5528
5415
  tagName: 'li',
5529
5416
  className: 'blank_slate'
5530
5417
  }),
@@ -5535,7 +5422,7 @@ var OtherEntriesCollectionView = Marionette.View.extend({
5535
5422
  }
5536
5423
  });
5537
5424
 
5538
- function template$o(data) {
5425
+ function template$n(data) {
5539
5426
  var __t, __p = '';
5540
5427
  __p += '<div class="box">\n <h1 class="dialog-header">\n ' +
5541
5428
  ((__t = ( I18n.t('pageflow.editor.templates.files_explorer.reuse_files') )) == null ? '' : __t) +
@@ -5564,7 +5451,7 @@ return __p
5564
5451
  }
5565
5452
 
5566
5453
  var FilesExplorerView = Marionette.ItemView.extend({
5567
- template: template$o,
5454
+ template: template$n,
5568
5455
  className: 'files_explorer editor dialog',
5569
5456
  mixins: [dialogView],
5570
5457
  ui: {
@@ -5577,7 +5464,6 @@ var FilesExplorerView = Marionette.ItemView.extend({
5577
5464
  if (this.options.callback) {
5578
5465
  this.options.callback(this.selection.get('entry'), this.selection.get('file'));
5579
5466
  }
5580
-
5581
5467
  this.close();
5582
5468
  }
5583
5469
  },
@@ -5585,8 +5471,9 @@ var FilesExplorerView = Marionette.ItemView.extend({
5585
5471
  this.selection = new Backbone.Model();
5586
5472
  this.listenTo(this.selection, 'change:entry', function () {
5587
5473
  this.tabsView.refresh();
5588
- }); // check if the OK button should be enabled.
5474
+ });
5589
5475
 
5476
+ // check if the OK button should be enabled.
5590
5477
  this.listenTo(this.selection, 'change', function (selection, options) {
5591
5478
  this.ui.okButton.prop('disabled', !this.selection.get('file'));
5592
5479
  });
@@ -5612,7 +5499,6 @@ var FilesExplorerView = Marionette.ItemView.extend({
5612
5499
  tab: function tab(fileType) {
5613
5500
  this.tabsView.tab(fileType.collectionName, _.bind(function () {
5614
5501
  var collection = this._collection(fileType);
5615
-
5616
5502
  var disabledIds = state.entry.getFileCollection(fileType).pluck('id');
5617
5503
  return new CollectionView({
5618
5504
  tagName: 'ul',
@@ -5629,15 +5515,13 @@ var FilesExplorerView = Marionette.ItemView.extend({
5629
5515
  },
5630
5516
  _collection: function _collection(fileType) {
5631
5517
  var collection,
5632
- entry = this.selection.get('entry');
5633
-
5518
+ entry = this.selection.get('entry');
5634
5519
  if (entry) {
5635
5520
  collection = entry.getFileCollection(fileType);
5636
5521
  collection.fetch();
5637
5522
  } else {
5638
5523
  collection = new Backbone.Collection();
5639
5524
  }
5640
-
5641
5525
  return collection;
5642
5526
  },
5643
5527
  _blankSlateConstructor: function _blankSlateConstructor() {
@@ -5646,12 +5530,11 @@ var FilesExplorerView = Marionette.ItemView.extend({
5646
5530
  });
5647
5531
  }
5648
5532
  });
5649
-
5650
5533
  FilesExplorerView.open = function (options) {
5651
5534
  app.dialogRegion.show(new FilesExplorerView(options));
5652
5535
  };
5653
5536
 
5654
- function template$p(data) {
5537
+ function template$o(data) {
5655
5538
  var __p = '';
5656
5539
  __p += '<th></th>\n<td></td>';
5657
5540
  return __p
@@ -5659,7 +5542,7 @@ return __p
5659
5542
 
5660
5543
  var FileMetaDataItemView = Marionette.ItemView.extend({
5661
5544
  tagName: 'tr',
5662
- template: template$p,
5545
+ template: template$o,
5663
5546
  ui: {
5664
5547
  label: 'th',
5665
5548
  value: 'td'
@@ -5682,7 +5565,7 @@ var FileMetaDataItemView = Marionette.ItemView.extend({
5682
5565
  }
5683
5566
  });
5684
5567
 
5685
- function template$q(data) {
5568
+ function template$p(data) {
5686
5569
  var __p = '';
5687
5570
  __p += '<p class="percent"></p>\n<p class="description"></p>\n<p class="error_message"></p>';
5688
5571
  return __p
@@ -5691,7 +5574,7 @@ return __p
5691
5574
  var FileStageItemView = Marionette.ItemView.extend({
5692
5575
  tagName: 'li',
5693
5576
  className: 'file_stage_item',
5694
- template: template$q,
5577
+ template: template$p,
5695
5578
  ui: {
5696
5579
  description: '.description',
5697
5580
  percent: '.percent',
@@ -5703,7 +5586,6 @@ var FileStageItemView = Marionette.ItemView.extend({
5703
5586
  onRender: function onRender() {
5704
5587
  this.update();
5705
5588
  this.$el.addClass(this.model.get('name'));
5706
-
5707
5589
  if (this.options.standAlone) {
5708
5590
  this.$el.addClass('stand_alone');
5709
5591
  } else {
@@ -5712,13 +5594,11 @@ var FileStageItemView = Marionette.ItemView.extend({
5712
5594
  },
5713
5595
  update: function update() {
5714
5596
  this.ui.description.text(this.model.localizedDescription());
5715
-
5716
5597
  if (typeof this.model.get('progress') === 'number' && this.model.get('active')) {
5717
5598
  this.ui.percent.text(this.model.get('progress') + '%');
5718
5599
  } else {
5719
5600
  this.ui.percent.text('');
5720
5601
  }
5721
-
5722
5602
  this.ui.errorMessage.toggle(!!this.model.get('error_message')).text(this._translatedErrorMessage());
5723
5603
  this.$el.toggleClass('active', this.model.get('active'));
5724
5604
  this.$el.toggleClass('finished', this.model.get('finished'));
@@ -5732,21 +5612,96 @@ var FileStageItemView = Marionette.ItemView.extend({
5732
5612
  }
5733
5613
  });
5734
5614
 
5615
+ function template$q(data) {
5616
+ var __t, __p = '';
5617
+ __p += '<span class="value"></span>\n<button class="edit" type="button" title="' +
5618
+ ((__t = ( I18n.t('pageflow.editor.templates.file_meta_data_item_value_view.edit') )) == null ? '' : __t) +
5619
+ '">\n</button>\n\n';
5620
+ return __p
5621
+ }
5622
+
5623
+ /**
5624
+ * Base class for views used as `valueView` for file type meta data
5625
+ * attributes.
5626
+ *
5627
+ * @param {Object} [options]
5628
+ *
5629
+ * @param {string} [options.name]
5630
+ * Name of the meta data item used in translation keys.
5631
+ *
5632
+ * @param {string} [options.settingsDialogTabLink]
5633
+ * Dispaly a link to open the specified tab of the file settings
5634
+ * dialog.
5635
+ *
5636
+ * @since 12.0
5637
+ *
5638
+ * @class
5639
+ */
5640
+ var FileMetaDataItemValueView = Marionette.ItemView.extend({
5641
+ template: template$q,
5642
+ className: 'value_wrapper',
5643
+ ui: {
5644
+ value: '.value',
5645
+ editLink: '.edit'
5646
+ },
5647
+ events: {
5648
+ 'click .edit': function clickEdit() {
5649
+ FileSettingsDialogView.open({
5650
+ model: this.model,
5651
+ tabName: this.options.settingsDialogTabLink
5652
+ });
5653
+ }
5654
+ },
5655
+ modelEvents: {
5656
+ 'change': 'toggleEditLink'
5657
+ },
5658
+ onRender: function onRender() {
5659
+ this.listenTo(this.model, 'change:' + this.options.name, this.update);
5660
+ this.toggleEditLink();
5661
+ this.update();
5662
+ },
5663
+ update: function update() {
5664
+ this.ui.value.text(this.getText() || I18n$1.t('pageflow.editor.views.file_meta_data_item_value_view.blank'));
5665
+ },
5666
+ getText: function getText() {
5667
+ throw new Error('Not implemented');
5668
+ },
5669
+ toggleEditLink: function toggleEditLink() {
5670
+ this.ui.editLink.toggle(!!this.options.settingsDialogTabLink && !this.model.isNew());
5671
+ }
5672
+ });
5673
+
5674
+ var TextFileMetaDataItemValueView = FileMetaDataItemValueView.extend({
5675
+ getText: function getText() {
5676
+ var model;
5677
+ if (this.options.fromConfiguration) {
5678
+ model = this.model.configuration;
5679
+ } else {
5680
+ model = this.model;
5681
+ }
5682
+ var value = model.get(this.options.name);
5683
+ if (value && this.options.formatValue) {
5684
+ return this.options.formatValue(value);
5685
+ }
5686
+ return value;
5687
+ }
5688
+ });
5689
+
5735
5690
  function template$r(data) {
5736
5691
  var __t, __p = '';
5737
- __p += '<span class="file_thumbnail"></span>\n\n<span class="file_name"></span>\n<a class="select">' +
5692
+ __p += '<button class="file_thumbnail_button" type="button">\n <span class="file_thumbnail"></span>\n</button>\n\n<span class="file_name"></span>\n<button class="select" type="button">' +
5738
5693
  ((__t = ( I18n.t('pageflow.editor.templates.file_item.select') )) == null ? '' : __t) +
5739
- '</a>\n\n<div class="actions">\n <a class="settings" title="' +
5694
+ '</button>\n\n<div class="actions">\n <button class="settings" type="button" title="' +
5740
5695
  ((__t = ( I18n.t('pageflow.editor.templates.file_item.settings') )) == null ? '' : __t) +
5741
- '"></a>\n <a class="confirm" title="' +
5696
+ '"></button>\n <button class="confirm" type="button" title="' +
5742
5697
  ((__t = ( I18n.t('pageflow.editor.templates.file_item.confirm') )) == null ? '' : __t) +
5743
- '"></a>\n <a class="retry" title="' +
5698
+ '"></button>\n <button class="retry" type="button" title="' +
5744
5699
  ((__t = ( I18n.t('pageflow.editor.templates.file_item.retry') )) == null ? '' : __t) +
5745
- '"></a>\n <a class="remove" title="' +
5700
+ '"></button>\n <button class="remove" type="button" title="' +
5746
5701
  ((__t = ( I18n.t('pageflow.editor.templates.file_item.destroy') )) == null ? '' : __t) +
5747
- '"></a>\n <a class="cancel" title="' +
5702
+ '"></button>\n <button class="cancel" type="button" title="' +
5748
5703
  ((__t = ( I18n.t('pageflow.editor.templates.file_item.cancel') )) == null ? '' : __t) +
5749
- '"></a>\n</div>\n\n<div class="details">\n <ul class="file_stage_items"></ul>\n\n <div class="file_meta_data">\n <table cellpadding="0" cellspacing="0">\n <tbody class="attributes">\n </tbody>\n <tbody class="downloads">\n <tr>\n <th>' +
5704
+ '"></button>\n</div>\n\n<div class="details">\n <ul class="file_stage_items"></ul>\n\n <div class="file_meta_data">\n <table cellpadding="0" cellspacing="0">\n <tbody class="attributes">\n </tbody>\n <tbody class="downloads">\n <tr>\n <th>' +
5750
5705
  ((__t = ( I18n.t('pageflow.editor.templates.file_item.source') )) == null ? '' : __t) +
5751
5706
  '</th>\n <td><a class="original" href="#" download target="_blank">' +
5752
5707
  ((__t = ( I18n.t('pageflow.editor.templates.file_item.download') )) == null ? '' : __t) +
@@ -5767,23 +5722,15 @@ var FileItemView = Marionette.ItemView.extend({
5767
5722
  cancelButton: '.cancel',
5768
5723
  retryButton: '.retry',
5769
5724
  thumbnail: '.file_thumbnail',
5725
+ thumbnailButton: '.file_thumbnail_button',
5770
5726
  stageItems: '.file_stage_items',
5727
+ details: '.details',
5771
5728
  metaData: 'tbody.attributes',
5772
5729
  downloads: 'tbody.downloads',
5773
5730
  downloadLink: 'a.original'
5774
5731
  },
5775
5732
  events: {
5776
- 'click .select': function clickSelect() {
5777
- var result = this.options.selectionHandler.call(this.model);
5778
-
5779
- if (result !== false) {
5780
- editor.navigate(this.options.selectionHandler.getReferer(), {
5781
- trigger: true
5782
- });
5783
- }
5784
-
5785
- return false;
5786
- },
5733
+ 'click .select': 'select',
5787
5734
  'click .settings': function clickSettings() {
5788
5735
  FileSettingsDialogView.open({
5789
5736
  model: this.model
@@ -5793,13 +5740,28 @@ var FileItemView = Marionette.ItemView.extend({
5793
5740
  'click .confirm': 'confirm',
5794
5741
  'click .remove': 'destroy',
5795
5742
  'click .retry': 'retry',
5796
- 'click .file_thumbnail': 'toggleExpanded'
5743
+ 'click .file_thumbnail_button': 'toggleExpanded'
5744
+ },
5745
+ initialize: function initialize() {
5746
+ var _this = this;
5747
+ if (this.options.listHighlight) {
5748
+ this.listenTo(this.options.listHighlight, 'change:currentId change:active', function () {
5749
+ if (_this.updateHighlight()) {
5750
+ _this.el.scrollIntoView({
5751
+ block: 'nearest',
5752
+ behavior: 'smooth'
5753
+ });
5754
+ }
5755
+ });
5756
+ this.listenTo(this.options.listHighlight, 'selected:' + this.model.id, this.select);
5757
+ }
5797
5758
  },
5798
5759
  modelEvents: {
5799
5760
  'change': 'update'
5800
5761
  },
5801
5762
  onRender: function onRender() {
5802
5763
  this.update();
5764
+ this.setupAriaAttributes();
5803
5765
  this.subview(new FileThumbnailView({
5804
5766
  el: this.ui.thumbnail,
5805
5767
  model: this.model
@@ -5809,20 +5771,19 @@ var FileItemView = Marionette.ItemView.extend({
5809
5771
  collection: this.model.stages,
5810
5772
  itemViewConstructor: FileStageItemView
5811
5773
  }));
5812
-
5813
5774
  _.each(this.metaDataViews(), function (view) {
5814
5775
  this.ui.metaData.append(this.subview(view).el);
5815
5776
  }, this);
5777
+ this.updateHighlight();
5816
5778
  },
5817
5779
  update: function update() {
5818
5780
  if (this.isClosed) {
5819
5781
  return;
5820
5782
  }
5821
-
5822
5783
  this.$el.attr('data-id', this.model.id);
5823
- this.ui.fileName.text(this.model.get('file_name') || '(Unbekannt)');
5824
- this.ui.downloadLink.attr('href', this.model.get('original_url'));
5825
- this.ui.downloads.toggle(this.model.isUploaded() && !_.isEmpty(this.model.get('original_url')));
5784
+ this.ui.fileName.text(this.model.title());
5785
+ this.ui.downloadLink.attr('href', this.model.get('download_url'));
5786
+ this.ui.downloads.toggle(this.model.isUploaded() && !_.isEmpty(this.model.get('download_url')));
5826
5787
  this.ui.selectButton.toggle(!!this.options.selectionHandler);
5827
5788
  this.ui.settingsButton.toggle(!this.model.isNew());
5828
5789
  this.ui.cancelButton.toggle(this.model.isUploading());
@@ -5840,7 +5801,6 @@ var FileItemView = Marionette.ItemView.extend({
5840
5801
  valueView: TextFileMetaDataItemValueView
5841
5802
  };
5842
5803
  }
5843
-
5844
5804
  return new FileMetaDataItemView(_.extend({
5845
5805
  model: model
5846
5806
  }, options));
@@ -5850,34 +5810,166 @@ var FileItemView = Marionette.ItemView.extend({
5850
5810
  this.$el.toggleClass('expanded');
5851
5811
  this.updateToggleTitle();
5852
5812
  },
5813
+ setupAriaAttributes: function setupAriaAttributes() {
5814
+ var uniqueId = this.model.get('id') || this.model.cid;
5815
+ var detailsId = 'file-details-' + uniqueId;
5816
+ this.ui.thumbnailButton.attr('aria-controls', detailsId);
5817
+ this.ui.details.attr('id', detailsId);
5818
+ },
5853
5819
  updateToggleTitle: function updateToggleTitle() {
5854
- this.ui.thumbnail.attr('title', this.$el.hasClass('expanded') ? 'Details ausblenden' : 'Details einblenden');
5820
+ var isExpanded = this.$el.hasClass('expanded');
5821
+ var titleText = I18n$1.t(isExpanded ? 'pageflow.editor.templates.file_item.collapse_details' : 'pageflow.editor.templates.file_item.expand_details');
5822
+ this.ui.thumbnailButton.attr('aria-expanded', isExpanded.toString());
5823
+ this.ui.thumbnailButton.attr('title', titleText);
5824
+ this.ui.thumbnailButton.attr('aria-label', titleText);
5855
5825
  },
5856
5826
  destroy: function destroy() {
5857
5827
  if (confirm("Datei wirklich wirklich löschen?")) {
5858
5828
  this.model.destroy();
5859
5829
  }
5860
5830
  },
5861
- cancel: function cancel() {
5862
- this.model.cancelUpload();
5831
+ cancel: function cancel() {
5832
+ this.model.cancelUpload();
5833
+ },
5834
+ confirm: function confirm() {
5835
+ editor.navigate('/confirmable_files?type=' + this.model.modelName + '&id=' + this.model.id, {
5836
+ trigger: true
5837
+ });
5838
+ },
5839
+ retry: function retry() {
5840
+ this.model.retry();
5841
+ },
5842
+ select: function select() {
5843
+ var result = this.options.selectionHandler.call(this.model);
5844
+ if (result !== false) {
5845
+ editor.navigate(this.options.selectionHandler.getReferer(), {
5846
+ trigger: true
5847
+ });
5848
+ }
5849
+ return false;
5850
+ },
5851
+ updateHighlight: function updateHighlight() {
5852
+ if (!this.options.listHighlight) {
5853
+ return false;
5854
+ }
5855
+ var highlighted = this.options.listHighlight.get('currentId') === this.model.id && this.options.listHighlight.get('active');
5856
+ this.$el.toggleClass('keyboard_highlight', highlighted);
5857
+ this.$el.attr('aria-selected', highlighted ? 'true' : null);
5858
+ return highlighted;
5859
+ }
5860
+ });
5861
+
5862
+ function template$s(data) {
5863
+ var __t, __p = '';
5864
+ __p += '<input type="text"\n class="list_search_field-term"\n aria-label="' +
5865
+ ((__t = ( I18n.t('pageflow.editor.templates.list_search_field.placeholder') )) == null ? '' : __t) +
5866
+ '" />\n<span class="list_search_field-placeholder">\n ' +
5867
+ ((__t = ( I18n.t('pageflow.editor.templates.list_search_field.hint',
5868
+ {hotkey: '<kbd>/</kbd>'}) )) == null ? '' : __t) +
5869
+ '\n</span>\n<a href=""\n class="list_search_field-reset"\n title="' +
5870
+ ((__t = ( I18n.t('pageflow.editor.templates.list_search_field.reset') )) == null ? '' : __t) +
5871
+ '"></a>\n';
5872
+ return __p
5873
+ }
5874
+
5875
+ var ListSearchFieldView = Marionette.ItemView.extend({
5876
+ template: template$s,
5877
+ className: 'list_search_field',
5878
+ ui: {
5879
+ input: '.list_search_field-term'
5880
+ },
5881
+ events: {
5882
+ 'input .list_search_field-term': 'changeTerm',
5883
+ 'click .list_search_field-reset': 'reset',
5884
+ 'keydown .list_search_field-term': 'handleInputKeyDown',
5885
+ 'focus .list_search_field-term': 'handleInputFocus',
5886
+ 'blur .list_search_field-term': 'handleInputBlur'
5887
+ },
5888
+ initialize: function initialize() {
5889
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
5890
+ this.search = options.search;
5891
+ this.listHighlight = options.listHighlight;
5892
+ },
5893
+ onRender: function onRender() {
5894
+ var _this = this;
5895
+ this.toggleReset();
5896
+ if (this.options.ariaControlsId) {
5897
+ this.ui.input.attr('aria-controls', this.options.ariaControlsId);
5898
+ }
5899
+ this.handleDocumentKeyDown = this.handleDocumentKeyDown.bind(this);
5900
+ $(document).on('keydown', this.handleDocumentKeyDown);
5901
+ if (this.options.autoFocus) {
5902
+ setTimeout(function () {
5903
+ return _this.ui.input.focus();
5904
+ }, 0);
5905
+ }
5906
+ },
5907
+ onClose: function onClose() {
5908
+ $(document).off('keydown', this.handleDocumentKeyDown);
5909
+ },
5910
+ changeTerm: function changeTerm() {
5911
+ this.search.set('term', this.ui.input.val());
5912
+ this.toggleReset();
5913
+ },
5914
+ reset: function reset(event) {
5915
+ this.ui.input.val('');
5916
+ this.search.set('term', '');
5917
+ this.toggleReset();
5918
+ this.ui.input.focus();
5919
+ if (event) {
5920
+ event.preventDefault();
5921
+ }
5922
+ },
5923
+ handleDocumentKeyDown: function handleDocumentKeyDown(event) {
5924
+ var active = document.activeElement;
5925
+ if (event.key === '/' && !/input|textarea/i.test(active.tagName)) {
5926
+ this.ui.input.focus();
5927
+ event.preventDefault();
5928
+ }
5929
+ },
5930
+ handleInputKeyDown: function handleInputKeyDown(event) {
5931
+ if (event.key === 'Escape') {
5932
+ if (this.search.get('term')) {
5933
+ this.reset();
5934
+ } else {
5935
+ this.ui.input.blur();
5936
+ }
5937
+ } else if (this.listHighlight) {
5938
+ if (event.key === 'ArrowDown') {
5939
+ this.listHighlight.next();
5940
+ event.preventDefault();
5941
+ } else if (event.key === 'ArrowUp') {
5942
+ this.listHighlight.previous();
5943
+ event.preventDefault();
5944
+ } else if (event.key === 'Enter') {
5945
+ this.listHighlight.triggerSelect();
5946
+ }
5947
+ }
5948
+ },
5949
+ handleInputFocus: function handleInputFocus() {
5950
+ this.$el.addClass('focus');
5951
+ if (this.listHighlight) {
5952
+ this.listHighlight.set('active', true);
5953
+ }
5863
5954
  },
5864
- confirm: function confirm() {
5865
- editor.navigate('/confirmable_files?type=' + this.model.modelName + '&id=' + this.model.id, {
5866
- trigger: true
5867
- });
5955
+ handleInputBlur: function handleInputBlur() {
5956
+ this.$el.removeClass('focus');
5957
+ if (this.listHighlight) {
5958
+ this.listHighlight.set('active', false);
5959
+ }
5868
5960
  },
5869
- retry: function retry() {
5870
- this.model.retry();
5961
+ toggleReset: function toggleReset() {
5962
+ this.$el.toggleClass('has_value', !!this.search.get('term'));
5871
5963
  }
5872
5964
  });
5873
5965
 
5874
- function template$s(data) {
5966
+ function template$t(data) {
5875
5967
  var __t, __p = '';
5876
5968
  __p += '<div class="filtered_files-banner">\n <span class="filtered_files-banner_prefix">\n ' +
5877
5969
  ((__t = ( I18n.t('pageflow.editor.views.filtered_files_view.banner_prefix') )) == null ? '' : __t) +
5878
5970
  '\n </span>\n <span class="filtered_files-filter_name"></span>\n <a href=""\n class="filtered_files-reset_filter"\n title="' +
5879
5971
  ((__t = ( I18n.t('pageflow.editor.views.filtered_files_view.reset_filter') )) == null ? '' : __t) +
5880
- '">\n </a>\n</div>\n';
5972
+ '">\n </a>\n</div>\n<div class="filtered_files-filter_bar">\n</div>\n';
5881
5973
  return __p
5882
5974
  }
5883
5975
 
@@ -5890,11 +5982,13 @@ return __p
5890
5982
  }
5891
5983
 
5892
5984
  var FilteredFilesView = Marionette.ItemView.extend({
5893
- template: template$s,
5985
+ template: template$t,
5894
5986
  className: 'filtered_files',
5895
5987
  ui: {
5896
5988
  banner: '.filtered_files-banner',
5897
- filterName: '.filtered_files-filter_name'
5989
+ filterName: '.filtered_files-filter_name',
5990
+ filterBar: '.filtered_files-filter_bar',
5991
+ sort: '.filtered_files-sort'
5898
5992
  },
5899
5993
  events: {
5900
5994
  'click .filtered_files-reset_filter': function clickFiltered_filesReset_filter() {
@@ -5904,29 +5998,72 @@ var FilteredFilesView = Marionette.ItemView.extend({
5904
5998
  return false;
5905
5999
  }
5906
6000
  },
5907
- onRender: function onRender() {
5908
- var entry = this.options.entry;
5909
- var fileType = this.options.fileType;
5910
- var collection = entry.getFileCollection(fileType);
5911
- var blankSlateText = I18n$1.t('pageflow.editor.templates.files_blank_slate.no_files');
5912
-
6001
+ initialize: function initialize() {
6002
+ this.search = new Search({}, {
6003
+ attribute: 'display_name',
6004
+ storageKey: 'pageflow.filtered_files.sort_order'
6005
+ });
6006
+ var collection = this.options.entry.getFileCollection(this.options.fileType);
5913
6007
  if (this.options.filterName) {
5914
- if (this.filteredCollection) {
5915
- this.filteredCollection.dispose();
5916
- }
5917
-
5918
6008
  collection = this.filteredCollection = collection.withFilter(this.options.filterName);
5919
- blankSlateText = this.filterTranslation('blank_slate');
5920
6009
  }
5921
-
5922
- this.appendSubview(new CollectionView({
6010
+ this.searchFilteredCollection = this.search.applyTo(collection);
6011
+ if (this.options.selectionHandler) {
6012
+ this.listHighlight = new ListHighlight({}, {
6013
+ collection: this.searchFilteredCollection
6014
+ });
6015
+ }
6016
+ },
6017
+ onRender: function onRender() {
6018
+ this.renderNamedFilter();
6019
+ this.renderSearchField();
6020
+ this.renderSortMenu();
6021
+ this.renderCollectionView();
6022
+ },
6023
+ renderNamedFilter: function renderNamedFilter() {
6024
+ this.ui.banner.toggle(!!this.options.filterName);
6025
+ if (this.options.filterName) {
6026
+ this.ui.filterName.text(this.filterTranslation('name'));
6027
+ }
6028
+ },
6029
+ renderSearchField: function renderSearchField() {
6030
+ this.searchFieldView = this.appendSubview(new ListSearchFieldView({
6031
+ search: this.search,
6032
+ listHighlight: this.listHighlight,
6033
+ ariaControlsId: 'filtered_files',
6034
+ autoFocus: !!this.options.selectionHandler
6035
+ }), {
6036
+ to: this.ui.filterBar
6037
+ });
6038
+ },
6039
+ renderSortMenu: function renderSortMenu() {
6040
+ this.appendSubview(new DropDownButtonView({
6041
+ title: I18n$1.t('pageflow.editor.views.filtered_files_view.sort_button_label'),
6042
+ alignMenu: 'right',
6043
+ openOnClick: true,
6044
+ items: new SortMenuItemsCollection([{
6045
+ name: 'alphabetical'
6046
+ }, {
6047
+ name: 'most_recent'
6048
+ }], {
6049
+ search: this.search
6050
+ })
6051
+ }), {
6052
+ to: this.ui.filterBar
6053
+ });
6054
+ },
6055
+ renderCollectionView: function renderCollectionView() {
6056
+ var blankSlateText = this.options.filterName ? this.filterTranslation('blank_slate') : I18n$1.t('pageflow.editor.templates.files_blank_slate.no_files');
6057
+ this.appendSubview(this.subview(new CollectionView({
5923
6058
  tagName: 'ul',
6059
+ id: 'filtered_files',
5924
6060
  className: 'files expandable',
5925
- collection: collection,
6061
+ collection: this.searchFilteredCollection,
5926
6062
  itemViewConstructor: FileItemView,
5927
6063
  itemViewOptions: {
5928
- metaDataAttributes: fileType.metaDataAttributes,
5929
- selectionHandler: this.options.selectionHandler
6064
+ metaDataAttributes: this.options.fileType.metaDataAttributes,
6065
+ selectionHandler: this.options.selectionHandler,
6066
+ listHighlight: this.listHighlight
5930
6067
  },
5931
6068
  blankSlateViewConstructor: Marionette.ItemView.extend({
5932
6069
  template: blankSlateTemplate,
@@ -5936,12 +6073,7 @@ var FilteredFilesView = Marionette.ItemView.extend({
5936
6073
  };
5937
6074
  }
5938
6075
  })
5939
- }));
5940
- this.ui.banner.toggle(!!this.options.filterName);
5941
-
5942
- if (this.options.filterName) {
5943
- this.ui.filterName.text(this.filterTranslation('name'));
5944
- }
6076
+ })));
5945
6077
  },
5946
6078
  filterTranslation: function filterTranslation(keyName, options) {
5947
6079
  var filterName = this.options.filterName;
@@ -5949,13 +6081,33 @@ var FilteredFilesView = Marionette.ItemView.extend({
5949
6081
  return i18nUtils.findTranslation(['pageflow.entry_types.' + entryTypeName + '.editor.files.filters.' + this.options.fileType.collectionName + '.' + filterName + '.' + keyName, 'pageflow.entry_types.' + entryTypeName + '.editor.files.common_filters.' + keyName, 'pageflow.editor.files.filters.' + this.options.fileType.collectionName + '.' + filterName + '.' + keyName, 'pageflow.editor.files.common_filters.' + keyName], options);
5950
6082
  },
5951
6083
  onClose: function onClose() {
5952
- if (this.filteredCollection) {
5953
- this.filteredCollection.dispose();
5954
- }
6084
+ var _this$filteredCollect;
6085
+ Marionette.ItemView.prototype.onClose.call(this);
6086
+ (_this$filteredCollect = this.filteredCollection) === null || _this$filteredCollect === void 0 ? void 0 : _this$filteredCollect.dispose();
6087
+ this.searchFilteredCollection.dispose();
6088
+ }
6089
+ });
6090
+ var SortMenuItem = Backbone.Model.extend({
6091
+ initialize: function initialize(attributes, options) {
6092
+ var _this = this;
6093
+ this.search = options.search;
6094
+ this.set('label', I18n$1.t("pageflow.editor.views.filtered_files_view.sort.".concat(this.get('name'))));
6095
+ this.set('kind', 'radio');
6096
+ var updateChecked = function updateChecked() {
6097
+ _this.set('checked', _this.search.get('order') === _this.get('name'));
6098
+ };
6099
+ this.listenTo(this.search, 'change:order', updateChecked);
6100
+ updateChecked();
6101
+ },
6102
+ selected: function selected() {
6103
+ this.search.set('order', this.get('name'));
5955
6104
  }
5956
6105
  });
6106
+ var SortMenuItemsCollection = Backbone.Collection.extend({
6107
+ model: SortMenuItem
6108
+ });
5957
6109
 
5958
- function template$t(data) {
6110
+ function template$u(data) {
5959
6111
  var __t, __p = '';
5960
6112
  __p += '<div class="box choose_importer_box">\n <h1 class="dialog-header">' +
5961
6113
  ((__t = ( I18n.t('pageflow.editor.views.files_view.importer.heading') )) == null ? '' : __t) +
@@ -5965,7 +6117,7 @@ __p += '<div class="box choose_importer_box">\n <h1 class="dialog-header">' +
5965
6117
  return __p
5966
6118
  }
5967
6119
 
5968
- function template$u(data) {
6120
+ function template$v(data) {
5969
6121
  var __t, __p = '';
5970
6122
  __p += '<button class=\'importer\' data-key=\'' +
5971
6123
  ((__t = ( data.fileImporter.key )) == null ? '' : __t) +
@@ -5980,7 +6132,7 @@ return __p
5980
6132
  }
5981
6133
 
5982
6134
  var ImporterSelectView = Marionette.ItemView.extend({
5983
- template: template$u,
6135
+ template: template$v,
5984
6136
  className: 'importer_select',
5985
6137
  tagName: 'li',
5986
6138
  events: {
@@ -5997,7 +6149,7 @@ var ImporterSelectView = Marionette.ItemView.extend({
5997
6149
  });
5998
6150
 
5999
6151
  var ChooseImporterView = Marionette.ItemView.extend({
6000
- template: template$t,
6152
+ template: template$u,
6001
6153
  className: 'choose_importer editor dialog',
6002
6154
  mixins: [dialogView],
6003
6155
  ui: {
@@ -6013,7 +6165,6 @@ var ChooseImporterView = Marionette.ItemView.extend({
6013
6165
  if (this.options.callback) {
6014
6166
  this.options.callback(importer);
6015
6167
  }
6016
-
6017
6168
  this.close();
6018
6169
  },
6019
6170
  onRender: function onRender() {
@@ -6027,12 +6178,11 @@ var ChooseImporterView = Marionette.ItemView.extend({
6027
6178
  });
6028
6179
  }
6029
6180
  });
6030
-
6031
6181
  ChooseImporterView.open = function (options) {
6032
6182
  app.dialogRegion.show(new ChooseImporterView(options).render());
6033
6183
  };
6034
6184
 
6035
- function template$v(data) {
6185
+ function template$w(data) {
6036
6186
  var __t, __p = '';
6037
6187
  __p += '<div class="box file_importer_box">\n <h1 class="dialog-header">' +
6038
6188
  ((__t = ( I18n.t('pageflow.editor.file_importers.'+data.importerKey+'.dialog_label') )) == null ? '' : __t) +
@@ -6046,7 +6196,7 @@ __p += '<div class="box file_importer_box">\n <h1 class="dialog-header">' +
6046
6196
  return __p
6047
6197
  }
6048
6198
 
6049
- function template$w(data) {
6199
+ function template$x(data) {
6050
6200
  var __t, __p = '';
6051
6201
  __p += '<div class="box">\n <h1 class="dialog-header">' +
6052
6202
  ((__t = ( I18n.t('pageflow.editor.templates.confirm_upload.header') )) == null ? '' : __t) +
@@ -6062,14 +6212,14 @@ __p += '<div class="box">\n <h1 class="dialog-header">' +
6062
6212
  return __p
6063
6213
  }
6064
6214
 
6065
- function template$x(data) {
6215
+ function template$y(data) {
6066
6216
  var __p = '';
6067
6217
  __p += '';
6068
6218
  return __p
6069
6219
  }
6070
6220
 
6071
6221
  var EditFileView = Marionette.ItemView.extend({
6072
- template: template$x,
6222
+ template: template$y,
6073
6223
  className: 'edit_file',
6074
6224
  onRender: function onRender() {
6075
6225
  var fileType = this.model.fileType();
@@ -6079,19 +6229,14 @@ var EditFileView = Marionette.ItemView.extend({
6079
6229
  model: this.model.configuration,
6080
6230
  attributeTranslationKeyPrefixes: ['pageflow.entry_types.' + entryTypeName + '.editor.files.attributes.' + fileType.collectionName, 'pageflow.entry_types.' + entryTypeName + '.editor.files.common_attributes', 'pageflow.editor.files.attributes.' + fileType.collectionName, 'pageflow.editor.files.common_attributes', 'pageflow.editor.nested_files.' + fileType.collectionName, 'pageflow.editor.nested_files.common_attributes']
6081
6231
  });
6082
-
6083
- if (this.options.displayFileName) {
6084
- tab.input('file_name', TextInputView, {
6085
- model: this.model,
6086
- disabled: true
6087
- });
6088
- }
6089
-
6232
+ tab.input('display_name', FileNameInputView, {
6233
+ model: this.model,
6234
+ required: true
6235
+ });
6090
6236
  tab.input('rights', TextInputView, {
6091
6237
  model: this.model,
6092
6238
  placeholder: entry.get('default_file_rights')
6093
6239
  });
6094
-
6095
6240
  if (editor.entryType.supportsExtendedFileRights && !fileType.noExtendedFileRights) {
6096
6241
  tab.input('source_url', TextInputView);
6097
6242
  tab.input('license', SelectInputView, {
@@ -6106,14 +6251,11 @@ var EditFileView = Marionette.ItemView.extend({
6106
6251
  values: ['credits', 'inline']
6107
6252
  });
6108
6253
  }
6109
-
6110
6254
  tab.view(SeparatorView);
6111
-
6112
6255
  _(this.fileTypeInputs()).each(function (options) {
6113
6256
  tab.input(options.name, options.inputView, options.inputViewOptions);
6114
6257
  });
6115
-
6116
- tab.input('original_url', UrlDisplayView, {
6258
+ tab.input('download_url', UrlDisplayView, {
6117
6259
  model: this.model
6118
6260
  });
6119
6261
  this.appendSubview(tab);
@@ -6134,7 +6276,6 @@ var UploadableFilesView = Marionette.View.extend({
6134
6276
  className: 'uploadable_files',
6135
6277
  initialize: function initialize() {
6136
6278
  this.uploadableFiles = this.collection.uploadable();
6137
-
6138
6279
  if (!this.options.selection.has('file')) {
6139
6280
  this.options.selection.set('file', this.uploadableFiles.first());
6140
6281
  }
@@ -6159,7 +6300,7 @@ var UploadableFilesView = Marionette.View.extend({
6159
6300
  },
6160
6301
  commonColumns: function commonColumns(options) {
6161
6302
  return [{
6162
- name: 'file_name',
6303
+ name: 'display_name',
6163
6304
  headerText: options.fileTypeDisplayName,
6164
6305
  cellView: TextTableCellView
6165
6306
  }, {
@@ -6177,7 +6318,7 @@ var UploadableFilesView = Marionette.View.extend({
6177
6318
  });
6178
6319
 
6179
6320
  var ConfirmFileImportUploadView = Marionette.Layout.extend({
6180
- template: template$w,
6321
+ template: template$x,
6181
6322
  className: 'confirm_upload editor dialog',
6182
6323
  mixins: [dialogView],
6183
6324
  regions: {
@@ -6196,13 +6337,11 @@ var ConfirmFileImportUploadView = Marionette.Layout.extend({
6196
6337
  },
6197
6338
  getSelectedFiles: function getSelectedFiles() {
6198
6339
  var files = [];
6199
-
6200
6340
  _.each(state.files, function (collection) {
6201
6341
  if (collection.length > 0) {
6202
6342
  files = files.concat(collection.toJSON());
6203
6343
  }
6204
6344
  });
6205
-
6206
6345
  return files;
6207
6346
  },
6208
6347
  initialize: function initialize() {
@@ -6231,7 +6370,6 @@ var ConfirmFileImportUploadView = Marionette.Layout.extend({
6231
6370
  },
6232
6371
  update: function update() {
6233
6372
  var file = this.selection.get('file');
6234
-
6235
6373
  if (file) {
6236
6374
  this.selectedFileRegion.show(new EditFileView({
6237
6375
  model: file
@@ -6241,13 +6379,12 @@ var ConfirmFileImportUploadView = Marionette.Layout.extend({
6241
6379
  }
6242
6380
  }
6243
6381
  });
6244
-
6245
6382
  ConfirmFileImportUploadView.open = function (options) {
6246
6383
  app.dialogRegion.show(new ConfirmFileImportUploadView(options));
6247
6384
  };
6248
6385
 
6249
6386
  var FilesImporterView = Marionette.ItemView.extend({
6250
- template: template$v,
6387
+ template: template$w,
6251
6388
  className: 'files_importer editor dialog',
6252
6389
  mixins: [dialogView],
6253
6390
  ui: {
@@ -6271,7 +6408,6 @@ var FilesImporterView = Marionette.ItemView.extend({
6271
6408
  });
6272
6409
  this.listenTo(this.model.get('importer'), "change", function (event) {
6273
6410
  this.updateImportButton();
6274
-
6275
6411
  if (!this.isInitialized) {
6276
6412
  this.updateAuthenticationView();
6277
6413
  }
@@ -6279,7 +6415,6 @@ var FilesImporterView = Marionette.ItemView.extend({
6279
6415
  },
6280
6416
  updateAuthenticationView: function updateAuthenticationView() {
6281
6417
  var importer = this.model.get('importer');
6282
-
6283
6418
  if (importer.get('isAuthenticated')) {
6284
6419
  this.ui.contentPanel.empty();
6285
6420
  this.ui.contentPanel.append(this.model.get('importer').createFileImportDialogView().render().el);
@@ -6294,14 +6429,14 @@ var FilesImporterView = Marionette.ItemView.extend({
6294
6429
  var self = this;
6295
6430
  this.model.get('importer').getFilesMetaData().then(function (metaData) {
6296
6431
  if (metaData) {
6297
- self.model.set('metaData', metaData); // add each selected file meta to state.files
6298
-
6432
+ self.model.set('metaData', metaData);
6433
+ // add each selected file meta to state.files
6299
6434
  for (var i = 0; i < metaData.files.length; i++) {
6300
6435
  var file = metaData.files[i];
6301
6436
  var fileType = editor.fileTypes.findByUpload(file);
6302
6437
  file = new fileType.model({
6303
6438
  state: 'uploadable',
6304
- file_name: file.name,
6439
+ display_name: file.name,
6305
6440
  content_type: file.type,
6306
6441
  file_size: -1,
6307
6442
  rights: file.rights,
@@ -6314,7 +6449,6 @@ var FilesImporterView = Marionette.ItemView.extend({
6314
6449
  });
6315
6450
  state.entry.getFileCollection(fileType).add(file);
6316
6451
  }
6317
-
6318
6452
  ConfirmFileImportUploadView.open({
6319
6453
  fileTypes: editor.fileTypes,
6320
6454
  fileImportModel: self.model,
@@ -6332,12 +6466,11 @@ var FilesImporterView = Marionette.ItemView.extend({
6332
6466
  }
6333
6467
  }
6334
6468
  });
6335
-
6336
6469
  FilesImporterView.open = function (options) {
6337
6470
  app.dialogRegion.show(new FilesImporterView(options).render());
6338
6471
  };
6339
6472
 
6340
- function template$y(data) {
6473
+ function template$z(data) {
6341
6474
  var __t, __p = '';
6342
6475
  __p += '<button class="">\n <span class="label">' +
6343
6476
  ((__t = ( I18n.t('pageflow.editor.templates.select_button.select') )) == null ? '' : __t) +
@@ -6346,7 +6479,7 @@ return __p
6346
6479
  }
6347
6480
 
6348
6481
  var SelectButtonView = Marionette.ItemView.extend({
6349
- template: template$y,
6482
+ template: template$z,
6350
6483
  className: 'select_button',
6351
6484
  ui: {
6352
6485
  button: 'button',
@@ -6359,15 +6492,12 @@ var SelectButtonView = Marionette.ItemView.extend({
6359
6492
  e.preventDefault();
6360
6493
  var index = getClickedIndex(e.target);
6361
6494
  this.model.get('options')[index].handler();
6362
-
6363
6495
  function getClickedIndex(target) {
6364
6496
  var $target = $(target),
6365
- index = parseInt($target.data('index'), 10);
6366
-
6497
+ index = parseInt($target.data('index'), 10);
6367
6498
  if (isNaN(index)) {
6368
6499
  index = parseInt($target.find('a').data('index'), 10);
6369
6500
  }
6370
-
6371
6501
  return index;
6372
6502
  }
6373
6503
  }
@@ -6381,7 +6511,7 @@ var SelectButtonView = Marionette.ItemView.extend({
6381
6511
  }
6382
6512
  });
6383
6513
 
6384
- function template$z(data) {
6514
+ function template$A(data) {
6385
6515
  var __t, __p = '';
6386
6516
  __p += '<a class="back">' +
6387
6517
  ((__t = ( I18n.t('pageflow.editor.templates.files.back') )) == null ? '' : __t) +
@@ -6390,7 +6520,7 @@ return __p
6390
6520
  }
6391
6521
 
6392
6522
  var FilesView = Marionette.ItemView.extend({
6393
- template: template$z,
6523
+ template: template$A,
6394
6524
  className: 'manage_files',
6395
6525
  events: {
6396
6526
  'click a.back': 'goBack',
@@ -6410,7 +6540,6 @@ var FilesView = Marionette.ItemView.extend({
6410
6540
  });
6411
6541
  }
6412
6542
  }];
6413
-
6414
6543
  if (editor.fileImporters.keys().length > 0) {
6415
6544
  menuOptions.push({
6416
6545
  label: I18n$1.t('pageflow.editor.views.files_view.import'),
@@ -6425,7 +6554,6 @@ var FilesView = Marionette.ItemView.extend({
6425
6554
  }
6426
6555
  });
6427
6556
  }
6428
-
6429
6557
  this.addFileModel = new Backbone.Model({
6430
6558
  label: I18n$1.t('pageflow.editor.views.files_view.add'),
6431
6559
  options: menuOptions
@@ -6448,12 +6576,12 @@ var FilesView = Marionette.ItemView.extend({
6448
6576
  tab: function tab(fileType) {
6449
6577
  var selectionMode = this.options.allowSelectingAny || this.options.tabName === fileType.collectionName;
6450
6578
  this.tabsView.tab(fileType.collectionName, _.bind(function () {
6451
- return this.subview(new FilteredFilesView({
6579
+ return new FilteredFilesView({
6452
6580
  entry: state.entry,
6453
6581
  fileType: fileType,
6454
6582
  selectionHandler: selectionMode && this.options.selectionHandler,
6455
6583
  filterName: selectionMode && this.options.filterName
6456
- }));
6584
+ });
6457
6585
  }, this));
6458
6586
  this.listenTo(this.model, 'change:uploading_' + fileType.collectionName + '_count', function (model, value) {
6459
6587
  this.tabsView.toggleSpinnerOnTab(fileType.collectionName, value > 0);
@@ -6475,14 +6603,14 @@ var FilesView = Marionette.ItemView.extend({
6475
6603
  }
6476
6604
  });
6477
6605
 
6478
- function template$A(data) {
6606
+ function template$B(data) {
6479
6607
  var __p = '';
6480
6608
  __p += '<div class="quota_state">\n</div>\n<div class="outlet">\n</div>\n<div class="exhausted_message">\n</div>\n';
6481
6609
  return __p
6482
6610
  }
6483
6611
 
6484
6612
  var EntryPublicationQuotaDecoratorView = Marionette.Layout.extend({
6485
- template: template$A,
6613
+ template: template$B,
6486
6614
  className: 'quota_decorator',
6487
6615
  regions: {
6488
6616
  outlet: '.outlet'
@@ -6499,7 +6627,6 @@ var EntryPublicationQuotaDecoratorView = Marionette.Layout.extend({
6499
6627
  },
6500
6628
  update: function update() {
6501
6629
  var view = this;
6502
-
6503
6630
  if (this.model.get('checking')) {
6504
6631
  view.ui.state.text(I18n$1.t('pageflow.editor.quotas.loading'));
6505
6632
  view.ui.exhaustedMessage.hide().html('');
@@ -6516,14 +6643,13 @@ var EntryPublicationQuotaDecoratorView = Marionette.Layout.extend({
6516
6643
  } else {
6517
6644
  view.ui.state.hide();
6518
6645
  }
6519
-
6520
6646
  view.outlet.show(view.options.view);
6521
6647
  }
6522
6648
  }
6523
6649
  }
6524
6650
  });
6525
6651
 
6526
- function template$B(data) {
6652
+ function template$C(data) {
6527
6653
  var __t, __p = '';
6528
6654
  __p += '<div class="files_pending notice">\n <p>' +
6529
6655
  ((__t = ( I18n.t('pageflow.editor.templates.publish_entry.files_pending_notice') )) == null ? '' : __t) +
@@ -6574,7 +6700,7 @@ return __p
6574
6700
  }
6575
6701
 
6576
6702
  var PublishEntryView = Marionette.ItemView.extend({
6577
- template: template$B,
6703
+ template: template$C,
6578
6704
  className: 'publish_entry',
6579
6705
  ui: {
6580
6706
  publishUntilFields: '.publish_until_fields',
@@ -6632,31 +6758,27 @@ var PublishEntryView = Marionette.ItemView.extend({
6632
6758
  this.ui.successLink.attr('href', this.model.get('pretty_url'));
6633
6759
  this.ui.successLink.text(this.model.get('pretty_url'));
6634
6760
  var publishedUntil = new Date(this.model.get('published_until'));
6635
-
6636
6761
  if (publishedUntil > new Date()) {
6637
6762
  this.ui.publishUntilField.datepicker('setDate', publishedUntil);
6638
6763
  this.ui.publishUntilTimeField.val(timeStr(publishedUntil));
6639
6764
  } else {
6640
6765
  this.ui.publishUntilField.datepicker('setDate', this.defaultPublishedUntilDate());
6641
6766
  }
6642
-
6643
6767
  this.ui.userNameField.val(this.options.account.get('name'));
6644
-
6645
6768
  if (this.model.get('password_protected')) {
6646
6769
  this.ui.passwordProtectedCheckBox.prop('checked', true);
6647
6770
  this.togglePasswordFields();
6648
6771
  } else {
6649
6772
  this.ui.passwordField.val(this.randomPassword());
6650
6773
  }
6651
-
6652
6774
  this.ui.noindexCheckBox.prop('checked', this.model.get('last_published_with_noindex'));
6653
6775
  this.ui.alreadyPublishedWithPassword.toggle(this.model.get('published') && this.model.get('password_protected'));
6654
6776
  this.ui.previouslyPublishedWithPassword.toggle(!this.model.get('published') && this.model.get('password_protected'));
6655
- this.ui.alreadyPublishedWithoutPassword.toggle(this.model.get('published') && !this.model.get('password_protected')); // Helpers
6777
+ this.ui.alreadyPublishedWithoutPassword.toggle(this.model.get('published') && !this.model.get('password_protected'));
6656
6778
 
6779
+ // Helpers
6657
6780
  function timeStr(date) {
6658
6781
  return twoDigits(date.getHours()) + ':' + twoDigits(date.getMinutes());
6659
-
6660
6782
  function twoDigits(val) {
6661
6783
  return ("0" + val).slice(-2);
6662
6784
  }
@@ -6664,28 +6786,23 @@ var PublishEntryView = Marionette.ItemView.extend({
6664
6786
  },
6665
6787
  save: function save() {
6666
6788
  var publishedUntil = null;
6667
-
6668
6789
  if (this.$el.hasClass('publishing')) {
6669
6790
  return;
6670
6791
  }
6671
-
6672
6792
  if (this.ui.publishUntilRadioBox.is(':checked')) {
6673
6793
  publishedUntil = this.ui.publishUntilField.datepicker('getDate');
6674
6794
  setTime(publishedUntil, this.ui.publishUntilTimeField.val());
6675
-
6676
6795
  if (!this.checkPublishUntilTime()) {
6677
6796
  alert('Bitte legen Sie einen gültigen Depublikationszeitpunkt fest.');
6678
6797
  this.ui.publishUntilTimeField.focus();
6679
6798
  return;
6680
6799
  }
6681
-
6682
6800
  if (!publishedUntil || !checkDate(publishedUntil)) {
6683
6801
  alert('Bitte legen Sie ein Depublikationsdatum fest.');
6684
6802
  this.ui.publishUntilField.focus();
6685
6803
  return;
6686
6804
  }
6687
6805
  }
6688
-
6689
6806
  var that = this;
6690
6807
  this.options.entryPublication.publish({
6691
6808
  published_until: publishedUntil,
@@ -6698,41 +6815,35 @@ var PublishEntryView = Marionette.ItemView.extend({
6698
6815
  if (that.isClosed) {
6699
6816
  return;
6700
6817
  }
6701
-
6702
6818
  that.$el.removeClass('publishing');
6703
6819
  that.$el.addClass('succeeded');
6704
6820
  that.$('input').removeAttr('disabled');
6705
6821
  var publishedMessage = that.options.entryPublication.get('published_message_html');
6706
-
6707
6822
  if (publishedMessage) {
6708
6823
  that.ui.successNotice.append(publishedMessage);
6709
6824
  }
6710
-
6711
6825
  that.enableSave();
6712
6826
  });
6713
6827
  this.$el.addClass('publishing');
6714
6828
  this.$('input').attr('disabled', '1');
6715
- this.disableSave(); // Helpers
6829
+ this.disableSave();
6716
6830
 
6831
+ // Helpers
6717
6832
  function setTime(date, time) {
6718
6833
  date.setHours.apply(date, parseTime(time));
6719
6834
  }
6720
-
6721
6835
  function parseTime(str) {
6722
6836
  return str.split(':').map(function (number) {
6723
6837
  return parseInt(number, 10);
6724
6838
  });
6725
6839
  }
6726
-
6727
6840
  function checkDate(date) {
6728
6841
  if (Object.prototype.toString.call(date) === "[object Date]") {
6729
6842
  if (isNaN(date.getTime())) {
6730
6843
  return false;
6731
6844
  }
6732
-
6733
6845
  return true;
6734
6846
  }
6735
-
6736
6847
  return false;
6737
6848
  }
6738
6849
  },
@@ -6757,11 +6868,9 @@ var PublishEntryView = Marionette.ItemView.extend({
6757
6868
  this.ui.publishUntilRadioBox[0].checked = false;
6758
6869
  this.ui.publishUntilFields.addClass('disabled');
6759
6870
  this.checkForm();
6760
-
6761
6871
  if (!this.checkPublishUntilTime()) {
6762
6872
  this.ui.publishUntilTimeField.val('00:00');
6763
6873
  }
6764
-
6765
6874
  this.ui.publishUntilTimeField.removeClass('invalid');
6766
6875
  this.ui.publishUntilField.removeClass('invalid');
6767
6876
  },
@@ -6794,7 +6903,6 @@ var PublishEntryView = Marionette.ItemView.extend({
6794
6903
  this.ui.publishUntilTimeField.addClass('invalid');
6795
6904
  return false;
6796
6905
  }
6797
-
6798
6906
  this.ui.publishUntilTimeField.removeClass('invalid');
6799
6907
  return true;
6800
6908
  },
@@ -6818,7 +6926,6 @@ var PublishEntryView = Marionette.ItemView.extend({
6818
6926
  }).join('');
6819
6927
  }
6820
6928
  });
6821
-
6822
6929
  PublishEntryView.create = function (options) {
6823
6930
  return new BackButtonDecoratorView({
6824
6931
  view: new EntryPublicationQuotaDecoratorView({
@@ -6841,10 +6948,9 @@ var SidebarController = Marionette.Controller.extend({
6841
6948
  },
6842
6949
  files: function files(collectionName, handler, payload, filterName) {
6843
6950
  var _split = (collectionName || '').split(':'),
6844
- _split2 = _slicedToArray(_split, 2),
6845
- tabName = _split2[0],
6846
- suffix = _split2[1];
6847
-
6951
+ _split2 = _slicedToArray(_split, 2),
6952
+ tabName = _split2[0],
6953
+ suffix = _split2[1];
6848
6954
  this.region.show(new FilesView({
6849
6955
  model: this.entry,
6850
6956
  selectionHandler: handler && editor.createFileSelectionHandler(handler, payload),
@@ -6883,9 +6989,12 @@ var SidebarController = Marionette.Controller.extend({
6883
6989
  editor.setDefaultHelpEntry('pageflow.help_entries.publish');
6884
6990
  },
6885
6991
  widget: function widget(id) {
6992
+ var model = this.entry.widgets.get(id);
6886
6993
  this.region.show(new EditWidgetView({
6887
- model: this.entry.widgets.get(id)
6994
+ entry: this.entry,
6995
+ model: model
6888
6996
  }));
6997
+ this.entry.trigger('selectWidget', model);
6889
6998
  }
6890
6999
  });
6891
7000
 
@@ -6969,7 +7078,7 @@ var ScrollingView = Marionette.View.extend({
6969
7078
  }
6970
7079
  });
6971
7080
 
6972
- function template$C(data) {
7081
+ function template$D(data) {
6973
7082
  var __t, __p = '';
6974
7083
  __p += '<div class="box">\n <h2>' +
6975
7084
  ((__t = ( I18n.t('pageflow.editor.templates.help.title') )) == null ? '' : __t) +
@@ -6980,7 +7089,7 @@ return __p
6980
7089
  }
6981
7090
 
6982
7091
  var HelpView = Marionette.ItemView.extend({
6983
- template: template$C,
7092
+ template: template$D,
6984
7093
  className: 'help',
6985
7094
  ui: {
6986
7095
  placeholder: '.placeholder',
@@ -6996,7 +7105,6 @@ var HelpView = Marionette.ItemView.extend({
6996
7105
  },
6997
7106
  'click a': function clickA(event) {
6998
7107
  var link = $(event.currentTarget);
6999
-
7000
7108
  if (link.attr('href').indexOf('#') === 0) {
7001
7109
  this.showSection(link.attr('href').substring(1), {
7002
7110
  scrollIntoView: !link.parents('nav').length
@@ -7004,7 +7112,6 @@ var HelpView = Marionette.ItemView.extend({
7004
7112
  } else if (link.attr('href').match(/^http/)) {
7005
7113
  window.open(link.attr('href'), '_blank');
7006
7114
  }
7007
-
7008
7115
  return false;
7009
7116
  },
7010
7117
  'click .box': function clickBox() {
@@ -7037,10 +7144,8 @@ var HelpView = Marionette.ItemView.extend({
7037
7144
  var menuItem = $(this);
7038
7145
  var active = menuItem.find('a').attr('href') === '#' + name;
7039
7146
  menuItem.toggleClass('active', active);
7040
-
7041
7147
  if (active) {
7042
7148
  menuItem.parents('.expandable').addClass('expanded');
7043
-
7044
7149
  if (options.scrollIntoView) {
7045
7150
  menuItem[0].scrollIntoView();
7046
7151
  }
@@ -7057,7 +7162,7 @@ var PageThumbnailView = ModelThumbnailView.extend({
7057
7162
  className: 'model_thumbnail page_thumbnail'
7058
7163
  });
7059
7164
 
7060
- function template$D(data) {
7165
+ function template$E(data) {
7061
7166
  var __t, __p = '';
7062
7167
  __p += '<div>\n <span class="missing_page_thumbnail"></span>\n <span class="page_thumbnail"></span>\n <div class="title"></div>\n <div class="label"></div>\n <a class="remove" title="' +
7063
7168
  ((__t = ( I18n.t('pageflow.editor.templates.page_link_item.remove') )) == null ? '' : __t) +
@@ -7068,7 +7173,7 @@ return __p
7068
7173
  }
7069
7174
 
7070
7175
  var PageLinkItemView = Marionette.ItemView.extend({
7071
- template: template$D,
7176
+ template: template$E,
7072
7177
  tagName: 'li',
7073
7178
  className: 'page_link',
7074
7179
  ui: {
@@ -7099,7 +7204,6 @@ var PageLinkItemView = Marionette.ItemView.extend({
7099
7204
  },
7100
7205
  onRender: function onRender() {
7101
7206
  var page = this.model.targetPage();
7102
-
7103
7207
  if (page) {
7104
7208
  this.subview(new PageThumbnailView({
7105
7209
  el: this.ui.thumbnail,
@@ -7110,7 +7214,6 @@ var PageLinkItemView = Marionette.ItemView.extend({
7110
7214
  } else {
7111
7215
  this.ui.title.text(I18n$1.t('pageflow.editor.views.page_link_item_view.no_page'));
7112
7216
  }
7113
-
7114
7217
  this.ui.label.text(this.model.label());
7115
7218
  this.ui.label.toggle(!!this.model.label());
7116
7219
  this.ui.editButton.toggle(!!this.model.editPath());
@@ -7118,7 +7221,7 @@ var PageLinkItemView = Marionette.ItemView.extend({
7118
7221
  }
7119
7222
  });
7120
7223
 
7121
- function template$E(data) {
7224
+ function template$F(data) {
7122
7225
  var __t, __p = '';
7123
7226
  __p += '<label>\n <span class="name">' +
7124
7227
  ((__t = ( I18n.t('pageflow.editor.templates.page_links.label') )) == null ? '' : __t) +
@@ -7129,7 +7232,7 @@ return __p
7129
7232
  }
7130
7233
 
7131
7234
  var PageLinksView = Marionette.ItemView.extend({
7132
- template: template$E,
7235
+ template: template$F,
7133
7236
  className: 'page_links',
7134
7237
  ui: {
7135
7238
  links: 'ul.links',
@@ -7165,14 +7268,14 @@ var PageLinksView = Marionette.ItemView.extend({
7165
7268
  }
7166
7269
  });
7167
7270
 
7168
- function template$F(data) {
7271
+ function template$G(data) {
7169
7272
  var __p = '';
7170
7273
  __p += '<div class="emulation_mode_button-wrapper"\n data-tooltip-align="top center">\n <div class="emulation_mode_button-desktop_icon"/>\n <div class="emulation_mode_button-phone_icon"/>\n <div class="emulation_mode_button-track" />\n <div class="emulation_mode_button-thumb" />\n</div>\n';
7171
7274
  return __p
7172
7275
  }
7173
7276
 
7174
7277
  var EmulationModeButtonView = Marionette.ItemView.extend({
7175
- template: template$F,
7278
+ template: template$G,
7176
7279
  className: 'emulation_mode_button',
7177
7280
  mixins: [tooltipContainer],
7178
7281
  ui: {
@@ -7185,7 +7288,6 @@ var EmulationModeButtonView = Marionette.ItemView.extend({
7185
7288
  if (this.model.get('emulation_mode_disabled')) {
7186
7289
  return;
7187
7290
  }
7188
-
7189
7291
  if (this.model.has('emulation_mode')) {
7190
7292
  this.model.unset('emulation_mode');
7191
7293
  } else {
@@ -7206,7 +7308,7 @@ var EmulationModeButtonView = Marionette.ItemView.extend({
7206
7308
  }
7207
7309
  });
7208
7310
 
7209
- function template$G(data) {
7311
+ function template$H(data) {
7210
7312
  var __t, __p = '';
7211
7313
  __p +=
7212
7314
  ((__t = ( I18n.t('pageflow.editor.templates.help_button.open_help') )) == null ? '' : __t);
@@ -7214,7 +7316,7 @@ return __p
7214
7316
  }
7215
7317
 
7216
7318
  var HelpButtonView = Marionette.ItemView.extend({
7217
- template: template$G,
7319
+ template: template$H,
7218
7320
  className: 'help_button',
7219
7321
  events: {
7220
7322
  'click': function click() {
@@ -7231,7 +7333,6 @@ var SidebarFooterView = Marionette.View.extend({
7231
7333
  model: this.model
7232
7334
  }));
7233
7335
  }
7234
-
7235
7336
  this.appendSubview(new HelpButtonView());
7236
7337
  return this;
7237
7338
  }
@@ -7262,7 +7363,7 @@ var InfoBoxView = Marionette.View.extend({
7262
7363
  }
7263
7364
  });
7264
7365
 
7265
- function template$H(data) {
7366
+ function template$I(data) {
7266
7367
  var __t, __p = '';
7267
7368
  __p += '<span class="list_item_thumbnail"></span>\n<span class="list_item_missing_thumbnail"></span>\n<span class="list_item_type_pictogram type_pictogram"></span>\n\n<div class="list_item_title"></div>\n<div class="list_item_description"></div>\n\n<div class="list_item_buttons">\n <a class="list_item_edit_button" title="' +
7268
7369
  ((__t = ( I18n.t('pageflow.editor.templates.list_item.edit') )) == null ? '' : __t) +
@@ -7273,7 +7374,7 @@ return __p
7273
7374
  }
7274
7375
 
7275
7376
  var ListItemView = Marionette.ItemView.extend({
7276
- template: template$H,
7377
+ template: template$I,
7277
7378
  tagName: 'li',
7278
7379
  className: 'list_item',
7279
7380
  ui: {
@@ -7312,11 +7413,9 @@ var ListItemView = Marionette.ItemView.extend({
7312
7413
  el: this.ui.thumbnail,
7313
7414
  model: this.model
7314
7415
  }));
7315
-
7316
7416
  if (this.options.typeName) {
7317
7417
  this.$el.addClass(this.typeName());
7318
7418
  }
7319
-
7320
7419
  this.ui.editButton.toggleClass('is_available', !!this.options.onEdit);
7321
7420
  this.ui.removeButton.toggleClass('is_available', !!this.options.onRemove);
7322
7421
  this.update();
@@ -7347,7 +7446,7 @@ var ListItemView = Marionette.ItemView.extend({
7347
7446
  }
7348
7447
  });
7349
7448
 
7350
- function template$I(data) {
7449
+ function template$J(data) {
7351
7450
  var __t, __p = '';
7352
7451
  __p += '<div class="checking notice editor">\n <p>' +
7353
7452
  ((__t = ( I18n.t('pageflow.editor.templates.locked.loading') )) == null ? '' : __t) +
@@ -7362,7 +7461,7 @@ return __p
7362
7461
  }
7363
7462
 
7364
7463
  var LockedView = Marionette.ItemView.extend({
7365
- template: template$I,
7464
+ template: template$J,
7366
7465
  className: 'locked checking',
7367
7466
  ui: {
7368
7467
  breakButton: '.break',
@@ -7453,7 +7552,6 @@ var BackgroundImageEmbeddedView = Marionette.View.extend({
7453
7552
  } else {
7454
7553
  this.updateClassName();
7455
7554
  }
7456
-
7457
7555
  if (this.options.dataSizeAttributes) {
7458
7556
  this.updateDataSizeAttributes();
7459
7557
  }
@@ -7465,7 +7563,6 @@ var BackgroundImageEmbeddedView = Marionette.View.extend({
7465
7563
  var prefix = this.options.backgroundImageClassNamePrefix.call ? this.options.backgroundImageClassNamePrefix() : this.options.backgroundImageClassNamePrefix;
7466
7564
  prefix = prefix || 'image';
7467
7565
  var backgroundImageClassName = id && prefix + '_' + id;
7468
-
7469
7566
  if (this.currentBackgroundImageClassName !== backgroundImageClassName) {
7470
7567
  this.$el.removeClass(this.currentBackgroundImageClassName);
7471
7568
  this.$el.addClass(backgroundImageClassName);
@@ -7480,7 +7577,6 @@ var BackgroundImageEmbeddedView = Marionette.View.extend({
7480
7577
  },
7481
7578
  updateDataSizeAttributes: function updateDataSizeAttributes() {
7482
7579
  var imageFile = this.model.getImageFile(this.options.propertyName);
7483
-
7484
7580
  if (imageFile && imageFile.isReady()) {
7485
7581
  this.$el.attr('data-width', imageFile.get('width'));
7486
7582
  this.$el.attr('data-height', imageFile.get('height'));
@@ -7488,7 +7584,6 @@ var BackgroundImageEmbeddedView = Marionette.View.extend({
7488
7584
  this.$el.attr('data-width', '16');
7489
7585
  this.$el.attr('data-height', '9');
7490
7586
  }
7491
-
7492
7587
  this.$el.css({
7493
7588
  backgroundPosition: '0 0'
7494
7589
  });
@@ -7517,15 +7612,12 @@ var LazyVideoEmbeddedView = Marionette.View.extend({
7517
7612
  if (this.videoPlayer.isPresent() && this.model.hasChanged(this.options.propertyName)) {
7518
7613
  var paused = this.videoPlayer.paused();
7519
7614
  this.videoPlayer.src(this.model.getVideoFileSources(this.options.propertyName));
7520
-
7521
7615
  if (!paused) {
7522
7616
  this.videoPlayer.play();
7523
7617
  }
7524
7618
  }
7525
-
7526
7619
  if (this.options.dataSizeAttributes) {
7527
7620
  var videoFile = this.model.getVideoFile(this.options.propertyName);
7528
-
7529
7621
  if (videoFile && videoFile.isReady()) {
7530
7622
  this.$el.attr('data-width', videoFile.get('width'));
7531
7623
  this.$el.attr('data-height', videoFile.get('height'));
@@ -7537,7 +7629,7 @@ var LazyVideoEmbeddedView = Marionette.View.extend({
7537
7629
  }
7538
7630
  });
7539
7631
 
7540
- function template$J(data) {
7632
+ function template$K(data) {
7541
7633
  var __t, __p = '';
7542
7634
  __p += '<li class="uploading"><span class="count">0</span>' +
7543
7635
  ((__t = ( I18n.t('pageflow.editor.templates.notification.upload_pending') )) == null ? '' : __t) +
@@ -7560,7 +7652,7 @@ return __p
7560
7652
  var NotificationsView = Marionette.ItemView.extend({
7561
7653
  className: 'notifications',
7562
7654
  tagName: 'ul',
7563
- template: template$J,
7655
+ template: template$K,
7564
7656
  ui: {
7565
7657
  failedCount: '.failed .count',
7566
7658
  uploadingCount: '.uploading .count',
@@ -7604,12 +7696,10 @@ var FileProcessingStateDisplayView = Marionette.View.extend({
7604
7696
  if (typeof this.options.collection === 'string') {
7605
7697
  this.options.collection = state.entry.getFileCollection(editor.fileTypes.findByCollectionName(this.options.collection));
7606
7698
  }
7607
-
7608
7699
  this.listenTo(this.model, 'change:' + this.options.propertyName, this._update);
7609
7700
  },
7610
7701
  render: function render() {
7611
7702
  this._update();
7612
-
7613
7703
  return this;
7614
7704
  },
7615
7705
  _update: function _update() {
@@ -7618,9 +7708,7 @@ var FileProcessingStateDisplayView = Marionette.View.extend({
7618
7708
  this.fileStagesView.close();
7619
7709
  this.fileStagesView = null;
7620
7710
  }
7621
-
7622
7711
  this.file = this._getFile();
7623
-
7624
7712
  if (this.file) {
7625
7713
  this.listenTo(this.file.unfinishedStages, 'add remove', this._updateClassNames);
7626
7714
  this.fileStagesView = new CollectionView({
@@ -7633,7 +7721,6 @@ var FileProcessingStateDisplayView = Marionette.View.extend({
7633
7721
  });
7634
7722
  this.appendSubview(this.fileStagesView);
7635
7723
  }
7636
-
7637
7724
  this._updateClassNames();
7638
7725
  },
7639
7726
  _updateClassNames: function _updateClassNames() {
@@ -7654,7 +7741,6 @@ var NestedFilesView = Marionette.View.extend({
7654
7741
  this.options.selection.set('file', this.collection.first());
7655
7742
  this.options.selection.set('nextFile', this.collection.at(1));
7656
7743
  }
7657
-
7658
7744
  this.listenTo(this.collection, 'add', this.selectNewFile);
7659
7745
  this.listenTo(this.collection, 'remove', this.selectNextFileIfSelectionDeleted);
7660
7746
  this.listenTo(this.options.selection, 'change', this.setNextFile);
@@ -7684,7 +7770,6 @@ var NestedFilesView = Marionette.View.extend({
7684
7770
  configurationAttribute: true
7685
7771
  });
7686
7772
  });
7687
-
7688
7773
  nestedFilesColumns.push({
7689
7774
  name: 'delete',
7690
7775
  cellView: DeleteRowTableCellView,
@@ -7701,7 +7786,6 @@ var NestedFilesView = Marionette.View.extend({
7701
7786
  },
7702
7787
  selectNextFileIfSelectionDeleted: function selectNextFileIfSelectionDeleted() {
7703
7788
  var fileIndex = this.collection.indexOf(this.options.selection.get('file'));
7704
-
7705
7789
  if (fileIndex === -1) {
7706
7790
  var nextFile = this.options.selection.get('nextFile');
7707
7791
  this.options.selection.set('file', nextFile);
@@ -7709,7 +7793,6 @@ var NestedFilesView = Marionette.View.extend({
7709
7793
  },
7710
7794
  setNextFile: _.debounce(function () {
7711
7795
  var fileIndex = this.collection.indexOf(this.options.selection.get('file'));
7712
-
7713
7796
  if (typeof this.collection.at(fileIndex + 1) !== 'undefined') {
7714
7797
  this.options.selection.set('nextFile', this.collection.at(fileIndex + 1));
7715
7798
  } else if (typeof this.collection.at(fileIndex - 1) !== 'undefined') {
@@ -7720,7 +7803,7 @@ var NestedFilesView = Marionette.View.extend({
7720
7803
  }, 200)
7721
7804
  });
7722
7805
 
7723
- function template$K(data) {
7806
+ function template$L(data) {
7724
7807
  var __t, __p = '';
7725
7808
  __p += '<div class="text_tracks_container">\n <div class="files_upload_panel">\n <div class="files_panel">\n </div>\n <a class="upload" href="">' +
7726
7809
  ((__t = ( I18n.t('pageflow.editor.templates.text_tracks.upload') )) == null ? '' : __t) +
@@ -7731,7 +7814,7 @@ return __p
7731
7814
  }
7732
7815
 
7733
7816
  var TextTracksView = Marionette.Layout.extend({
7734
- template: template$K,
7817
+ template: template$L,
7735
7818
  className: 'text_tracks',
7736
7819
  regions: {
7737
7820
  selectedFileRegion: '.selected_file_region'
@@ -7765,7 +7848,6 @@ var TextTracksView = Marionette.Layout.extend({
7765
7848
  },
7766
7849
  update: function update() {
7767
7850
  var selectedFile = this.selection.get('file');
7768
-
7769
7851
  if (selectedFile) {
7770
7852
  this.selectedFileRegion.show(new EditFileView({
7771
7853
  model: selectedFile,
@@ -7795,7 +7877,7 @@ var TextTracksFileMetaDataItemValueView = FileMetaDataItemValueView.extend({
7795
7877
  }
7796
7878
  });
7797
7879
 
7798
- function template$L(data) {
7880
+ function template$M(data) {
7799
7881
  var __p = '';
7800
7882
  __p += '<label>\n <span class="list_label"></span>\n</label>\n\n<ul class="list_items"></ul>\n';
7801
7883
  return __p
@@ -7840,9 +7922,8 @@ return __p
7840
7922
  *
7841
7923
  * @class
7842
7924
  */
7843
-
7844
7925
  var ListView = Marionette.ItemView.extend({
7845
- template: template$L,
7926
+ template: template$M,
7846
7927
  className: 'list',
7847
7928
  ui: {
7848
7929
  label: '.list_label',
@@ -7872,7 +7953,7 @@ var ListView = Marionette.ItemView.extend({
7872
7953
  });
7873
7954
 
7874
7955
  var ConfirmUploadView = Marionette.Layout.extend({
7875
- template: template$w,
7956
+ template: template$x,
7876
7957
  className: 'confirm_upload editor dialog',
7877
7958
  mixins: [dialogView],
7878
7959
  regions: {
@@ -7906,7 +7987,6 @@ var ConfirmUploadView = Marionette.Layout.extend({
7906
7987
  },
7907
7988
  update: function update() {
7908
7989
  var file = this.selection.get('file');
7909
-
7910
7990
  if (file) {
7911
7991
  this.selectedFileRegion.show(new EditFileView({
7912
7992
  model: file
@@ -7916,7 +7996,6 @@ var ConfirmUploadView = Marionette.Layout.extend({
7916
7996
  }
7917
7997
  }
7918
7998
  });
7919
-
7920
7999
  ConfirmUploadView.watch = function (fileUploader, fileTypes, files) {
7921
8000
  fileUploader.on('new:batch', function () {
7922
8001
  ConfirmUploadView.open({
@@ -7926,7 +8005,6 @@ ConfirmUploadView.watch = function (fileUploader, fileTypes, files) {
7926
8005
  });
7927
8006
  });
7928
8007
  };
7929
-
7930
8008
  ConfirmUploadView.open = function (options) {
7931
8009
  app.dialogRegion.show(new ConfirmUploadView(options));
7932
8010
  };
@@ -7964,6 +8042,12 @@ ConfirmUploadView.open = function (options) {
7964
8042
  * Override the `destroyModel` method to customize destroy behavior.
7965
8043
  * Calls `destroyWithDelay` by default.
7966
8044
  *
8045
+ * Override the `goBackPath` property or method to customize the path
8046
+ * that the back button navigates to. Defaults to `/`.
8047
+ *
8048
+ * Set the `hideDestroyButton` property to `true` to hide the destroy
8049
+ * button.
8050
+ *
7967
8051
  * @param {Object} options
7968
8052
  * @param {Backbone.Model} options.model -
7969
8053
  * Model including the {@link configurationContainer},
@@ -7971,20 +8055,22 @@ ConfirmUploadView.open = function (options) {
7971
8055
  *
7972
8056
  * @since 15.1
7973
8057
  */
7974
-
7975
8058
  var EditConfigurationView = Marionette.Layout.extend({
7976
8059
  className: 'edit_configuration_view',
7977
8060
  template: function template(_ref) {
7978
- var t = _ref.t;
7979
- return "\n <a class=\"back\">".concat(t('back'), "</a>\n <a class=\"destroy\">").concat(t('destroy'), "</a>\n\n <div class=\"failure\">\n <p>").concat(t('save_error'), "</p>\n <p class=\"message\"></p>\n <a class=\"retry\" href=\"\">").concat(t('retry'), "</a>\n </div>\n\n <div class=\"configuration_container\"></div>\n ");
8061
+ var t = _ref.t,
8062
+ backLabel = _ref.backLabel,
8063
+ hideDestroyButton = _ref.hideDestroyButton;
8064
+ return "\n <a class=\"back\">".concat(backLabel, "</a>\n ").concat(hideDestroyButton ? '' : "<a class=\"destroy\">".concat(t('destroy'), "</a>"), "\n\n <div class=\"failure\">\n <p>").concat(t('save_error'), "</p>\n <p class=\"message\"></p>\n <a class=\"retry\" href=\"\">").concat(t('retry'), "</a>\n </div>\n\n <div class=\"configuration_container\"></div>\n ");
7980
8065
  },
7981
8066
  serializeData: function serializeData() {
7982
8067
  var _this = this;
7983
-
7984
8068
  return {
7985
8069
  t: function t(key) {
7986
8070
  return _this.t(key);
7987
- }
8071
+ },
8072
+ backLabel: this.getBackLabel(),
8073
+ hideDestroyButton: _.result(this, 'hideDestroyButton')
7988
8074
  };
7989
8075
  },
7990
8076
  mixins: [failureIndicatingView],
@@ -7997,7 +8083,6 @@ var EditConfigurationView = Marionette.Layout.extend({
7997
8083
  },
7998
8084
  onRender: function onRender() {
7999
8085
  var translationKeyPrefix = _.result(this, 'translationKeyPrefix');
8000
-
8001
8086
  this.configurationEditor = new ConfigurationEditorView({
8002
8087
  tabTranslationKeyPrefix: "".concat(translationKeyPrefix, ".tabs"),
8003
8088
  attributeTranslationKeyPrefixes: ["".concat(translationKeyPrefix, ".attributes")],
@@ -8020,13 +8105,16 @@ var EditConfigurationView = Marionette.Layout.extend({
8020
8105
  this.model.destroyWithDelay();
8021
8106
  },
8022
8107
  goBack: function goBack() {
8023
- editor.navigate('/', {
8108
+ var path = _.result(this, 'goBackPath') || '/';
8109
+ editor.navigate(path, {
8024
8110
  trigger: true
8025
8111
  });
8026
8112
  },
8113
+ getBackLabel: function getBackLabel() {
8114
+ return this.t(_.result(this, 'goBackPath') ? 'back' : 'outline');
8115
+ },
8027
8116
  t: function t(suffix) {
8028
8117
  var translationKeyPrefix = _.result(this, 'translationKeyPrefix');
8029
-
8030
8118
  return I18n$1.t("".concat(translationKeyPrefix, ".").concat(suffix), {
8031
8119
  defaultValue: I18n$1.t("pageflow.editor.views.edit_configuration.".concat(suffix))
8032
8120
  });
@@ -8200,7 +8288,6 @@ app.addInitializer(function (options) {
8200
8288
  settingsDialogTabLink: 'general'
8201
8289
  }
8202
8290
  }];
8203
-
8204
8291
  if (editor.entryType.supportsExtendedFileRights) {
8205
8292
  editor.fileTypes.commonMetaDataAttributes = [].concat(_toConsumableArray(editor.fileTypes.commonMetaDataAttributes), [{
8206
8293
  name: 'source_url',
@@ -8221,7 +8308,6 @@ app.addInitializer(function (options) {
8221
8308
  }
8222
8309
  }]);
8223
8310
  }
8224
-
8225
8311
  editor.fileTypes.commonSettingsDialogTabs = [{
8226
8312
  name: 'general',
8227
8313
  view: EditFileView
@@ -8250,33 +8336,31 @@ app.addInitializer(function (options) {
8250
8336
  settingsDialogTabLink: 'general'
8251
8337
  }
8252
8338
  };
8339
+ var altConfigurationEditorInput = {
8340
+ name: 'alt',
8341
+ inputView: TextInputView,
8342
+ inputViewOptions: {
8343
+ maxLength: 5000
8344
+ }
8345
+ };
8253
8346
  editor.fileTypes.register('image_files', {
8254
8347
  model: ImageFile,
8255
8348
  metaDataAttributes: ['dimensions', altMetaDataAttribute],
8256
8349
  matchUpload: /^image/,
8257
- configurationEditorInputs: [{
8258
- name: 'alt',
8259
- inputView: TextInputView
8260
- }]
8350
+ configurationEditorInputs: [altConfigurationEditorInput]
8261
8351
  });
8262
8352
  editor.fileTypes.register('video_files', {
8263
8353
  model: VideoFile,
8264
8354
  metaDataAttributes: ['format', 'dimensions', 'duration', textTracksMetaDataAttribute, altMetaDataAttribute],
8265
8355
  matchUpload: /^video/,
8266
- configurationEditorInputs: [{
8267
- name: 'alt',
8268
- inputView: TextInputView
8269
- }],
8356
+ configurationEditorInputs: [altConfigurationEditorInput],
8270
8357
  settingsDialogTabs: [textTracksSettingsDialogTab]
8271
8358
  });
8272
8359
  editor.fileTypes.register('audio_files', {
8273
8360
  model: AudioFile,
8274
8361
  metaDataAttributes: ['format', 'duration', textTracksMetaDataAttribute, altMetaDataAttribute],
8275
8362
  matchUpload: /^audio/,
8276
- configurationEditorInputs: [{
8277
- name: 'alt',
8278
- inputView: TextInputView
8279
- }],
8363
+ configurationEditorInputs: [altConfigurationEditorInput],
8280
8364
  settingsDialogTabs: [textTracksSettingsDialogTab]
8281
8365
  });
8282
8366
  editor.fileTypes.register('text_track_files', {
@@ -8370,12 +8454,13 @@ app.addInitializer(function (options) {
8370
8454
  state.pages = new PagesCollection(options.pages);
8371
8455
  state.chapters = new ChaptersCollection(options.chapters);
8372
8456
  state.storylines = new StorylinesCollection(options.storylines);
8457
+ state.site = new Site(options.site);
8373
8458
  state.entry = editor.createEntryModel(options, {
8374
8459
  widgets: widgets
8375
8460
  });
8376
- state.site = new Site(options.site);
8377
8461
  state.account = new Backbone.Model(options.account);
8378
8462
  widgets.subject = state.entry;
8463
+ widgets.setupConfigurationEditorTabViewGroups(ConfigurationEditorTabView.groups);
8379
8464
  state.storylineOrdering = new StorylineOrdering(state.storylines, state.pages);
8380
8465
  state.storylineOrdering.sort({
8381
8466
  silent: true
@@ -8428,7 +8513,6 @@ app.addInitializer(function () {
8428
8513
  })
8429
8514
  });
8430
8515
  });
8431
-
8432
8516
  editor.router = new SidebarRouter({
8433
8517
  controller: new SidebarController({
8434
8518
  region: app.sidebarRegion,
@@ -8451,12 +8535,14 @@ app.addInitializer(function () {
8451
8535
  });
8452
8536
  });
8453
8537
 
8454
- app.addInitializer(function () {
8538
+ app.addInitializer(function /* args */
8539
+ () {
8455
8540
  var context = this;
8456
8541
  var args = arguments;
8457
-
8458
8542
  _.each(editor.initializers, function (fn) {
8459
- fn.call(context, args);
8543
+ fn.call(context, _objectSpread2(_objectSpread2({}, args), {}, {
8544
+ entry: state.entry
8545
+ }));
8460
8546
  });
8461
8547
  });
8462
8548
 
@@ -8489,4 +8575,4 @@ app.addRegions({
8489
8575
  sidebarFooterRegion: 'sidebar .sidebar_footer_container'
8490
8576
  });
8491
8577
 
8492
- export { AudioFile, BackButtonDecoratorView, BackgroundImageEmbeddedView, BackgroundPositioningPreviewView, BackgroundPositioningSlidersView, BackgroundPositioningView, ChangeThemeDialogView, Chapter, ChapterConfiguration, ChapterPagesCollection, ChapterScaffold, ChaptersCollection, ChooseImporterView, Configuration, ConfirmEncodingView, ConfirmFileImportUploadView, ConfirmUploadView, ConfirmableFileItemView, DropDownButtonItemListView, DropDownButtonItemView, DropDownButtonView, EditConfigurationView, EditEntryView, EditFileView, EditLock, EditLockContainer, EditMetaDataView, EditWidgetView, EditWidgetsView, EditorApi, EditorView, EmulationModeButtonView, EncodedFile, EncodingConfirmation, Entry, EntryMetadata, EntryMetadataFileSelectionHandler, EntryPublication, EntryPublicationQuotaDecoratorView, ExplorerFileItemView, Failure, FileConfiguration, FileImport, FileInputView, FileItemView, FileMetaDataItemValueView, FileMetaDataItemView, FileProcessingStateDisplayView, FileReuse, FileSettingsDialogView, FileStage, FileStageItemView, FileThumbnailView, FileTypes, FileTypesCollection, FileUploader, FilesCollection, FilesExplorerView, FilesImporterView, FilesView, FilteredFilesView, ForeignKeySubsetCollection, HelpButtonView, HelpImageView, HelpView, ImageFile, InfoBoxView, InvalidNestedTypeError, LazyVideoEmbeddedView, ListItemView, ListView, LoadingView, LockedView, ModelThumbnailView, NestedFilesCollection, NestedFilesView, NestedTypeError, NotificationsView, OrderedPageLinksCollection, OtherEntriesCollection, OtherEntriesCollectionView, OtherEntry, OtherEntryItemView, Page, PageConfigurationFileSelectionHandler, PageLink, PageLinkConfigurationEditorView, PageLinkFileSelectionHandler, PageLinkItemView, PageLinksCollection, PageLinksView, PageThumbnailView, PagesCollection, PublishEntryView, ReferenceInputView, ReusableFile, Scaffold, ScrollingView, SelectButtonView, SidebarController, SidebarFooterView, SidebarRouter, Site, StaticThumbnailView, Storyline, StorylineChaptersCollection, StorylineConfiguration, StorylineOrdering, StorylineScaffold, StorylineTransitiveChildPages, StorylinesCollection, SubsetCollection, TextFileMetaDataItemValueView, TextTrackFile, TextTracksFileMetaDataItemValueView, TextTracksView, Theme, ThemeInputView, ThemeItemView, ThemesCollection, UnmatchedUploadError, UploadError, UploadableFile, UploadableFilesView, UploaderView, VideoFile, Widget, WidgetConfiguration, WidgetConfigurationFileSelectionHandler, WidgetItemView, WidgetTypes, WidgetsCollection, addAndReturnModel, app, authenticationProvider, configurationContainer, delayedDestroying, dialogView, editor, entryTypeEditorControllerUrls, failureIndicatingView, failureTracking, fileWithType, filesCountWatcher, formDataUtils, loadable, modelLifecycleTrackingView, orderedCollection, persistedPromise, polling, retryable, selectableView, stageProvider, startEditor, state, stylesheet, transientReferences, validFileTypeTranslationList };
8578
+ export { AudioFile, BackButtonDecoratorView, BackgroundImageEmbeddedView, BackgroundPositioningPreviewView, BackgroundPositioningSlidersView, BackgroundPositioningView, ChangeThemeDialogView, Chapter, ChapterConfiguration, ChapterPagesCollection, ChapterScaffold, ChaptersCollection, ChooseImporterView, Configuration, ConfirmEncodingView, ConfirmFileImportUploadView, ConfirmUploadView, ConfirmableFileItemView, DropDownButtonItemListView, DropDownButtonItemView, DropDownButtonView, EditConfigurationView, EditEntryView, EditFileView, EditLock, EditLockContainer, EditMetaDataView, EditWidgetView, EditWidgetsView, EditorApi, EditorView, EmulationModeButtonView, EncodedFile, EncodingConfirmation, Entry, EntryMetadata, EntryMetadataFileSelectionHandler, EntryPublication, EntryPublicationQuotaDecoratorView, ExplorerFileItemView, Failure, FileConfiguration, FileImport, FileInputView, FileItemView, FileMetaDataItemValueView, FileMetaDataItemView, FileProcessingStateDisplayView, FileReuse, FileSettingsDialogView, FileStage, FileStageItemView, FileThumbnailView, FileTypes, FileTypesCollection, FileUploader, FilesCollection, FilesExplorerView, FilesImporterView, FilesView, FilteredFilesView, ForeignKeySubsetCollection, HelpButtonView, HelpImageView, HelpView, ImageFile, InfoBoxView, InvalidNestedTypeError, LazyVideoEmbeddedView, ListHighlight, ListItemView, ListView, LoadingView, LockedView, ModelThumbnailView, NestedFilesCollection, NestedFilesView, NestedTypeError, NotificationsView, OrderedPageLinksCollection, OtherEntriesCollection, OtherEntriesCollectionView, OtherEntry, OtherEntryItemView, Page, PageConfigurationFileSelectionHandler, PageLink, PageLinkConfigurationEditorView, PageLinkFileSelectionHandler, PageLinkItemView, PageLinksCollection, PageLinksView, PageThumbnailView, PagesCollection, PublishEntryView, ReferenceInputView, ReusableFile, Scaffold, ScrollingView, Search, SelectButtonView, SidebarController, SidebarFooterView, SidebarRouter, Site, StaticThumbnailView, Storyline, StorylineChaptersCollection, StorylineConfiguration, StorylineOrdering, StorylineScaffold, StorylineTransitiveChildPages, StorylinesCollection, SubsetCollection, TextFileMetaDataItemValueView, TextTrackFile, TextTracksFileMetaDataItemValueView, TextTracksView, Theme, ThemeInputView, ThemeItemView, ThemesCollection, UnmatchedUploadError, UploadError, UploadableFile, UploadableFilesView, UploaderView, VideoFile, Widget, WidgetConfiguration, WidgetConfigurationFileSelectionHandler, WidgetItemView, WidgetTypes, WidgetsCollection, addAndReturnModel, app, authenticationProvider, configurationContainer, delayedDestroying, dialogView, editor, entryTypeEditorControllerUrls, failureIndicatingView, failureTracking, fileWithType, filesCountWatcher, formDataUtils, loadable, modelLifecycleTrackingView, orderedCollection, persistedPromise, polling, retryable, selectableView, stageProvider, startEditor, state, stylesheet, transientReferences, validFileTypeTranslationList };