pageflow 17.1.0 → 17.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (302) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +319 -345
  3. data/README.md +3 -4
  4. data/admins/pageflow/accounts.rb +6 -6
  5. data/admins/pageflow/entry.rb +10 -8
  6. data/admins/pageflow/membership.rb +11 -11
  7. data/admins/pageflow/translations.rb +5 -5
  8. data/app/assets/images/pageflow/admin/icons/comment.svg +1 -0
  9. data/app/assets/javascripts/pageflow/dist/ui.js +1350 -630
  10. data/app/assets/javascripts/pageflow/editor/vendor.js +0 -1
  11. data/app/assets/javascripts/pageflow/ui.js +0 -1
  12. data/app/assets/stylesheets/pageflow/admin/entry_comments_indicator.scss +45 -0
  13. data/app/assets/stylesheets/pageflow/admin/permalink_input.scss +8 -1
  14. data/app/assets/stylesheets/pageflow/admin.scss +1 -0
  15. data/app/assets/stylesheets/pageflow/animations/spin.scss +9 -0
  16. data/app/assets/stylesheets/pageflow/animations.scss +1 -0
  17. data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +21 -5
  18. data/app/assets/stylesheets/pageflow/editor/base.scss +9 -1
  19. data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +13 -1
  20. data/app/assets/stylesheets/pageflow/editor/edit_configuration_view.scss +5 -0
  21. data/app/assets/stylesheets/pageflow/editor/file_folders.scss +127 -0
  22. data/app/assets/stylesheets/pageflow/editor/file_meta_data.scss +0 -1
  23. data/app/assets/stylesheets/pageflow/editor/file_meta_data_overlay.scss +45 -0
  24. data/app/assets/stylesheets/pageflow/editor/file_preview.scss +138 -0
  25. data/app/assets/stylesheets/pageflow/editor/file_references.scss +68 -0
  26. data/app/assets/stylesheets/pageflow/editor/file_settings_dialog.scss +1 -1
  27. data/app/assets/stylesheets/pageflow/editor/file_stage_icons.scss +17 -0
  28. data/app/assets/stylesheets/pageflow/editor/file_stages.scss +25 -32
  29. data/app/assets/stylesheets/pageflow/editor/file_thumbnails.scss +31 -48
  30. data/app/assets/stylesheets/pageflow/editor/file_type_pills.scss +73 -0
  31. data/app/assets/stylesheets/pageflow/editor/files.scss +230 -50
  32. data/app/assets/stylesheets/pageflow/editor/filtered_files.scss +142 -14
  33. data/app/assets/stylesheets/pageflow/editor/folder_breadcrumb.scss +40 -0
  34. data/app/assets/stylesheets/pageflow/editor/info_box.scss +12 -0
  35. data/app/assets/stylesheets/pageflow/editor/inputs/edit_defaults_button.scss +15 -0
  36. data/app/assets/stylesheets/pageflow/editor/inputs.scss +1 -0
  37. data/app/assets/stylesheets/pageflow/editor/list.scss +88 -38
  38. data/app/assets/stylesheets/pageflow/editor/manage_files.scss +9 -0
  39. data/app/assets/stylesheets/pageflow/editor/menu.scss +14 -0
  40. data/app/assets/stylesheets/pageflow/editor/move_to_folder_dialog.scss +83 -0
  41. data/app/assets/stylesheets/pageflow/mixins/pageflow.scss +9 -5
  42. data/app/assets/stylesheets/pageflow/ui/color_picker.scss +230 -0
  43. data/app/assets/stylesheets/pageflow/ui/forms.scss +34 -4
  44. data/app/assets/stylesheets/pageflow/ui/functions.scss +12 -2
  45. data/app/assets/stylesheets/pageflow/ui/input/color_input.scss +9 -16
  46. data/app/assets/stylesheets/pageflow/ui/input/text_area_input.scss +15 -0
  47. data/app/assets/stylesheets/pageflow/ui/overrides.scss +8 -0
  48. data/app/assets/stylesheets/pageflow/ui/properties.scss +29 -0
  49. data/app/assets/stylesheets/pageflow/ui/tabs_view.scss +8 -3
  50. data/app/assets/stylesheets/pageflow/ui.scss +4 -2
  51. data/app/controllers/pageflow/editor/entries_controller.rb +1 -0
  52. data/app/controllers/pageflow/editor/file_folders_controller.rb +80 -0
  53. data/app/controllers/pageflow/editor/file_import_controller.rb +1 -0
  54. data/app/controllers/pageflow/editor/files_controller.rb +15 -6
  55. data/app/controllers/pageflow/editor/oembeds_controller.rb +21 -0
  56. data/app/controllers/pageflow/entries_controller.rb +17 -13
  57. data/app/controllers/pageflow/review/comment_thread_reads_controller.rb +30 -0
  58. data/app/controllers/pageflow/review/comment_threads_controller.rb +60 -0
  59. data/app/controllers/pageflow/review/comments_controller.rb +53 -0
  60. data/app/controllers/pageflow/revisions_controller.rb +4 -1
  61. data/app/helpers/pageflow/admin/entries_helper.rb +40 -0
  62. data/app/helpers/pageflow/admin/widgets_helper.rb +2 -2
  63. data/app/helpers/pageflow/common_entry_seed_helper.rb +5 -3
  64. data/app/helpers/pageflow/editor/config_helper.rb +3 -1
  65. data/app/helpers/pageflow/entries_helper.rb +24 -42
  66. data/app/helpers/pageflow/entry_json_seed_helper.rb +4 -4
  67. data/app/helpers/pageflow/public_i18n_helper.rb +9 -8
  68. data/app/helpers/pageflow/structured_data_helper.rb +4 -1
  69. data/app/helpers/pageflow/themes_helper.rb +1 -3
  70. data/app/helpers/pageflow/widgets_helper.rb +2 -1
  71. data/app/inputs/pageflow_permalink_input.rb +6 -2
  72. data/app/models/concerns/pageflow/auto_generated_perma_id.rb +11 -4
  73. data/app/models/concerns/pageflow/reusable_file.rb +1 -1
  74. data/app/models/concerns/pageflow/serialization_blacklist.rb +2 -4
  75. data/app/models/concerns/pageflow/translated_attributes.rb +101 -0
  76. data/app/models/concerns/pageflow/uploadable_file.rb +1 -1
  77. data/app/models/pageflow/comment.rb +30 -0
  78. data/app/models/pageflow/comment_thread.rb +65 -0
  79. data/app/models/pageflow/comment_thread_read.rb +22 -0
  80. data/app/models/pageflow/customized_theme.rb +3 -2
  81. data/app/models/pageflow/draft_entry.rb +20 -8
  82. data/app/models/pageflow/entry.rb +2 -0
  83. data/app/models/pageflow/entry_at_revision.rb +9 -1
  84. data/app/models/pageflow/entry_comment_summary.rb +114 -0
  85. data/app/models/pageflow/file_folder.rb +74 -0
  86. data/app/models/pageflow/file_reuse.rb +3 -2
  87. data/app/models/pageflow/file_usage.rb +8 -0
  88. data/app/models/pageflow/image_file.rb +24 -20
  89. data/app/models/pageflow/image_file_url_templates.rb +3 -3
  90. data/app/models/pageflow/legal_info.rb +38 -0
  91. data/app/models/pageflow/nested_revision_component_copy.rb +1 -1
  92. data/app/models/pageflow/permalink.rb +1 -0
  93. data/app/models/pageflow/revision.rb +6 -2
  94. data/app/models/pageflow/site.rb +10 -0
  95. data/app/models/pageflow/used_file.rb +8 -2
  96. data/app/models/pageflow/widget.rb +2 -2
  97. data/app/views/pageflow/audio_files/_audio_file.json.jbuilder +0 -1
  98. data/app/views/pageflow/editor/config/_seeds.json.jbuilder +4 -2
  99. data/app/views/pageflow/editor/entries/_entry.json.jbuilder +2 -1
  100. data/app/views/pageflow/editor/entries/seed.json.erb +1 -0
  101. data/app/views/pageflow/editor/file_folders/_file_folder.json.jbuilder +5 -0
  102. data/app/views/pageflow/editor/file_folders/create.json.jbuilder +1 -0
  103. data/app/views/pageflow/editor/file_folders/index.json.jbuilder +3 -0
  104. data/app/views/pageflow/editor/files/_file.json.jbuilder +2 -1
  105. data/app/views/pageflow/editor/image_files/_image_file.json.jbuilder +3 -0
  106. data/app/views/pageflow/editor/video_files/_video_file.json.jbuilder +3 -0
  107. data/app/views/pageflow/files/_file.json.jbuilder +2 -0
  108. data/app/views/pageflow/image_files/_image_file.json.jbuilder +0 -1
  109. data/app/views/pageflow/review/comment_threads/_comment_thread.json.jbuilder +20 -0
  110. data/app/views/pageflow/review/comment_threads/create.json.jbuilder +2 -0
  111. data/app/views/pageflow/review/comment_threads/index.json.jbuilder +13 -0
  112. data/app/views/pageflow/review/comments/_comment.json.jbuilder +13 -0
  113. data/app/views/pageflow/review/comments/create.json.jbuilder +1 -0
  114. data/app/views/pageflow/structured_data/_entry.json.jbuilder +2 -3
  115. data/app/views/pageflow/video_files/_video_file.json.jbuilder +0 -1
  116. data/config/locales/de.yml +152 -14
  117. data/config/locales/en.yml +150 -14
  118. data/config/routes.rb +14 -0
  119. data/db/migrate/20140418225525_setup_schema.rb +2 -2
  120. data/db/migrate/20251015000000_add_structured_data_type_name_to_revisions.rb +5 -0
  121. data/db/migrate/20251029142849_convert_legacy_social_embeds_to_unified_social_embed.rb +38 -0
  122. data/db/migrate/20251119083347_fix_main_storyline_positions.rb +16 -0
  123. data/db/migrate/20251119083348_ensure_scrolled_entries_have_excursion_storyline.rb +15 -0
  124. data/db/migrate/20260303000000_add_layout_version_to_revisions.rb +6 -0
  125. data/db/migrate/20260323000000_create_comment_threads_and_comments.rb +30 -0
  126. data/db/migrate/20260414000000_add_subject_range_to_comment_threads.rb +5 -0
  127. data/db/migrate/20260415000000_add_section_perma_id_to_comment_threads.rb +5 -0
  128. data/db/migrate/20260727000000_add_quote_to_comments.rb +5 -0
  129. data/db/migrate/20260728000000_create_file_folders.rb +17 -0
  130. data/db/migrate/20260805000000_add_attribute_translations_to_sites.rb +5 -0
  131. data/db/migrate/20260806000000_add_edited_at_to_comments.rb +5 -0
  132. data/db/migrate/20260810000000_create_pageflow_scrolled_lottie_files.rb +24 -0
  133. data/db/migrate/20260817000000_create_comment_thread_reads.rb +16 -0
  134. data/db/migrate/20260819000000_add_unread_comments_since_at_to_users.rb +18 -0
  135. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +8665 -3890
  136. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +979 -3571
  137. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +20199 -28120
  138. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +18652 -26584
  139. data/entry_types/scrolled/app/assets/stylesheets/pageflow_scrolled/ui.scss +2 -0
  140. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/content_elements_controller.rb +33 -3
  141. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/sections_controller.rb +2 -3
  142. data/entry_types/scrolled/app/controllers/pageflow_scrolled/entries_controller.rb +21 -1
  143. data/entry_types/scrolled/app/helpers/pageflow_scrolled/cache_helper.rb +3 -3
  144. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/entry_json_seed_helper.rb +7 -5
  145. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +5 -2
  146. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +13 -5
  147. data/entry_types/scrolled/app/helpers/pageflow_scrolled/i18n_helper.rb +28 -13
  148. data/entry_types/scrolled/app/helpers/pageflow_scrolled/packs_helper.rb +26 -11
  149. data/entry_types/scrolled/app/helpers/pageflow_scrolled/react_server_side_rendering_helper.rb +2 -2
  150. data/entry_types/scrolled/app/helpers/pageflow_scrolled/sprockets_helper.rb +16 -0
  151. data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +174 -3
  152. data/entry_types/scrolled/app/models/pageflow_scrolled/content_element.rb +49 -7
  153. data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file.rb +6 -0
  154. data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file_url_templates.rb +16 -0
  155. data/entry_types/scrolled/app/models/pageflow_scrolled/storyline.rb +1 -0
  156. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +13 -12
  157. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +30 -10
  158. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_theme.json.jbuilder +9 -0
  159. data/entry_types/scrolled/app/views/pageflow_scrolled/favicons/_entry.html.erb +1 -1
  160. data/entry_types/scrolled/config/configuration_schemas/pageflow-scrolled.json +414 -0
  161. data/entry_types/scrolled/config/locales/de.yml +512 -65
  162. data/entry_types/scrolled/config/locales/en.yml +518 -52
  163. data/entry_types/scrolled/lib/pageflow_scrolled/additional_seed_data.rb +2 -2
  164. data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +51 -6
  165. data/entry_types/scrolled/lib/pageflow_scrolled/configuration_schemas.rb +91 -0
  166. data/entry_types/scrolled/lib/pageflow_scrolled/engine.rb +2 -1
  167. data/entry_types/scrolled/lib/pageflow_scrolled/entry_file_reference_locations.rb +71 -0
  168. data/entry_types/scrolled/lib/pageflow_scrolled/entry_structured_data_types/faq_page.rb +58 -0
  169. data/entry_types/scrolled/lib/pageflow_scrolled/file_reference_locations.rb +69 -0
  170. data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +205 -22
  171. data/entry_types/scrolled/lib/pageflow_scrolled/react_widget_type.rb +2 -4
  172. data/entry_types/scrolled/lib/pageflow_scrolled/seeds.rb +56 -36
  173. data/entry_types/scrolled/lib/pageflow_scrolled/theme_options_default_scale.rb +35 -0
  174. data/entry_types/scrolled/lib/pageflow_scrolled.rb +13 -0
  175. data/entry_types/scrolled/lib/tasks/pageflow_scrolled/storybook.rake +19 -1
  176. data/entry_types/scrolled/package/config/configurationSchemas.js +70 -0
  177. data/entry_types/scrolled/package/config/rollup/configurationSchemas.js +19 -0
  178. data/entry_types/scrolled/package/config/webpack.js +72 -0
  179. data/entry_types/scrolled/package/contentElements/hotspots-frontend.css +1 -1
  180. data/entry_types/scrolled/package/contentElements/hotspots-frontend.js +1181 -915
  181. data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.css +1 -0
  182. data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.js +219 -0
  183. data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.css +1 -0
  184. data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.js +244 -0
  185. data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.css +1 -0
  186. data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.js +823 -0
  187. data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.js +71 -21
  188. data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.css +1 -0
  189. data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.js +277 -0
  190. data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.css +1 -0
  191. data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.js +244 -0
  192. data/entry_types/scrolled/package/contentElements-editor.js +1915 -1097
  193. data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
  194. data/entry_types/scrolled/package/contentElements-frontend.js +3093 -1679
  195. data/entry_types/scrolled/package/editor.css +1 -0
  196. data/entry_types/scrolled/package/editor.js +8667 -4077
  197. data/entry_types/scrolled/package/entryState.js +1600 -0
  198. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-20c600de.js +1979 -0
  199. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-58189220.js +2538 -0
  200. data/entry_types/scrolled/package/frontend/{PhonePlatformContext-9f76033e.js → PhonePlatformContext-035a99fa.js} +7 -12
  201. data/entry_types/scrolled/package/frontend/PhonePlatformContext-9da43797.js +33 -0
  202. data/entry_types/scrolled/package/frontend/Placeholder.module-bf0966b4.js +5267 -0
  203. data/entry_types/scrolled/package/frontend/Placeholder.module-d22a9335.js +4180 -0
  204. data/entry_types/scrolled/package/frontend/ThemeIcon-ab834849.js +754 -0
  205. data/entry_types/scrolled/package/frontend/ThemeIcon-f6cb3a8f.js +773 -0
  206. data/entry_types/scrolled/package/frontend/{ToggleFullscreenCornerButton-788e9cdb.js → ToggleFullscreenCornerButton-30fd1124.js} +6 -6
  207. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-61c25224.js +72 -0
  208. data/entry_types/scrolled/package/frontend/Viewer-1074fd59.js +147 -0
  209. data/entry_types/scrolled/package/frontend/Viewer-1482ca34.js +480 -0
  210. data/entry_types/scrolled/package/frontend/Viewer-70504231.js +210 -0
  211. data/entry_types/scrolled/package/frontend/{Viewer-0345ce57.js → Viewer-cc8d4072.js} +84 -92
  212. data/entry_types/scrolled/package/frontend/Wavesurfer-656e3c36.js +302 -0
  213. data/entry_types/scrolled/package/frontend/Wavesurfer-b753b838.js +385 -0
  214. data/entry_types/scrolled/package/frontend/commenting.css +1 -0
  215. data/entry_types/scrolled/package/frontend/extensions-167ae143.js +1455 -0
  216. data/entry_types/scrolled/package/frontend/extensions-2318f92b.js +5918 -0
  217. data/entry_types/scrolled/package/frontend/extensions-a461fd13.js +4890 -0
  218. data/entry_types/scrolled/package/frontend/extensions-c98269fd.js +1982 -0
  219. data/entry_types/scrolled/package/frontend/i18n-493cd2a6.js +93 -0
  220. data/entry_types/scrolled/package/frontend/i18n-e06cac14.js +100 -0
  221. data/entry_types/scrolled/package/frontend/{index-eb670c2f.js → index-2e5546f7.js} +36 -44
  222. data/entry_types/scrolled/package/frontend/index-de2b418d.js +130 -0
  223. data/entry_types/scrolled/package/frontend/index.css +1 -1
  224. data/entry_types/scrolled/package/frontend/index.js +1870 -875
  225. data/entry_types/scrolled/package/frontend/inlineEditing.css +1 -0
  226. data/entry_types/scrolled/package/frontend/useContentElementEditorState-a084912e.js +25 -0
  227. data/entry_types/scrolled/package/frontend/useDelayedBoolean-a387d85b.js +24 -0
  228. data/entry_types/scrolled/package/frontend/{usePhonePlatform-c2ba875d.js → usePhonePlatform-4ecb96d4.js} +1 -1
  229. data/entry_types/scrolled/package/frontend/usePhonePlatform-ce9ff08d.js +8 -0
  230. data/entry_types/scrolled/package/frontend-server.js +20 -209
  231. data/entry_types/scrolled/package/package.json +5 -3
  232. data/entry_types/scrolled/package/review.css +1 -0
  233. data/entry_types/scrolled/package/review.js +3306 -0
  234. data/entry_types/scrolled/package/testHelpers.js +836 -359
  235. data/entry_types/scrolled/package/widgets/consentBar.css +1 -1
  236. data/entry_types/scrolled/package/widgets/consentBar.js +269 -290
  237. data/entry_types/scrolled/package/widgets/defaultNavigation.css +2 -2
  238. data/entry_types/scrolled/package/widgets/defaultNavigation.js +911 -352
  239. data/entry_types/scrolled/package/widgets/excursionSheet.css +1 -1
  240. data/entry_types/scrolled/package/widgets/excursionSheet.js +87 -124
  241. data/entry_types/scrolled/package/widgets/iconInlineFileRights.css +1 -1
  242. data/entry_types/scrolled/package/widgets/iconInlineFileRights.js +62 -29
  243. data/entry_types/scrolled/package/widgets/iconScrollIndicator.js +32 -13
  244. data/entry_types/scrolled/package/widgets/mainStorylineSheet.css +1 -1
  245. data/entry_types/scrolled/package/widgets/mainStorylineSheet.js +56 -100
  246. data/entry_types/scrolled/package/widgets/textInlineFileRights.css +1 -1
  247. data/entry_types/scrolled/package/widgets/textInlineFileRights.js +33 -28
  248. data/entry_types/scrolled/package/widgets-server.js +1 -0
  249. data/entry_types/scrolled/spec/factories/lottie_files.rb +29 -0
  250. data/entry_types/scrolled/spec/fixtures/animation.lottie +0 -0
  251. data/entry_types/scrolled/spec/fixtures/font.woff +0 -0
  252. data/entry_types/scrolled/spec/fixtures/font.woff2 +0 -0
  253. data/lib/generators/pageflow/resque/resque_generator.rb +0 -2
  254. data/lib/pageflow/configuration.rb +49 -7
  255. data/lib/pageflow/deprecation.rb +19 -0
  256. data/lib/pageflow/engine.rb +1 -1
  257. data/lib/pageflow/entries_controller_env_helper.rb +32 -2
  258. data/lib/pageflow/entry_export_import/entry_serialization.rb +4 -4
  259. data/lib/pageflow/entry_export_import/page_type_versions.rb +2 -2
  260. data/lib/pageflow/entry_export_import/revision_serialization/import.rb +10 -0
  261. data/lib/pageflow/entry_export_import/revision_serialization.rb +3 -0
  262. data/lib/pageflow/entry_structured_data_types.rb +43 -0
  263. data/lib/pageflow/entry_type_configuration.rb +1 -0
  264. data/lib/pageflow/file_type.rb +2 -2
  265. data/lib/pageflow/file_types.rb +1 -1
  266. data/lib/pageflow/oembed_providers.rb +42 -0
  267. data/lib/pageflow/primary_domain_entry_redirect.rb +6 -0
  268. data/lib/pageflow/rails_version.rb +13 -2
  269. data/lib/pageflow/themes.rb +41 -0
  270. data/lib/pageflow/user_mixin.rb +12 -0
  271. data/lib/pageflow/version.rb +1 -1
  272. data/lib/pageflow.rb +1 -0
  273. data/package/config/eslint-rules/documented-in-toc.js +125 -0
  274. data/package/config/jest/index.js +0 -1
  275. data/package/config/webpack.js +0 -1
  276. data/package/config/webpack5.js +0 -1
  277. data/package/editor.js +4378 -1897
  278. data/package/frontend.js +647 -886
  279. data/package/package.json +2 -1
  280. data/package/review.js +384 -0
  281. data/package/testHelpers.js +248 -188
  282. data/package/ui.js +1346 -630
  283. data/package/vendor/stubs/wysihtml5.js +2 -0
  284. data/spec/factories/comment_thread_reads.rb +10 -0
  285. data/spec/factories/comment_threads.rb +10 -0
  286. data/spec/factories/comments.rb +9 -0
  287. data/spec/factories/file_folders.rb +8 -0
  288. data/spec/factories/published_entries.rb +2 -0
  289. metadata +188 -67
  290. data/app/assets/stylesheets/pageflow/editor/select_button.scss +0 -51
  291. data/entry_types/scrolled/app/helpers/pageflow_scrolled/generated_media_queries_helper.rb +0 -55
  292. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-51914be7.js +0 -5439
  293. data/entry_types/scrolled/package/frontend/ThemeIcon-81f2f066.js +0 -1932
  294. data/entry_types/scrolled/package/frontend/Viewer-cdc549cc.js +0 -147
  295. data/entry_types/scrolled/package/frontend/Wavesurfer-1cdc3925.js +0 -313
  296. data/entry_types/scrolled/package/frontend/components-96660ffd.js +0 -3803
  297. data/entry_types/scrolled/package/frontend/inherits-539844a6.js +0 -99
  298. data/entry_types/scrolled/package/frontend/useContentElementEditorState-77fe6c79.js +0 -51
  299. data/entry_types/scrolled/package/widgets/excursionDialog.css +0 -1
  300. data/entry_types/scrolled/package/widgets/excursionDialog.js +0 -109
  301. data/lib/generators/pageflow/resque/templates/resque_enqueue_after_commit_patch.rb +0 -25
  302. data/package/vendor/jquery.minicolors.js +0 -1108
data/package/ui.js CHANGED
@@ -6,7 +6,6 @@ import Backbone from 'backbone';
6
6
  import Sortable from 'sortablejs';
7
7
  import ChildViewContainer from 'backbone.babysitter';
8
8
  import IScroll from 'iscroll';
9
- import 'jquery.minicolors';
10
9
  import wysihtml5 from 'wysihtml5';
11
10
  import 'jquery-ui';
12
11
  import Cocktail from 'cocktail';
@@ -142,64 +141,6 @@ var i18nUtils = /*#__PURE__*/Object.freeze({
142
141
  translationKeysWithSuffix: translationKeysWithSuffix
143
142
  });
144
143
 
145
- function _arrayWithHoles(arr) {
146
- if (Array.isArray(arr)) return arr;
147
- }
148
-
149
- function _iterableToArrayLimit(arr, i) {
150
- if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
151
- var _arr = [];
152
- var _n = true;
153
- var _d = false;
154
- var _e = undefined;
155
-
156
- try {
157
- for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
158
- _arr.push(_s.value);
159
-
160
- if (i && _arr.length === i) break;
161
- }
162
- } catch (err) {
163
- _d = true;
164
- _e = err;
165
- } finally {
166
- try {
167
- if (!_n && _i["return"] != null) _i["return"]();
168
- } finally {
169
- if (_d) throw _e;
170
- }
171
- }
172
-
173
- return _arr;
174
- }
175
-
176
- function _arrayLikeToArray(arr, len) {
177
- if (len == null || len > arr.length) len = arr.length;
178
-
179
- for (var i = 0, arr2 = new Array(len); i < len; i++) {
180
- arr2[i] = arr[i];
181
- }
182
-
183
- return arr2;
184
- }
185
-
186
- function _unsupportedIterableToArray(o, minLen) {
187
- if (!o) return;
188
- if (typeof o === "string") return _arrayLikeToArray(o, minLen);
189
- var n = Object.prototype.toString.call(o).slice(8, -1);
190
- if (n === "Object" && o.constructor) n = o.constructor.name;
191
- if (n === "Map" || n === "Set") return Array.from(n);
192
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
193
- }
194
-
195
- function _nonIterableRest() {
196
- throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
197
- }
198
-
199
- function _slicedToArray(arr, i) {
200
- return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
201
- }
202
-
203
144
  /**
204
145
  * Create object that can be passed to Marionette ui property from CSS
205
146
  * module object.
@@ -239,11 +180,8 @@ function _slicedToArray(arr, i) {
239
180
  *
240
181
  * @memberof cssModulesUtils
241
182
  */
242
- function ui(styles) {
243
- for (var _len = arguments.length, classNames = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
244
- classNames[_key - 1] = arguments[_key];
245
- }
246
- return classNames.reduce(function (result, className) {
183
+ function ui(styles, ...classNames) {
184
+ return classNames.reduce((result, className) => {
247
185
  result[className] = selector(styles, className);
248
186
  return result;
249
187
  }, {});
@@ -287,12 +225,9 @@ function ui(styles) {
287
225
  * @memberof cssModulesUtils
288
226
  */
289
227
  function events(styles, mapping) {
290
- return Object.keys(mapping).reduce(function (result, key) {
291
- var _key$split = key.split(' '),
292
- _key$split2 = _slicedToArray(_key$split, 2),
293
- event = _key$split2[0],
294
- className = _key$split2[1];
295
- result["".concat(event, " ").concat(selector(styles, className))] = mapping[key];
228
+ return Object.keys(mapping).reduce((result, key) => {
229
+ const [event, className] = key.split(' ');
230
+ result[`${event} ${selector(styles, className)}`] = mapping[key];
296
231
  return result;
297
232
  }, {});
298
233
  }
@@ -310,11 +245,11 @@ function events(styles, mapping) {
310
245
  * @memberof cssModulesUtils
311
246
  */
312
247
  function selector(styles, className) {
313
- var classNames = styles[className];
248
+ const classNames = styles[className];
314
249
  if (!classNames) {
315
- throw new Error("Unknown class name ".concat(className, " in mapping. Knwon names: ").concat(Object.keys(styles).join(', '), "."));
250
+ throw new Error(`Unknown class name ${className} in mapping. Knwon names: ${Object.keys(styles).join(', ')}.`);
316
251
  }
317
- return ".".concat(classNames.replace(/ /g, '.'));
252
+ return `.${classNames.replace(/ /g, '.')}`;
318
253
  }
319
254
 
320
255
  var cssModulesUtils = /*#__PURE__*/Object.freeze({
@@ -324,6 +259,56 @@ var cssModulesUtils = /*#__PURE__*/Object.freeze({
324
259
  selector: selector
325
260
  });
326
261
 
262
+ function setup({
263
+ binding,
264
+ model,
265
+ listener,
266
+ callback,
267
+ normalize = value => value,
268
+ option,
269
+ bindingValue
270
+ }) {
271
+ if (binding) {
272
+ _.flatten([binding]).forEach(attribute => {
273
+ listener.listenTo(model, 'change:' + attribute, update);
274
+ });
275
+ }
276
+ update();
277
+ function update() {
278
+ callback(resolve({
279
+ binding,
280
+ model,
281
+ normalize,
282
+ option,
283
+ bindingValue
284
+ }));
285
+ }
286
+ }
287
+ function resolve({
288
+ binding,
289
+ model,
290
+ normalize = value => value,
291
+ option,
292
+ bindingValue
293
+ }) {
294
+ const boundValue = Array.isArray(binding) ? binding.map(attribute => model.get(attribute)) : model.get(binding);
295
+ if (bindingValue !== undefined) {
296
+ return boundValue === bindingValue;
297
+ } else if (typeof option === 'function') {
298
+ return normalize(option(boundValue));
299
+ } else if (option !== undefined) {
300
+ return normalize(option);
301
+ } else if (binding) {
302
+ return normalize(boundValue);
303
+ }
304
+ }
305
+
306
+ var attributeBindingUtils = /*#__PURE__*/Object.freeze({
307
+ __proto__: null,
308
+ setup: setup,
309
+ resolve: resolve
310
+ });
311
+
327
312
  // Class-y constructor by github.com/opensas
328
313
  // https://github.com/jashkenas/backbone/issues/2601
329
314
 
@@ -331,30 +316,29 @@ function BaseObject(options) {
331
316
  this.initialize.apply(this, arguments);
332
317
  }
333
318
  _.extend(BaseObject.prototype, Backbone.Events, {
334
- initialize: function initialize(options) {}
319
+ initialize: function (options) {}
335
320
  });
336
321
 
337
322
  // The self-propagating extend function that Backbone classes use.
338
323
  BaseObject.extend = Backbone.Model.extend;
339
324
 
340
- var serverSideValidation = {
341
- initialize: function initialize() {
342
- var _this = this;
325
+ const serverSideValidation = {
326
+ initialize() {
343
327
  this.validationErrors = {};
344
- this.listenTo(this, 'error', function (model, request) {
328
+ this.listenTo(this, 'error', (model, request) => {
345
329
  if (request.status === 422) {
346
- _this.validationErrors = JSON.parse(request.responseText).errors;
347
- _this.trigger('invalid');
330
+ this.validationErrors = JSON.parse(request.responseText).errors;
331
+ this.trigger('invalid');
348
332
  }
349
333
  });
350
- this.listenTo(this, 'sync', function () {
351
- _this.validationErrors = {};
334
+ this.listenTo(this, 'sync', () => {
335
+ this.validationErrors = {};
352
336
  });
353
337
  }
354
338
  };
355
339
 
356
- var CollectionView = Marionette.View.extend({
357
- initialize: function initialize() {
340
+ const CollectionView = Marionette.View.extend({
341
+ initialize: function () {
358
342
  this.rendered = false;
359
343
  this.itemViews = new ChildViewContainer();
360
344
  this.collection.map(this.addItem, this);
@@ -372,7 +356,7 @@ var CollectionView = Marionette.View.extend({
372
356
  });
373
357
  }
374
358
  },
375
- render: function render() {
359
+ render: function () {
376
360
  if (!this.rendered) {
377
361
  this.$el.append(this.itemViews.map(function (itemView) {
378
362
  itemView.$el.data('view', itemView);
@@ -383,11 +367,11 @@ var CollectionView = Marionette.View.extend({
383
367
  }
384
368
  return this;
385
369
  },
386
- onClose: function onClose() {
370
+ onClose: function () {
387
371
  this.itemViews.call('close');
388
372
  this.closePlaceHolderView();
389
373
  },
390
- addItem: function addItem(item) {
374
+ addItem: function (item) {
391
375
  var view = new this.options.itemViewConstructor(_.extend({
392
376
  model: item
393
377
  }, this.getItemViewOptions(item)));
@@ -404,7 +388,7 @@ var CollectionView = Marionette.View.extend({
404
388
  this.togglePlaceHolder();
405
389
  }
406
390
  },
407
- removeItem: function removeItem(item) {
391
+ removeItem: function (item) {
408
392
  var view = this.itemViews.findByModel(item);
409
393
  if (view) {
410
394
  this.itemViews.remove(view);
@@ -412,7 +396,7 @@ var CollectionView = Marionette.View.extend({
412
396
  this.togglePlaceHolder();
413
397
  }
414
398
  },
415
- sort: function sort() {
399
+ sort: function () {
416
400
  var last = null;
417
401
  this.collection.each(function (item) {
418
402
  var itemView = this.itemViews.findByModel(item);
@@ -429,31 +413,36 @@ var CollectionView = Marionette.View.extend({
429
413
  last = element;
430
414
  }, this);
431
415
  },
432
- getItemViewOptions: function getItemViewOptions(item) {
416
+ getItemViewOptions: function (item) {
433
417
  if (typeof this.options.itemViewOptions === 'function') {
434
418
  return this.options.itemViewOptions(item);
435
419
  } else {
436
420
  return this.options.itemViewOptions || {};
437
421
  }
438
422
  },
439
- closePlaceHolderView: function closePlaceHolderView() {
423
+ closePlaceHolderView: function () {
440
424
  if (this.placeHolderView) {
441
425
  this.placeHolderView.close();
442
426
  this.placeHolderView = null;
443
427
  }
444
428
  },
445
- togglePlaceHolder: function togglePlaceHolder() {
429
+ togglePlaceHolder: function () {
446
430
  var lastPlaceholderConstructor = this.placeHolderConstructor;
447
431
  this.placeHolderConstructor = this.getPlaceHolderConstructor();
448
432
  if (this.itemViews.length || !this.placeHolderConstructor) {
449
433
  this.closePlaceHolderView();
450
434
  } else if (!this.placeHolderView || lastPlaceholderConstructor !== this.placeHolderConstructor) {
451
435
  this.closePlaceHolderView();
452
- this.placeHolderView = new this.placeHolderConstructor();
436
+ this.placeHolderView = new this.placeHolderConstructor(this.getPlaceHolderViewOptions());
453
437
  this.$el.append(this.placeHolderView.render().el);
454
438
  }
455
439
  },
456
- getPlaceHolderConstructor: function getPlaceHolderConstructor() {
440
+ getPlaceHolderViewOptions: function () {
441
+ if (this.placeHolderConstructor === this.options.blankSlateViewConstructor) {
442
+ return this.options.blankSlateViewOptions;
443
+ }
444
+ },
445
+ getPlaceHolderConstructor: function () {
457
446
  if (this.loading && this.options.loadingViewConstructor) {
458
447
  return this.options.loadingViewConstructor;
459
448
  } else if (this.options.blankSlateViewConstructor) {
@@ -462,9 +451,8 @@ var CollectionView = Marionette.View.extend({
462
451
  }
463
452
  });
464
453
 
465
- var SortableCollectionView = CollectionView.extend({
466
- render: function render() {
467
- var _this = this;
454
+ const SortableCollectionView = CollectionView.extend({
455
+ render: function () {
468
456
  CollectionView.prototype.render.call(this);
469
457
  this.sortable = Sortable.create(this.el, {
470
458
  group: this.options.connectWith,
@@ -472,125 +460,90 @@ var SortableCollectionView = CollectionView.extend({
472
460
  ghostClass: 'sortable-placeholder',
473
461
  forceFallback: this.options.forceDraggableFallback,
474
462
  fallbackTolerance: 3,
475
- onEnd: function onEnd(event) {
476
- var item = $(event.item);
477
- if (item.parent().is(_this.el)) {
478
- _this.updateOrder();
463
+ onEnd: event => {
464
+ const item = $(event.item);
465
+ if (item.parent().is(this.el)) {
466
+ this.updateOrder();
479
467
  }
480
468
  },
481
- onRemove: function onRemove(event) {
482
- var view = $(event.item).data('view');
483
- _this.itemViews.remove(view);
484
- _this.collection.remove(view.model);
469
+ onRemove: event => {
470
+ const view = $(event.item).data('view');
471
+ this.itemViews.remove(view);
472
+ this.collection.remove(view.model);
485
473
  },
486
- onSort: function onSort(event) {
487
- if (event.from !== event.to && event.to === _this.el) {
488
- var view = $(event.item).data('view');
489
- _this.reindexPositions();
490
- _this.itemViews.add(view);
491
- _this.collection.add(view.model);
492
- _this.collection.saveOrder();
474
+ onSort: event => {
475
+ if (event.from !== event.to && event.to === this.el) {
476
+ const view = $(event.item).data('view');
477
+ this.reindexPositions();
478
+ this.itemViews.add(view);
479
+ this.collection.add(view.model);
480
+ this.collection.saveOrder();
493
481
  }
494
482
  }
495
483
  });
496
484
  return this;
497
485
  },
498
- onClose: function onClose() {
486
+ onClose() {
499
487
  CollectionView.prototype.onClose.call(this);
500
488
  this.sortable.destroy();
501
489
  },
502
- disableSorting: function disableSorting() {
490
+ disableSorting() {
503
491
  this.sortable.option('disabled', true);
504
492
  },
505
- enableSorting: function enableSorting() {
493
+ enableSorting() {
506
494
  this.sortable.option('disabled', false);
507
495
  },
508
- addItem: function addItem(item) {
496
+ addItem: function (item) {
509
497
  if (!this.itemViews.findByModel(item)) {
510
498
  CollectionView.prototype.addItem.call(this, item);
511
499
  }
512
500
  },
513
- removeItem: function removeItem(item) {
501
+ removeItem: function (item) {
514
502
  if (this.itemViews.findByModel(item)) {
515
503
  CollectionView.prototype.removeItem.call(this, item);
516
504
  }
517
505
  },
518
- updateOrder: function updateOrder() {
506
+ updateOrder: function () {
519
507
  this.reindexPositions();
520
508
  this.collection.sort();
521
509
  this.collection.saveOrder();
522
510
  },
523
- reindexPositions: function reindexPositions() {
511
+ reindexPositions: function () {
524
512
  this.$el.children().each(function (index) {
525
513
  $(this).data('view').model.set('position', index);
526
514
  });
527
515
  }
528
516
  });
529
517
 
530
- function _objectWithoutPropertiesLoose(source, excluded) {
531
- if (source == null) return {};
532
- var target = {};
533
- var sourceKeys = Object.keys(source);
534
- var key, i;
535
-
536
- for (i = 0; i < sourceKeys.length; i++) {
537
- key = sourceKeys[i];
538
- if (excluded.indexOf(key) >= 0) continue;
539
- target[key] = source[key];
540
- }
541
-
542
- return target;
543
- }
544
-
545
- function _objectWithoutProperties(source, excluded) {
546
- if (source == null) return {};
547
- var target = _objectWithoutPropertiesLoose(source, excluded);
548
- var key, i;
549
-
550
- if (Object.getOwnPropertySymbols) {
551
- var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
552
-
553
- for (i = 0; i < sourceSymbolKeys.length; i++) {
554
- key = sourceSymbolKeys[i];
555
- if (excluded.indexOf(key) >= 0) continue;
556
- if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
557
- target[key] = source[key];
558
- }
559
- }
560
-
561
- return target;
562
- }
563
-
564
- var _excluded = ["ignoreUndefined"];
565
- var ConfigurationEditorTabView = Marionette.View.extend({
518
+ const ConfigurationEditorTabView = Marionette.View.extend({
566
519
  className: 'configuration_editor_tab',
567
- initialize: function initialize() {
520
+ initialize: function () {
568
521
  this.inputs = new ChildViewContainer();
569
522
  this.groups = this.options.groups || ConfigurationEditorTabView.groups;
570
523
  },
571
- input: function input(propertyName, view, options) {
524
+ input: function (propertyName, view, options) {
572
525
  this.view(view, _.extend({
573
526
  placeholderModel: this.options.placeholderModel,
574
527
  propertyName: propertyName,
575
528
  attributeTranslationKeyPrefixes: this.options.attributeTranslationKeyPrefixes
576
529
  }, options || {}));
577
530
  },
578
- view: function view(_view, options) {
579
- this.inputs.add(new _view(_.extend({
531
+ view: function (view, options) {
532
+ this.inputs.add(new view(_.extend({
580
533
  model: this.model,
581
534
  parentTab: this.options.tab
582
535
  }, options || {})));
583
536
  },
584
- group: function group(name, options) {
537
+ group: function (name, options) {
585
538
  this.groups.apply(name, this, options);
586
539
  },
587
- render: function render() {
540
+ render: function () {
588
541
  this.inputs.each(function (input) {
589
542
  this.$el.append(input.render().el);
590
543
  }, this);
591
544
  return this;
592
545
  },
593
- onClose: function onClose() {
546
+ onClose: function () {
594
547
  if (this.inputs) {
595
548
  this.inputs.call('close');
596
549
  }
@@ -604,10 +557,10 @@ ConfigurationEditorTabView.Groups = function () {
604
557
  }
605
558
  groups[name] = fn;
606
559
  };
607
- this.apply = function (name, context) {
608
- var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
609
- ignoreUndefined = _ref.ignoreUndefined,
610
- options = _objectWithoutProperties(_ref, _excluded);
560
+ this.apply = function (name, context, {
561
+ ignoreUndefined,
562
+ ...options
563
+ } = {}) {
611
564
  if (!(name in groups)) {
612
565
  if (ignoreUndefined) {
613
566
  return;
@@ -621,7 +574,7 @@ ConfigurationEditorTabView.groups = new ConfigurationEditorTabView.Groups();
621
574
 
622
575
  function template(data) {
623
576
  var __p = '';
624
- __p += '<div class="tabs_view-scroller">\n <ul class="tabs_view-headers"></ul>\n</div>\n<div class="tabs_view-container"></div>\n';
577
+ __p += '<div class="tabs_view-scroller">\n <div class="tabs_view-tabs" role="tablist" aria-orientation="horizontal"></div>\n</div>\n<div class="tabs_view-container"></div>\n';
625
578
  return __p
626
579
  }
627
580
 
@@ -647,36 +600,43 @@ return __p
647
600
  *
648
601
  * @class
649
602
  */
650
- var TabsView = Marionette.Layout.extend( /* @lends TabView.prototype */{
651
- template: template,
603
+ const TabsView = Marionette.Layout.extend( /* @lends TabView.prototype */{
604
+ template,
652
605
  className: 'tabs_view',
653
606
  ui: {
654
- headers: '.tabs_view-headers',
655
- scroller: '.tabs_view-scroller'
607
+ tabs: '.tabs_view-tabs',
608
+ scroller: '.tabs_view-scroller',
609
+ container: '.tabs_view-container'
656
610
  },
657
611
  regions: {
658
612
  container: '.tabs_view-container'
659
613
  },
660
614
  events: {
661
- 'click .tabs_view-headers > li': function clickTabs_viewHeadersLi(event) {
662
- this.changeTab($(event.target).data('tab-name'));
615
+ 'click .tabs_view-tab': function (event) {
616
+ this.changeTab($(event.currentTarget).data('tab-name'));
617
+ },
618
+ 'keydown .tabs_view-tab': function (event) {
619
+ this._handleKeyDown(event);
663
620
  }
664
621
  },
665
- initialize: function initialize() {
622
+ initialize: function () {
666
623
  this.tabFactoryFns = {};
667
624
  this.tabNames = [];
668
625
  this.currentTabName = null;
669
626
  this._refreshScrollerOnSideBarResize();
670
627
  },
671
- tab: function tab(name, factoryFn) {
628
+ tab: function (name, factoryFn) {
672
629
  this.tabFactoryFns[name] = factoryFn;
673
630
  this.tabNames.push(name);
674
631
  },
675
- onRender: function onRender() {
632
+ onRender: function () {
676
633
  _.each(this.tabNames, function (name) {
677
634
  var label = findTranslation(this._labelTranslationKeys(name));
678
- this.ui.headers.append($('<li />').attr('data-tab-name', name).text(label));
635
+ var tabId = this._tabId(name);
636
+ var button = $('<button />').attr('type', 'button').attr('role', 'tab').attr('id', tabId).attr('data-tab-name', name).attr('tabindex', '-1').attr('aria-selected', 'false').addClass('tabs_view-tab').text(label);
637
+ this.ui.tabs.append(button);
679
638
  }, this);
639
+ this.ui.container.attr('id', this._panelId()).attr('role', 'tabpanel');
680
640
  this.scroller = new IScroll(this.ui.scroller[0], {
681
641
  scrollX: true,
682
642
  scrollY: false,
@@ -684,14 +644,22 @@ var TabsView = Marionette.Layout.extend( /* @lends TabView.prototype */{
684
644
  mouseWheel: true,
685
645
  preventDefault: false
686
646
  });
687
- this.changeTab(this.defaultTab());
647
+ this.changeTab(this.defaultTab(), {
648
+ refresh: true
649
+ });
688
650
  },
689
- changeTab: function changeTab(name) {
690
- this.container.show(this.tabFactoryFns[name]());
691
- this._updateActiveHeader(name);
651
+ changeTab: function (name, options = {}) {
652
+ if (!this.tabFactoryFns[name]) {
653
+ return;
654
+ }
655
+ if (this.currentTabName !== name || options.refresh) {
656
+ this.container.show(this.tabFactoryFns[name]());
657
+ }
692
658
  this.currentTabName = name;
659
+ this._updateActiveTab(name, options.focusTab);
660
+ this._updateActivePanel(name);
693
661
  },
694
- defaultTab: function defaultTab() {
662
+ defaultTab: function () {
695
663
  if (_.include(this.tabNames, this.options.defaultTab)) {
696
664
  return this.options.defaultTab;
697
665
  } else {
@@ -701,19 +669,23 @@ var TabsView = Marionette.Layout.extend( /* @lends TabView.prototype */{
701
669
  /**
702
670
  * Rerender current tab.
703
671
  */
704
- refresh: function refresh() {
705
- this.changeTab(this.currentTabName);
672
+ refresh: function () {
673
+ if (this.currentTabName) {
674
+ this.changeTab(this.currentTabName, {
675
+ refresh: true
676
+ });
677
+ }
706
678
  },
707
679
  /**
708
680
  * Adjust tabs scroller to changed width of view.
709
681
  */
710
- refreshScroller: function refreshScroller() {
682
+ refreshScroller: function () {
711
683
  this.scroller.refresh();
712
684
  },
713
- toggleSpinnerOnTab: function toggleSpinnerOnTab(name, visible) {
685
+ toggleSpinnerOnTab: function (name, visible) {
714
686
  this.$('[data-tab-name=' + name + ']').toggleClass('spinner', visible);
715
687
  },
716
- _labelTranslationKeys: function _labelTranslationKeys(name) {
688
+ _labelTranslationKeys: function (name) {
717
689
  var result = _.map(this.options.translationKeyPrefixes, function (prefix) {
718
690
  return prefix + '.' + name;
719
691
  });
@@ -725,23 +697,85 @@ var TabsView = Marionette.Layout.extend( /* @lends TabView.prototype */{
725
697
  }
726
698
  return result;
727
699
  },
728
- _updateActiveHeader: function _updateActiveHeader(activeTabName) {
700
+ _updateActiveTab: function (activeTabName, focusTab) {
729
701
  var scroller = this.scroller;
730
- this.ui.headers.children().each(function () {
731
- if ($(this).data('tab-name') === activeTabName) {
702
+ var panelId = this._panelId();
703
+ this.ui.tabs.children().each(function () {
704
+ var button = $(this);
705
+ var isActive = button.data('tab-name') === activeTabName;
706
+ if (isActive) {
732
707
  scroller.scrollToElement(this, 200, true);
733
- $(this).addClass('active');
708
+ button.addClass('active');
709
+ button.attr('aria-selected', 'true');
710
+ button.attr('aria-controls', panelId);
711
+ button.attr('tabindex', '0');
712
+ if (focusTab) {
713
+ button.focus();
714
+ }
734
715
  } else {
735
- $(this).removeClass('active');
716
+ button.removeClass('active');
717
+ button.attr('aria-selected', 'false');
718
+ button.attr('tabindex', '-1');
736
719
  }
737
720
  });
738
721
  },
739
- _refreshScrollerOnSideBarResize: function _refreshScrollerOnSideBarResize() {
722
+ _updateActivePanel: function (activeTabName) {
723
+ this.ui.container.attr('aria-labelledby', this._tabId(activeTabName));
724
+ },
725
+ _handleKeyDown: function (event) {
726
+ var tabName = $(event.currentTarget).data('tab-name');
727
+ var nextTabName;
728
+ switch (event.key) {
729
+ case 'ArrowLeft':
730
+ case 'ArrowUp':
731
+ nextTabName = this._previousTabName(tabName);
732
+ break;
733
+ case 'ArrowRight':
734
+ case 'ArrowDown':
735
+ nextTabName = this._nextTabName(tabName);
736
+ break;
737
+ case 'Home':
738
+ nextTabName = _.first(this.tabNames);
739
+ break;
740
+ case 'End':
741
+ nextTabName = _.last(this.tabNames);
742
+ break;
743
+ case 'Enter':
744
+ case ' ':
745
+ nextTabName = tabName;
746
+ break;
747
+ default:
748
+ return;
749
+ }
750
+ event.preventDefault();
751
+ if (nextTabName) {
752
+ this.changeTab(nextTabName, {
753
+ focusTab: true
754
+ });
755
+ }
756
+ },
757
+ _previousTabName: function (currentTabName) {
758
+ var currentIndex = _.indexOf(this.tabNames, currentTabName);
759
+ var previousIndex = (currentIndex - 1 + this.tabNames.length) % this.tabNames.length;
760
+ return this.tabNames[previousIndex];
761
+ },
762
+ _nextTabName: function (currentTabName) {
763
+ var currentIndex = _.indexOf(this.tabNames, currentTabName);
764
+ var nextIndex = (currentIndex + 1) % this.tabNames.length;
765
+ return this.tabNames[nextIndex];
766
+ },
767
+ _refreshScrollerOnSideBarResize: function () {
740
768
  if (pageflow.app) {
741
769
  this.listenTo(pageflow.app, 'resize', function () {
742
770
  this.scroller.refresh();
743
771
  });
744
772
  }
773
+ },
774
+ _tabId: function (name) {
775
+ return this.cid + '-tab-' + name;
776
+ },
777
+ _panelId: function () {
778
+ return this.cid + '-panel';
745
779
  }
746
780
  });
747
781
 
@@ -770,9 +804,9 @@ var TabsView = Marionette.Layout.extend( /* @lends TabView.prototype */{
770
804
  *
771
805
  * @class
772
806
  */
773
- var ConfigurationEditorView = Marionette.View.extend({
807
+ const ConfigurationEditorView = Marionette.View.extend({
774
808
  className: 'configuration_editor',
775
- initialize: function initialize() {
809
+ initialize: function () {
776
810
  this.tabsView = new TabsView({
777
811
  translationKeyPrefixes: this.options.tabTranslationKeyPrefixes || [this.options.tabTranslationKeyPrefix],
778
812
  fallbackTranslationKeyPrefix: 'pageflow.ui.configuration_editor.tabs',
@@ -780,10 +814,10 @@ var ConfigurationEditorView = Marionette.View.extend({
780
814
  });
781
815
  this.configure();
782
816
  },
783
- configure: function configure() {},
784
- tab: function tab(name, callbackOrOptions, callback) {
817
+ configure: function () {},
818
+ tab: function (name, callbackOrOptions, callback) {
785
819
  callback = callback || callbackOrOptions;
786
- var options = callback ? callbackOrOptions : {};
820
+ const options = callback ? callbackOrOptions : {};
787
821
  this.tabsView.tab(name, _.bind(function () {
788
822
  var tabView = new ConfigurationEditorTabView({
789
823
  model: options.model || this.model,
@@ -798,23 +832,23 @@ var ConfigurationEditorView = Marionette.View.extend({
798
832
  /**
799
833
  * Rerender current tab.
800
834
  */
801
- refresh: function refresh() {
835
+ refresh: function () {
802
836
  this.tabsView.refresh();
803
837
  },
804
838
  /**
805
839
  * Adjust tabs scroller to changed width of view.
806
840
  */
807
- refreshScroller: function refreshScroller() {
841
+ refreshScroller: function () {
808
842
  this.tabsView.refreshScroller();
809
843
  },
810
- render: function render() {
844
+ render: function () {
811
845
  this.$el.append(this.subview(this.tabsView).el);
812
846
  return this;
813
847
  }
814
848
  });
815
849
  _.extend(ConfigurationEditorView, {
816
850
  repository: {},
817
- register: function register(pageTypeName, prototype) {
851
+ register: function (pageTypeName, prototype) {
818
852
  this.repository[pageTypeName] = ConfigurationEditorView.extend(prototype);
819
853
  }
820
854
  });
@@ -839,13 +873,13 @@ return __p
839
873
  *
840
874
  * @since 12.0
841
875
  */
842
- var TableCellView = Marionette.ItemView.extend({
876
+ const TableCellView = Marionette.ItemView.extend({
843
877
  tagName: 'td',
844
878
  template: template$1,
845
- className: function className() {
879
+ className: function () {
846
880
  return this.options.className;
847
881
  },
848
- onRender: function onRender() {
882
+ onRender: function () {
849
883
  this.listenTo(this.getModel(), 'change:' + this.options.column.name, this.update);
850
884
  this.setupContentBinding();
851
885
  this.update();
@@ -853,20 +887,20 @@ var TableCellView = Marionette.ItemView.extend({
853
887
  /**
854
888
  * Override in concrete cell view.
855
889
  */
856
- update: function update() {
890
+ update: function () {
857
891
  throw 'Not implemented';
858
892
  },
859
893
  /**
860
894
  * Returns the column attribute's value in the row model.
861
895
  */
862
- attributeValue: function attributeValue() {
896
+ attributeValue: function () {
863
897
  if (typeof this.options.column.value == 'function') {
864
898
  return this.options.column.value(this.model);
865
899
  } else {
866
900
  return this.getModel().get(this.options.column.name);
867
901
  }
868
902
  },
869
- getModel: function getModel() {
903
+ getModel: function () {
870
904
  if (this.options.column.configurationAttribute) {
871
905
  return this.model.configuration;
872
906
  } else {
@@ -891,10 +925,10 @@ var TableCellView = Marionette.ItemView.extend({
891
925
  * // Looks for keys of the form:
892
926
  * // <table_view_translation_key_prefix>.<column_attribute>.cell_title
893
927
  */
894
- attributeTranslation: function attributeTranslation(keyName, options) {
928
+ attributeTranslation: function (keyName, options) {
895
929
  return findTranslation(this.attributeTranslationKeys(keyName), options);
896
930
  },
897
- attributeTranslationKeys: function attributeTranslationKeys(keyName) {
931
+ attributeTranslationKeys: function (keyName) {
898
932
  return _(this.options.attributeTranslationKeyPrefixes || []).map(function (prefix) {
899
933
  return prefix + '.' + this.options.column.name + '.' + keyName;
900
934
  }, this);
@@ -908,7 +942,7 @@ var TableCellView = Marionette.ItemView.extend({
908
942
  *
909
943
  * @protected
910
944
  */
911
- setupContentBinding: function setupContentBinding() {
945
+ setupContentBinding: function () {
912
946
  if (this.options.column.contentBinding) {
913
947
  this.listenTo(this.getModel(), 'change:' + this.options.column.contentBinding, this.update);
914
948
  this.update();
@@ -916,30 +950,30 @@ var TableCellView = Marionette.ItemView.extend({
916
950
  }
917
951
  });
918
952
 
919
- var TableHeaderCellView = TableCellView.extend({
953
+ const TableHeaderCellView = TableCellView.extend({
920
954
  tagName: 'th',
921
- render: function render() {
955
+ render: function () {
922
956
  this.$el.text(this.options.column.headerText || this.attributeTranslation('column_header'));
923
957
  this.$el.data('columnName', this.options.column.name);
924
958
  return this;
925
959
  }
926
960
  });
927
961
 
928
- var TableRowView = Marionette.View.extend({
962
+ const TableRowView = Marionette.View.extend({
929
963
  tagName: 'tr',
930
964
  events: {
931
- 'click': function click() {
965
+ 'click': function () {
932
966
  if (this.options.selection) {
933
967
  this.options.selection.set(this.selectionAttribute(), this.model);
934
968
  }
935
969
  }
936
970
  },
937
- initialize: function initialize() {
971
+ initialize: function () {
938
972
  if (this.options.selection) {
939
973
  this.listenTo(this.options.selection, 'change', this.updateClassName);
940
974
  }
941
975
  },
942
- render: function render() {
976
+ render: function () {
943
977
  _(this.options.columns).each(function (column) {
944
978
  this.appendSubview(new column.cellView(_.extend({
945
979
  model: this.model,
@@ -950,13 +984,13 @@ var TableRowView = Marionette.View.extend({
950
984
  this.updateClassName();
951
985
  return this;
952
986
  },
953
- updateClassName: function updateClassName() {
987
+ updateClassName: function () {
954
988
  this.$el.toggleClass('is_selected', this.isSelected());
955
989
  },
956
- isSelected: function isSelected() {
990
+ isSelected: function () {
957
991
  return this.options.selection && this.options.selection.get(this.selectionAttribute()) === this.model;
958
992
  },
959
- selectionAttribute: function selectionAttribute() {
993
+ selectionAttribute: function () {
960
994
  return this.options.selectionAttribute || 'current';
961
995
  }
962
996
  });
@@ -977,7 +1011,7 @@ __p += '<td colspan="' +
977
1011
  return __p
978
1012
  }
979
1013
 
980
- var TableView = Marionette.ItemView.extend({
1014
+ const TableView = Marionette.ItemView.extend({
981
1015
  tagName: 'table',
982
1016
  className: 'table_view',
983
1017
  template: template$2,
@@ -985,7 +1019,7 @@ var TableView = Marionette.ItemView.extend({
985
1019
  headRow: 'thead tr',
986
1020
  body: 'tbody'
987
1021
  },
988
- onRender: function onRender() {
1022
+ onRender: function () {
989
1023
  var view = this;
990
1024
  _(this.options.columns).each(function (column) {
991
1025
  this.ui.headRow.append(this.subview(new TableHeaderCellView({
@@ -1007,7 +1041,7 @@ var TableView = Marionette.ItemView.extend({
1007
1041
  tagName: 'tr',
1008
1042
  className: 'blank_slate',
1009
1043
  template: blankSlateTemplate,
1010
- serializeData: function serializeData() {
1044
+ serializeData: function () {
1011
1045
  return {
1012
1046
  blankSlateText: view.options.blankSlateText,
1013
1047
  colSpan: view.options.columns.length
@@ -1024,18 +1058,18 @@ __p += '<span class="label">\n</span>\n';
1024
1058
  return __p
1025
1059
  }
1026
1060
 
1027
- var TooltipView = Marionette.ItemView.extend({
1061
+ const TooltipView = Marionette.ItemView.extend({
1028
1062
  template: template$3,
1029
1063
  className: 'tooltip',
1030
1064
  ui: {
1031
1065
  label: '.label'
1032
1066
  },
1033
- hide: function hide() {
1067
+ hide: function () {
1034
1068
  this.visible = false;
1035
1069
  clearTimeout(this.timeout);
1036
1070
  this.$el.removeClass('visible');
1037
1071
  },
1038
- show: function show(text, position, options) {
1072
+ show: function (text, position, options) {
1039
1073
  options = options || {};
1040
1074
  this.visible = true;
1041
1075
  clearTimeout(this.timeout);
@@ -1065,99 +1099,36 @@ var TooltipView = Marionette.ItemView.extend({
1065
1099
  }
1066
1100
  });
1067
1101
 
1068
- function _defineProperty(obj, key, value) {
1069
- if (key in obj) {
1070
- Object.defineProperty(obj, key, {
1071
- value: value,
1072
- enumerable: true,
1073
- configurable: true,
1074
- writable: true
1075
- });
1076
- } else {
1077
- obj[key] = value;
1078
- }
1079
-
1080
- return obj;
1081
- }
1082
-
1083
- function ownKeys(object, enumerableOnly) {
1084
- var keys = Object.keys(object);
1085
-
1086
- if (Object.getOwnPropertySymbols) {
1087
- var symbols = Object.getOwnPropertySymbols(object);
1088
- if (enumerableOnly) symbols = symbols.filter(function (sym) {
1089
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
1090
- });
1091
- keys.push.apply(keys, symbols);
1092
- }
1093
-
1094
- return keys;
1095
- }
1096
-
1097
- function _objectSpread2(target) {
1098
- for (var i = 1; i < arguments.length; i++) {
1099
- var source = arguments[i] != null ? arguments[i] : {};
1100
-
1101
- if (i % 2) {
1102
- ownKeys(Object(source), true).forEach(function (key) {
1103
- _defineProperty(target, key, source[key]);
1104
- });
1105
- } else if (Object.getOwnPropertyDescriptors) {
1106
- Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
1107
- } else {
1108
- ownKeys(Object(source)).forEach(function (key) {
1109
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
1110
- });
1111
- }
1112
- }
1113
-
1114
- return target;
1115
- }
1116
-
1117
- var attributeBinding = {
1118
- setupBooleanAttributeBinding: function setupBooleanAttributeBinding(optionName, updateMethod) {
1102
+ const attributeBinding = {
1103
+ setupBooleanAttributeBinding(optionName, updateMethod) {
1119
1104
  this.setupAttributeBinding(optionName, updateMethod, Boolean);
1120
1105
  },
1121
- getBooleanAttributBoundOption: function getBooleanAttributBoundOption(optionName) {
1106
+ getBooleanAttributBoundOption(optionName) {
1122
1107
  return this.getAttributeBoundOption(optionName, Boolean);
1123
1108
  },
1124
- setupAttributeBinding: function setupAttributeBinding(optionName, updateMethod) {
1125
- var _this = this;
1126
- var normalize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function (value) {
1127
- return value;
1128
- };
1129
- var binding = this.options["".concat(optionName, "Binding")];
1130
- var model = this.options["".concat(optionName, "BindingModel")] || this.model;
1131
- var view = this;
1132
- if (binding) {
1133
- _.flatten([binding]).forEach(function (attribute) {
1134
- _this.listenTo(model, 'change:' + attribute, update);
1135
- });
1136
- }
1137
- update();
1138
- function update() {
1139
- updateMethod.call(view, view.getAttributeBoundOption(optionName, normalize));
1140
- }
1109
+ setupAttributeBinding(optionName, updateMethod, normalize = value => value) {
1110
+ const binding = this.options[`${optionName}Binding`];
1111
+ const model = this.options[`${optionName}BindingModel`] || this.model;
1112
+ setup({
1113
+ binding,
1114
+ model,
1115
+ listener: this,
1116
+ normalize,
1117
+ option: this.options[optionName],
1118
+ bindingValue: this.options[`${optionName}BindingValue`],
1119
+ callback: value => updateMethod.call(this, value)
1120
+ });
1141
1121
  },
1142
- getAttributeBoundOption: function getAttributeBoundOption(optionName) {
1143
- var normalize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function (value) {
1144
- return value;
1145
- };
1146
- var binding = this.options["".concat(optionName, "Binding")];
1147
- var model = this.options["".concat(optionName, "BindingModel")] || this.model;
1148
- var bindingValueOptionName = "".concat(optionName, "BindingValue");
1149
- var value = Array.isArray(binding) ? binding.map(function (attribute) {
1150
- return model.get(attribute);
1151
- }) : model.get(binding);
1152
- if (bindingValueOptionName in this.options) {
1153
- return value === this.options[bindingValueOptionName];
1154
- } else if (typeof this.options[optionName] === 'function') {
1155
- return normalize(this.options[optionName](value));
1156
- } else if (optionName in this.options) {
1157
- return normalize(this.options[optionName]);
1158
- } else if (binding) {
1159
- return normalize(value);
1160
- }
1122
+ getAttributeBoundOption(optionName, normalize = value => value) {
1123
+ const binding = this.options[`${optionName}Binding`];
1124
+ const model = this.options[`${optionName}BindingModel`] || this.model;
1125
+ return resolve({
1126
+ binding,
1127
+ model,
1128
+ normalize,
1129
+ option: this.options[optionName],
1130
+ bindingValue: this.options[`${optionName}BindingValue`]
1131
+ });
1161
1132
  }
1162
1133
  };
1163
1134
 
@@ -1250,6 +1221,11 @@ var attributeBinding = {
1250
1221
  * An array of prefixes to lookup translations for labels and
1251
1222
  * inline help texts based on attribute names.
1252
1223
  *
1224
+ * @param {string} [options.attributeTranslationPropertyName]
1225
+ * Use this property name instead of `propertyName` for looking up
1226
+ * translations. Useful when the property name has been transformed
1227
+ * but translations should use the original name.
1228
+ *
1253
1229
  * @param {string} [options.additionalInlineHelpText]
1254
1230
  * A text that will be appended to the translation based inline
1255
1231
  * text.
@@ -1295,7 +1271,8 @@ var attributeBinding = {
1295
1271
  *
1296
1272
  * @mixin
1297
1273
  */
1298
- var inputView = _objectSpread2(_objectSpread2({}, attributeBinding), {}, {
1274
+ const inputView = {
1275
+ ...attributeBinding,
1299
1276
  ui: {
1300
1277
  label: 'label',
1301
1278
  labelText: 'label .name',
@@ -1323,19 +1300,22 @@ var inputView = _objectSpread2(_objectSpread2({}, attributeBinding), {}, {
1323
1300
  * @since 0.9
1324
1301
  * @member
1325
1302
  */
1326
- attributeTranslationKeys: function attributeTranslationKeys$1(keyName, options) {
1327
- return attributeTranslationKeys(this.options.propertyName, keyName, _.extend({
1303
+ attributeTranslationKeys: function (keyName, options) {
1304
+ return attributeTranslationKeys(this.options.attributeTranslationPropertyName || this.options.propertyName, keyName, _.extend({
1328
1305
  prefixes: this.options.attributeTranslationKeyPrefixes,
1329
1306
  fallbackModelI18nKey: this.model.i18nKey
1330
1307
  }, options || {}));
1331
1308
  },
1332
- onRender: function onRender() {
1309
+ onRender: function () {
1333
1310
  this.$el.addClass('input');
1334
1311
  this.$el.addClass(this.model.modelName + '_' + this.options.propertyName);
1335
1312
  this.$el.data('inputPropertyName', this.options.propertyName);
1336
1313
  this.$el.data('labelText', this.labelText());
1337
1314
  this.$el.data('inlineHelpText', this.inlineHelpText());
1338
1315
  this.ui.labelText.text(this.labelText());
1316
+ if (this.options.hideLabel) {
1317
+ this.ui.label.addClass('visually_hidden');
1318
+ }
1339
1319
  this.updateInlineHelp();
1340
1320
  this.setLabelFor();
1341
1321
  this.setupBooleanAttributeBinding('disabled', this.updateDisabled);
@@ -1345,15 +1325,15 @@ var inputView = _objectSpread2(_objectSpread2({}, attributeBinding), {}, {
1345
1325
  * The label to display in the form.
1346
1326
  * @return {string}
1347
1327
  */
1348
- labelText: function labelText() {
1328
+ labelText: function () {
1349
1329
  return this.options.label || this.localizedAttributeName();
1350
1330
  },
1351
- localizedAttributeName: function localizedAttributeName() {
1331
+ localizedAttributeName: function () {
1352
1332
  return findTranslation(this.attributeTranslationKeys('label', {
1353
1333
  fallbackPrefix: 'activerecord.attributes'
1354
1334
  }));
1355
1335
  },
1356
- updateInlineHelp: function updateInlineHelp() {
1336
+ updateInlineHelp: function () {
1357
1337
  this.ui.inlineHelp.html(this.inlineHelpText());
1358
1338
  if (!this.inlineHelpText()) {
1359
1339
  this.ui.inlineHelp.hide();
@@ -1363,7 +1343,7 @@ var inputView = _objectSpread2(_objectSpread2({}, attributeBinding), {}, {
1363
1343
  * The inline help text for the form field.
1364
1344
  * @return {string}
1365
1345
  */
1366
- inlineHelpText: function inlineHelpText() {
1346
+ inlineHelpText: function () {
1367
1347
  var keys = this.attributeTranslationKeys('inline_help', {
1368
1348
  fallbackPrefix: 'pageflow.ui.inline_help'
1369
1349
  });
@@ -1375,34 +1355,34 @@ var inputView = _objectSpread2(_objectSpread2({}, attributeBinding), {}, {
1375
1355
  html: true
1376
1356
  }), this.options.additionalInlineHelpText]).join(' ');
1377
1357
  },
1378
- setLabelFor: function setLabelFor() {
1358
+ setLabelFor() {
1379
1359
  if (this.ui.input && this.ui.label.length === 1 && !this.ui.input.attr('id')) {
1380
- var id = 'input_' + this.model.modelName + '_' + this.options.propertyName;
1360
+ const id = 'input_' + this.model.modelName + '_' + this.options.propertyName;
1381
1361
  this.ui.input.attr('id', id);
1382
1362
  this.ui.label.attr('for', id);
1383
1363
  }
1384
1364
  },
1385
- isDisabled: function isDisabled() {
1365
+ isDisabled: function () {
1386
1366
  return this.getBooleanAttributBoundOption('disabled');
1387
1367
  },
1388
- updateDisabled: function updateDisabled() {
1368
+ updateDisabled: function () {
1389
1369
  this.$el.toggleClass('input-disabled', !!this.isDisabled());
1390
1370
  this.updateInlineHelp();
1391
1371
  if (this.ui.input) {
1392
1372
  this.updateDisabledAttribute(this.ui.input);
1393
1373
  }
1394
1374
  },
1395
- updateDisabledAttribute: function updateDisabledAttribute(element) {
1375
+ updateDisabledAttribute: function (element) {
1396
1376
  if (this.isDisabled()) {
1397
1377
  element.attr('disabled', true);
1398
1378
  } else {
1399
1379
  element.removeAttr('disabled');
1400
1380
  }
1401
1381
  },
1402
- updateVisible: function updateVisible() {
1382
+ updateVisible: function () {
1403
1383
  this.$el.toggleClass('hidden_via_binding', this.getBooleanAttributBoundOption('visible') === false);
1404
1384
  }
1405
- });
1385
+ };
1406
1386
 
1407
1387
  function template$4(data) {
1408
1388
  var __p = '';
@@ -1418,7 +1398,7 @@ return __p
1418
1398
  *
1419
1399
  * @class
1420
1400
  */
1421
- var CheckBoxGroupInputView = Marionette.ItemView.extend({
1401
+ const CheckBoxGroupInputView = Marionette.ItemView.extend({
1422
1402
  mixins: [inputView],
1423
1403
  template: template$4,
1424
1404
  className: 'check_box_group_input',
@@ -1429,7 +1409,7 @@ var CheckBoxGroupInputView = Marionette.ItemView.extend({
1429
1409
  label: 'label',
1430
1410
  container: '.check_boxes_container'
1431
1411
  },
1432
- initialize: function initialize() {
1412
+ initialize: function () {
1433
1413
  if (!this.options.texts) {
1434
1414
  if (!this.options.translationKeys) {
1435
1415
  var translationKeyPrefix = this.options.translationKeyPrefix || findKeyWithTranslation(this.attributeTranslationKeys('values', {
@@ -1444,26 +1424,26 @@ var CheckBoxGroupInputView = Marionette.ItemView.extend({
1444
1424
  });
1445
1425
  }
1446
1426
  },
1447
- onRender: function onRender() {
1427
+ onRender: function () {
1448
1428
  this.ui.label.attr('for', this.cid);
1449
1429
  this.appendOptions();
1450
1430
  this.load();
1451
1431
  this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
1452
1432
  },
1453
- appendOptions: function appendOptions() {
1433
+ appendOptions: function () {
1454
1434
  _.each(this.options.values, function (value, index) {
1455
1435
  var option = '<div class="check_box">' + '<label><input type="checkbox" name="' + value + '" />' + this.options.texts[index] + '</label></div>';
1456
1436
  this.ui.container.append($(option));
1457
1437
  }, this);
1458
1438
  },
1459
- save: function save() {
1439
+ save: function () {
1460
1440
  var configured = {};
1461
1441
  _.each(this.ui.container.find('input'), function (input) {
1462
1442
  configured[$(input).attr('name')] = $(input).prop('checked');
1463
1443
  });
1464
1444
  this.model.set(this.options.propertyName, configured);
1465
1445
  },
1466
- load: function load() {
1446
+ load: function () {
1467
1447
  if (!this.isClosed) {
1468
1448
  _.each(this.options.values, function (value) {
1469
1449
  this.ui.container.find('input[name="' + value + '"]').prop('checked', this.model.get(this.options.propertyName)[value]);
@@ -1491,7 +1471,7 @@ return __p
1491
1471
  *
1492
1472
  * @class
1493
1473
  */
1494
- var UrlDisplayView = Marionette.ItemView.extend({
1474
+ const UrlDisplayView = Marionette.ItemView.extend({
1495
1475
  mixins: [inputView],
1496
1476
  template: template$5,
1497
1477
  ui: {
@@ -1501,43 +1481,21 @@ var UrlDisplayView = Marionette.ItemView.extend({
1501
1481
  'change': 'update'
1502
1482
  },
1503
1483
  events: {
1504
- 'click a': function clickA(event) {
1484
+ 'click a': function (event) {
1505
1485
  // Ensure default is not prevented by parent event listener.
1506
1486
  event.stopPropagation();
1507
1487
  }
1508
1488
  },
1509
- onRender: function onRender() {
1489
+ onRender: function () {
1510
1490
  this.update();
1511
1491
  },
1512
- update: function update() {
1492
+ update: function () {
1513
1493
  var url = this.model.get(this.options.propertyName || 'original_url');
1514
1494
  this.$el.toggle(this.model.isUploaded() && !_.isEmpty(url));
1515
1495
  this.ui.link.attr('href', url);
1516
1496
  }
1517
1497
  });
1518
1498
 
1519
- function _classCallCheck(instance, Constructor) {
1520
- if (!(instance instanceof Constructor)) {
1521
- throw new TypeError("Cannot call a class as a function");
1522
- }
1523
- }
1524
-
1525
- function _defineProperties(target, props) {
1526
- for (var i = 0; i < props.length; i++) {
1527
- var descriptor = props[i];
1528
- descriptor.enumerable = descriptor.enumerable || false;
1529
- descriptor.configurable = true;
1530
- if ("value" in descriptor) descriptor.writable = true;
1531
- Object.defineProperty(target, descriptor.key, descriptor);
1532
- }
1533
- }
1534
-
1535
- function _createClass(Constructor, protoProps, staticProps) {
1536
- if (protoProps) _defineProperties(Constructor.prototype, protoProps);
1537
- if (staticProps) _defineProperties(Constructor, staticProps);
1538
- return Constructor;
1539
- }
1540
-
1541
1499
  /**
1542
1500
  * Input view for a number.
1543
1501
  *
@@ -1550,84 +1508,73 @@ function _createClass(Constructor, protoProps, staticProps) {
1550
1508
  *
1551
1509
  * @class
1552
1510
  */
1553
- var NumberInputView = Marionette.ItemView.extend({
1511
+ const NumberInputView = Marionette.ItemView.extend({
1554
1512
  mixins: [inputView],
1555
- template: function template() {
1556
- return "\n <label>\n <span class=\"name\"></span>\n <span class=\"inline_help\"></span>\n </label>\n <input type=\"text\" dir=\"auto\" />\n ";
1557
- },
1513
+ template: () => `
1514
+ <label>
1515
+ <span class="name"></span>
1516
+ <span class="inline_help"></span>
1517
+ </label>
1518
+ <input type="text" dir="auto" />
1519
+ `,
1558
1520
  ui: {
1559
1521
  input: 'input'
1560
1522
  },
1561
1523
  events: {
1562
1524
  'change': 'onChange'
1563
1525
  },
1564
- initialize: function initialize() {
1526
+ initialize() {
1565
1527
  this.parser = new NumberParser(this.options.locale);
1566
1528
  },
1567
- onRender: function onRender() {
1529
+ onRender: function () {
1568
1530
  this.load();
1569
1531
  this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
1570
1532
  },
1571
- onChange: function onChange() {
1533
+ onChange: function () {
1572
1534
  this.save();
1573
1535
  this.load();
1574
1536
  },
1575
- onClose: function onClose() {
1537
+ onClose: function () {
1576
1538
  this.save();
1577
1539
  },
1578
- save: function save() {
1579
- var inputValue = this.ui.input.val();
1540
+ save: function () {
1541
+ const inputValue = this.ui.input.val();
1580
1542
  this.model.set(this.options.propertyName, this.parser.parse(inputValue) || 0);
1581
1543
  },
1582
- load: function load() {
1583
- var input = this.ui.input;
1584
- var value = this.model.get(this.options.propertyName) || 0;
1544
+ load: function () {
1545
+ const input = this.ui.input;
1546
+ const value = this.model.get(this.options.propertyName) || 0;
1585
1547
  input.val(value.toLocaleString(this.options.locale, {
1586
1548
  useGrouping: false
1587
1549
  }));
1588
1550
  },
1589
- displayValidationError: function displayValidationError(message) {
1551
+ displayValidationError: function (message) {
1590
1552
  this.$el.addClass('invalid');
1591
1553
  this.ui.input.attr('title', message);
1592
1554
  },
1593
- resetValidationError: function resetValidationError(message) {
1555
+ resetValidationError: function (message) {
1594
1556
  this.$el.removeClass('invalid');
1595
1557
  this.ui.input.attr('title', '');
1596
1558
  }
1597
1559
  });
1598
- var NumberParser = /*#__PURE__*/function () {
1599
- function NumberParser(locale) {
1600
- _classCallCheck(this, NumberParser);
1601
- var format = new Intl.NumberFormat(locale);
1602
- var parts = format.formatToParts(12345.6);
1603
- var numerals = Array.from({
1560
+ class NumberParser {
1561
+ constructor(locale) {
1562
+ const format = new Intl.NumberFormat(locale);
1563
+ const parts = format.formatToParts(12345.6);
1564
+ const numerals = Array.from({
1604
1565
  length: 10
1605
- }).map(function (_, i) {
1606
- return format.format(i);
1607
- });
1608
- var index = new Map(numerals.map(function (d, i) {
1609
- return [d, i];
1610
- }));
1611
- this._group = new RegExp("[".concat(parts.find(function (d) {
1612
- return d.type === "group";
1613
- }).value, "]"), "g");
1614
- this._decimal = new RegExp("[".concat(parts.find(function (d) {
1615
- return d.type === "decimal";
1616
- }).value, "]"));
1617
- this._numeral = new RegExp("[".concat(numerals.join(""), "]"), "g");
1618
- this._index = function (d) {
1619
- return index.get(d);
1620
- };
1566
+ }).map((_, i) => format.format(i));
1567
+ const index = new Map(numerals.map((d, i) => [d, i]));
1568
+ this._group = new RegExp(`[${parts.find(d => d.type === "group").value}]`, "g");
1569
+ this._decimal = new RegExp(`[${parts.find(d => d.type === "decimal").value}]`);
1570
+ this._numeral = new RegExp(`[${numerals.join("")}]`, "g");
1571
+ this._index = d => index.get(d);
1621
1572
  }
1622
- _createClass(NumberParser, [{
1623
- key: "parse",
1624
- value: function parse(string) {
1625
- string = string.trim().replace(this._group, "").replace(this._decimal, ".").replace(this._numeral, this._index);
1626
- return string ? +string : NaN;
1627
- }
1628
- }]);
1629
- return NumberParser;
1630
- }();
1573
+ parse(string) {
1574
+ string = string.trim().replace(this._group, "").replace(this._decimal, ".").replace(this._numeral, this._index);
1575
+ return string ? +string : NaN;
1576
+ }
1577
+ }
1631
1578
 
1632
1579
  /**
1633
1580
  * Text based input view that can display a placeholder.
@@ -1650,20 +1597,20 @@ var NumberParser = /*#__PURE__*/function () {
1650
1597
  * Obtain placeholder by looking up the configured `propertyName`
1651
1598
  * inside a given model.
1652
1599
  */
1653
- var inputWithPlaceholderText = {
1654
- onRender: function onRender() {
1600
+ const inputWithPlaceholderText = {
1601
+ onRender: function () {
1655
1602
  this.updatePlaceholder();
1656
1603
  if (this.options.placeholderBinding) {
1657
1604
  this.listenTo(this.model, 'change:' + this.options.placeholderBinding, this.updatePlaceholder);
1658
1605
  }
1659
1606
  },
1660
- updateDisabled: function updateDisabled() {
1607
+ updateDisabled: function () {
1661
1608
  this.updatePlaceholder();
1662
1609
  },
1663
- updatePlaceholder: function updatePlaceholder() {
1610
+ updatePlaceholder: function () {
1664
1611
  this.ui.input.attr('placeholder', this.placeholderText());
1665
1612
  },
1666
- placeholderText: function placeholderText() {
1613
+ placeholderText: function () {
1667
1614
  if (!this.isDisabled() || !this.options.hidePlaceholderIfDisabled) {
1668
1615
  if (this.options.placeholder) {
1669
1616
  if (typeof this.options.placeholder == 'function') {
@@ -1677,25 +1624,22 @@ var inputWithPlaceholderText = {
1677
1624
  }
1678
1625
  return '';
1679
1626
  },
1680
- placeholderModelValue: function placeholderModelValue() {
1627
+ placeholderModelValue: function () {
1681
1628
  return this.options.placeholderModel && this.options.placeholderModel.get(this.options.propertyName);
1682
1629
  }
1683
1630
  };
1684
1631
 
1685
- var viewWithValidationErrorMessages = {
1686
- onRender: function onRender() {
1632
+ const viewWithValidationErrorMessages = {
1633
+ onRender() {
1687
1634
  this.listenTo(this.model, 'invalid sync', this.updateValidationErrorMessages);
1688
1635
  this.updateValidationErrorMessages();
1689
1636
  },
1690
- updateValidationErrorMessages: function updateValidationErrorMessages() {
1691
- var _this = this;
1692
- var errors = this.model.validationErrors && this.model.validationErrors[this.options.propertyName] || [];
1637
+ updateValidationErrorMessages() {
1638
+ const errors = this.model.validationErrors && this.model.validationErrors[this.options.propertyName] || [];
1693
1639
  if (errors.length) {
1694
1640
  this.validationErrorList = this.validationErrorList || $('<ul class="validation_error_messages" />').appendTo(this.el);
1695
1641
  this.validationErrorList.html('');
1696
- errors.forEach(function (error) {
1697
- return _this.validationErrorList.append("<li>".concat(error, "</li>"));
1698
- });
1642
+ errors.forEach(error => this.validationErrorList.append(`<li>${error}</li>`));
1699
1643
  this.$el.addClass('invalid');
1700
1644
  } else if (this.validationErrorList) {
1701
1645
  this.validationErrorList.remove();
@@ -1730,7 +1674,7 @@ return __p
1730
1674
  *
1731
1675
  * @class
1732
1676
  */
1733
- var TextInputView = Marionette.ItemView.extend({
1677
+ const TextInputView = Marionette.ItemView.extend({
1734
1678
  mixins: [inputView, inputWithPlaceholderText, viewWithValidationErrorMessages],
1735
1679
  template: template$6,
1736
1680
  ui: {
@@ -1739,25 +1683,25 @@ var TextInputView = Marionette.ItemView.extend({
1739
1683
  events: {
1740
1684
  'change': 'onChange'
1741
1685
  },
1742
- onRender: function onRender() {
1686
+ onRender: function () {
1743
1687
  this.load();
1744
1688
  this.validate();
1745
1689
  this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
1746
1690
  },
1747
- onChange: function onChange() {
1691
+ onChange: function () {
1748
1692
  if (this.validate()) {
1749
1693
  this.save();
1750
1694
  }
1751
1695
  },
1752
- onClose: function onClose() {
1696
+ onClose: function () {
1753
1697
  if (this.validate()) {
1754
1698
  this.save();
1755
1699
  }
1756
1700
  },
1757
- save: function save() {
1701
+ save: function () {
1758
1702
  this.model.set(this.options.propertyName, this.ui.input.val());
1759
1703
  },
1760
- load: function load() {
1704
+ load: function () {
1761
1705
  var input = this.ui.input;
1762
1706
  input.val(this.model.get(this.options.propertyName));
1763
1707
 
@@ -1767,7 +1711,7 @@ var TextInputView = Marionette.ItemView.extend({
1767
1711
  // for new and maxLength-conforming data: add validation
1768
1712
  this.validateMaxLength = input.val().length <= this.options.maxLength;
1769
1713
  },
1770
- validate: function validate() {
1714
+ validate: function () {
1771
1715
  var input = this.ui.input;
1772
1716
  if (this.options.required && !input.val()) {
1773
1717
  this.displayValidationError(I18n$1.t('pageflow.ui.views.inputs.text_input_view.required_field'));
@@ -1783,11 +1727,11 @@ var TextInputView = Marionette.ItemView.extend({
1783
1727
  return true;
1784
1728
  }
1785
1729
  },
1786
- displayValidationError: function displayValidationError(message) {
1730
+ displayValidationError: function (message) {
1787
1731
  this.$el.addClass('invalid');
1788
1732
  this.ui.input.attr('title', message);
1789
1733
  },
1790
- resetValidationError: function resetValidationError(message) {
1734
+ resetValidationError: function (message) {
1791
1735
  this.$el.removeClass('invalid');
1792
1736
  this.ui.input.attr('title', '');
1793
1737
  }
@@ -1799,31 +1743,640 @@ __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></spa
1799
1743
  return __p
1800
1744
  }
1801
1745
 
1802
- var FileNameInputView = TextInputView.extend({
1746
+ const FileNameInputView = TextInputView.extend({
1803
1747
  template: template$7,
1804
1748
  className: 'file_name_input',
1805
1749
  ui: Object.assign({}, TextInputView.prototype.ui, {
1806
1750
  extension: '.file_name_input-extension'
1807
1751
  }),
1808
- onRender: function onRender() {
1752
+ onRender: function () {
1809
1753
  TextInputView.prototype.onRender.call(this);
1810
1754
  },
1811
- save: function save() {
1812
- var baseName = this.ui.input.val();
1813
- var extension = this.ui.extension.text();
1755
+ save: function () {
1756
+ const baseName = this.ui.input.val();
1757
+ const extension = this.ui.extension.text();
1814
1758
  this.model.set(this.options.propertyName, baseName + extension);
1815
1759
  },
1816
- load: function load() {
1817
- var fullName = this.model.get(this.options.propertyName) || '';
1818
- var match = fullName.match(/^(.*?)(\.[^.]+)?$/);
1819
- var baseName = match ? match[1] : fullName;
1820
- var extension = match && match[2] ? match[2] : '';
1760
+ load: function () {
1761
+ const fullName = this.model.get(this.options.propertyName) || '';
1762
+ const match = fullName.match(/^(.*?)(\.[^.]+)?$/);
1763
+ const baseName = match ? match[1] : fullName;
1764
+ const extension = match && match[2] ? match[2] : '';
1821
1765
  this.ui.input.val(baseName);
1822
1766
  this.ui.extension.text(extension);
1823
1767
  this.options.maxLength = this.options.maxLength || 255;
1824
1768
  }
1825
1769
  });
1826
1770
 
1771
+ // Inspired by https://github.com/mdbassit/Coloris
1772
+ const ctx = typeof OffscreenCanvas !== 'undefined' && new OffscreenCanvas(1, 1).getContext('2d');
1773
+ const DEFAULT_DISPLAY_COLOR = {
1774
+ r: 255,
1775
+ g: 255,
1776
+ b: 255,
1777
+ a: 1
1778
+ };
1779
+ let nextDescriptionId = 0;
1780
+ const PICKER_HTML = '<div class="color_picker-gradient" role="application">' + '<div class="color_picker-marker" tabindex="0"></div>' + '</div>' + '<div class="color_picker-hue">' + '<input type="range" min="0" max="360" step="1">' + '<div></div>' + '</div>' + '<div class="color_picker-alpha">' + '<input type="range" min="0" max="100" step="1">' + '<div></div>' + '<span></span>' + '</div>' + '<div class="color_picker-swatches"></div>';
1781
+ class ColorPicker {
1782
+ constructor(input, options = {}) {
1783
+ this._input = input;
1784
+ this._colorAreaDims = {};
1785
+ this._alpha = options.alpha || false;
1786
+ this._onChange = options.onChange;
1787
+ this._defaultColor = strToRGBA(options.defaultValue);
1788
+ this._fallbackColor = strToRGBA(options.fallbackColor);
1789
+ this._fallbackColorDescription = options.fallbackColorDescription;
1790
+ this._swatches = options.swatches || [];
1791
+ this._wrapInput();
1792
+ this._createPicker();
1793
+ this._renderSwatches();
1794
+ this._bindEvents();
1795
+ this._updateColor(strToRGBA(this._input.value), {
1796
+ silent: true
1797
+ });
1798
+ }
1799
+ setValue(str) {
1800
+ this._input.value = str || '';
1801
+ this._updateColor(strToRGBA(str), {
1802
+ silent: true
1803
+ });
1804
+ }
1805
+ update(options) {
1806
+ if ('defaultValue' in options) {
1807
+ this._defaultColor = strToRGBA(options.defaultValue);
1808
+ }
1809
+ if ('fallbackColor' in options) {
1810
+ this._fallbackColor = strToRGBA(options.fallbackColor);
1811
+ this._updateColor(this._currentColor, {
1812
+ silent: true
1813
+ });
1814
+ }
1815
+ if (options.swatches) {
1816
+ this._swatches = options.swatches;
1817
+ this._renderSwatches();
1818
+ }
1819
+ }
1820
+ destroy() {
1821
+ this._close();
1822
+ this._unbindEvents();
1823
+ this._picker.remove();
1824
+ this._unwrapInput();
1825
+ }
1826
+
1827
+ // Setup
1828
+
1829
+ _createPicker() {
1830
+ this._picker = document.createElement('div');
1831
+ this._picker.className = 'color_picker';
1832
+ this._picker.classList.toggle('color_picker-no_alpha', !this._alpha);
1833
+ this._picker.innerHTML = PICKER_HTML;
1834
+ this._input.parentNode.appendChild(this._picker);
1835
+ this._colorArea = this._picker.querySelector('.color_picker-gradient');
1836
+ this._colorMarker = this._picker.querySelector('.color_picker-marker');
1837
+ this._hueSlider = this._picker.querySelector('.color_picker-hue input');
1838
+ this._hueMarker = this._picker.querySelector('.color_picker-hue div');
1839
+ this._alphaSlider = this._picker.querySelector('.color_picker-alpha input');
1840
+ this._alphaMarker = this._picker.querySelector('.color_picker-alpha div');
1841
+ this._swatchesContainer = this._picker.querySelector('.color_picker-swatches');
1842
+ this._hueSlider.setAttribute('aria-label', I18n$1.t('pageflow.ui.color_picker.hue'));
1843
+ if (this._alpha) {
1844
+ this._alphaSlider.setAttribute('aria-label', I18n$1.t('pageflow.ui.color_picker.opacity'));
1845
+ }
1846
+ }
1847
+ _wrapInput() {
1848
+ const parent = this._input.parentNode;
1849
+ if (!parent.classList.contains('color_picker-field')) {
1850
+ const wrapper = document.createElement('div');
1851
+ wrapper.innerHTML = '<span></span>';
1852
+ parent.insertBefore(wrapper, this._input);
1853
+ wrapper.className = 'color_picker-field';
1854
+ wrapper.appendChild(this._input);
1855
+ if (this._fallbackColorDescription) {
1856
+ this._descriptionElement = document.createElement('span');
1857
+ this._descriptionElement.hidden = true;
1858
+ this._descriptionElement.id = 'color_picker_desc_' + nextDescriptionId++;
1859
+ wrapper.appendChild(this._descriptionElement);
1860
+ this._input.setAttribute('aria-describedby', this._descriptionElement.id);
1861
+ }
1862
+ }
1863
+ }
1864
+ _unwrapInput() {
1865
+ const wrapper = this._input.parentNode;
1866
+ if (wrapper && wrapper.classList.contains('color_picker-field')) {
1867
+ const parent = wrapper.parentNode;
1868
+ parent.insertBefore(this._input, wrapper);
1869
+ parent.removeChild(wrapper);
1870
+ }
1871
+ }
1872
+ _renderSwatches() {
1873
+ const swatches = this._swatches.map(normalizeSwatch).filter(swatch => this._alpha || !isTranslucentSwatch(swatch.value));
1874
+ this._swatchesContainer.textContent = '';
1875
+ this._swatchesContainer.classList.toggle('color_picker-empty', !swatches.length);
1876
+ if (!swatches.length) {
1877
+ return;
1878
+ }
1879
+ groupSwatches(swatches).forEach((group, index) => {
1880
+ if (index > 0) {
1881
+ const divider = document.createElement('span');
1882
+ divider.className = 'color_picker-swatch_divider';
1883
+ this._swatchesContainer.appendChild(divider);
1884
+ }
1885
+ group.swatches.forEach(({
1886
+ value,
1887
+ text
1888
+ }) => {
1889
+ const button = document.createElement('button');
1890
+ button.setAttribute('type', 'button');
1891
+ button.title = text;
1892
+ button.style.color = value;
1893
+ button.textContent = value;
1894
+ this._swatchesContainer.appendChild(button);
1895
+ });
1896
+ });
1897
+ }
1898
+
1899
+ // Lifecycle
1900
+
1901
+ _open() {
1902
+ if (this._picker.classList.contains('color_picker-open')) {
1903
+ return;
1904
+ }
1905
+ this._picker.classList.add('color_picker-open');
1906
+ this._updatePosition();
1907
+ this._setColorFromStr(this._input.value);
1908
+ this._addDocListeners();
1909
+ }
1910
+ _close() {
1911
+ if (!this._picker.classList.contains('color_picker-open')) {
1912
+ return;
1913
+ }
1914
+ this._picker.classList.remove('color_picker-open');
1915
+ this._removeDocListeners();
1916
+ }
1917
+ _updatePosition() {
1918
+ const margin = 2;
1919
+ const pickerHeight = this._picker.offsetHeight;
1920
+ const pickerWidth = this._picker.offsetWidth;
1921
+ const inputRect = this._input.getBoundingClientRect();
1922
+ const clipRect = getClipRect(this._input);
1923
+ const spaceBelow = clipRect.bottom - inputRect.bottom;
1924
+ const flipTop = pickerHeight + margin > spaceBelow && pickerHeight + margin <= inputRect.top - clipRect.top;
1925
+ this._picker.classList.toggle('color_picker-top', flipTop);
1926
+ if (flipTop) {
1927
+ this._picker.style.top = 'auto';
1928
+ this._picker.style.bottom = `calc(100% + ${margin}px)`;
1929
+ } else {
1930
+ this._picker.style.top = `calc(100% + ${margin}px)`;
1931
+ this._picker.style.bottom = '';
1932
+ }
1933
+ if (inputRect.left + pickerWidth > clipRect.right) {
1934
+ this._picker.style.left = 'auto';
1935
+ this._picker.style.right = '0';
1936
+ } else {
1937
+ this._picker.style.left = '0';
1938
+ this._picker.style.right = '';
1939
+ }
1940
+ const areaRect = this._colorArea.getBoundingClientRect();
1941
+ this._colorAreaDims = {
1942
+ width: this._colorArea.offsetWidth,
1943
+ height: this._colorArea.offsetHeight,
1944
+ x: areaRect.x + window.scrollX,
1945
+ y: areaRect.y + window.scrollY
1946
+ };
1947
+ }
1948
+
1949
+ // Color interaction
1950
+
1951
+ _setColorFromStr(str, options) {
1952
+ this._updateColor(strToRGBA(str), options);
1953
+ const {
1954
+ h,
1955
+ s,
1956
+ v,
1957
+ a
1958
+ } = rgbaToHSVA(this._displayColor);
1959
+ this._hueSlider.value = h;
1960
+ this._picker.style.color = `hsl(${h}, 100%, 50%)`;
1961
+ this._hueMarker.style.left = `${h / 360 * 100}%`;
1962
+ this._colorMarker.style.left = `${this._colorAreaDims.width * s / 100}px`;
1963
+ this._colorMarker.style.top = `${this._colorAreaDims.height - this._colorAreaDims.height * v / 100}px`;
1964
+ this._alphaSlider.value = a * 100;
1965
+ this._alphaMarker.style.left = `${a * 100}%`;
1966
+ }
1967
+ _moveMarker(event) {
1968
+ let x = event.pageX - this._colorAreaDims.x;
1969
+ let y = event.pageY - this._colorAreaDims.y;
1970
+ this._setMarkerPosition(x, y);
1971
+ event.preventDefault();
1972
+ event.stopPropagation();
1973
+ }
1974
+ _moveMarkerOnKeydown(dx, dy) {
1975
+ let x = this._colorMarker.style.left.replace('px', '') * 1 + dx;
1976
+ let y = this._colorMarker.style.top.replace('px', '') * 1 + dy;
1977
+ this._setMarkerPosition(x, y);
1978
+ }
1979
+ _setHue() {
1980
+ const hue = this._hueSlider.value * 1;
1981
+ const x = this._colorMarker.style.left.replace('px', '') * 1;
1982
+ const y = this._colorMarker.style.top.replace('px', '') * 1;
1983
+ this._picker.style.color = `hsl(${hue}, 100%, 50%)`;
1984
+ this._hueMarker.style.left = `${hue / 360 * 100}%`;
1985
+ this._setColorAtPosition(x, y);
1986
+ }
1987
+ _setAlpha() {
1988
+ const alpha = this._alphaSlider.value / 100;
1989
+ this._alphaMarker.style.left = `${alpha * 100}%`;
1990
+ this._updateColor({
1991
+ a: alpha
1992
+ });
1993
+ this._syncInput();
1994
+ }
1995
+ _setMarkerPosition(x, y) {
1996
+ x = x < 0 ? 0 : x > this._colorAreaDims.width ? this._colorAreaDims.width : x;
1997
+ y = y < 0 ? 0 : y > this._colorAreaDims.height ? this._colorAreaDims.height : y;
1998
+ this._colorMarker.style.left = `${x}px`;
1999
+ this._colorMarker.style.top = `${y}px`;
2000
+ this._setColorAtPosition(x, y);
2001
+ this._colorMarker.focus();
2002
+ }
2003
+ _setColorAtPosition(x, y) {
2004
+ const hsva = {
2005
+ h: this._hueSlider.value * 1,
2006
+ s: x / this._colorAreaDims.width * 100,
2007
+ v: 100 - y / this._colorAreaDims.height * 100,
2008
+ a: this._alphaSlider.value / 100
2009
+ };
2010
+ const rgba = hsvaToRGBA(hsva);
2011
+ this._updateColor(rgba);
2012
+ this._syncInput();
2013
+ }
2014
+ _updateColor(rgba, {
2015
+ silent
2016
+ } = {}) {
2017
+ rgba = rgba || this._defaultColor;
2018
+ if (rgba && !this._alpha) rgba.a = 1;
2019
+ this._currentColor = rgba && {
2020
+ ...this._displayColor,
2021
+ ...rgba
2022
+ };
2023
+ this._displayColor = this._currentColor || this._fallbackColor || DEFAULT_DISPLAY_COLOR;
2024
+ const hex = rgbaToHex(this._displayColor);
2025
+ const opaqueHex = hex.substring(0, 7);
2026
+ this._colorMarker.style.color = opaqueHex;
2027
+ this._alphaMarker.parentNode.style.color = opaqueHex;
2028
+ this._alphaMarker.style.color = hex;
2029
+ const formatted = this._formatHex(this._currentColor);
2030
+ const fallbackHex = this._formatHex(this._fallbackColor);
2031
+ const wrapper = this._input.parentNode;
2032
+ if (wrapper && wrapper.classList.contains('color_picker-field')) {
2033
+ wrapper.style.color = formatted || fallbackHex || '';
2034
+ }
2035
+ if (this._descriptionElement) {
2036
+ this._descriptionElement.textContent = !formatted && fallbackHex ? `${this._fallbackColorDescription}: ${fallbackHex}` : '';
2037
+ }
2038
+
2039
+ // Force repaint the color and alpha gradients (Chrome workaround)
2040
+ this._colorArea.style.display = 'none';
2041
+ this._colorArea.offsetHeight;
2042
+ this._colorArea.style.display = '';
2043
+ this._alphaMarker.nextElementSibling.style.display = 'none';
2044
+ this._alphaMarker.nextElementSibling.offsetHeight;
2045
+ this._alphaMarker.nextElementSibling.style.display = '';
2046
+ if (!silent && this._onChange) this._onChange(formatted);
2047
+ }
2048
+ _syncInput() {
2049
+ this._input.value = this._formatHex(this._currentColor) || '';
2050
+ }
2051
+ _formatHex(rgba) {
2052
+ if (!rgba) return null;
2053
+ const hex = rgbaToHex(rgba);
2054
+ return rgba.a < 1 ? hex : hex.substring(0, 7);
2055
+ }
2056
+ _normalizeInputValue() {
2057
+ const hex = this._formatHex(this._currentColor) || '';
2058
+ if (hex !== this._input.value) {
2059
+ this._input.value = hex;
2060
+ }
2061
+ }
2062
+
2063
+ // Event wiring
2064
+
2065
+ _bindEvents() {
2066
+ this._onInputClick = () => {
2067
+ this._open();
2068
+ };
2069
+ this._onInputKeydown = event => {
2070
+ if (event.key === 'Enter') {
2071
+ this._normalizeInputValue();
2072
+ }
2073
+ if (this._picker.classList.contains('color_picker-open')) {
2074
+ return;
2075
+ }
2076
+ if (event.key === 'Enter' || event.key === 'ArrowDown' && event.altKey) {
2077
+ this._open();
2078
+ event.stopPropagation();
2079
+ }
2080
+ };
2081
+ this._onPickerMousedown = event => {
2082
+ this._picker.classList.remove('color_picker-keyboard_nav');
2083
+ event.stopPropagation();
2084
+ };
2085
+ this._onAreaPointerdown = event => {
2086
+ event.preventDefault();
2087
+ this._colorArea.setPointerCapture(event.pointerId);
2088
+ this._dragging = true;
2089
+ };
2090
+ this._onAreaPointermove = event => {
2091
+ if (this._dragging) {
2092
+ this._moveMarker(event);
2093
+ }
2094
+ };
2095
+ this._onAreaPointerup = () => {
2096
+ this._dragging = false;
2097
+ };
2098
+ this._onInputEvent = () => {
2099
+ if (this._picker.classList.contains('color_picker-open')) {
2100
+ this._setColorFromStr(this._input.value);
2101
+ } else {
2102
+ this._updateColor(strToRGBA(this._input.value));
2103
+ }
2104
+ };
2105
+ this._onSwatchClick = event => {
2106
+ if (event.target.closest('.color_picker-swatches button')) {
2107
+ this._setColorFromStr(event.target.closest('.color_picker-swatches button').textContent);
2108
+ this._syncInput();
2109
+ }
2110
+ };
2111
+ this._onMarkerKeydown = event => {
2112
+ const movements = {
2113
+ ArrowUp: [0, -1],
2114
+ ArrowDown: [0, 1],
2115
+ ArrowLeft: [-1, 0],
2116
+ ArrowRight: [1, 0]
2117
+ };
2118
+ if (movements[event.key]) {
2119
+ this._moveMarkerOnKeydown(...movements[event.key]);
2120
+ event.preventDefault();
2121
+ }
2122
+ };
2123
+ this._onAreaClick = event => this._moveMarker(event);
2124
+ this._onHueInput = () => this._setHue();
2125
+ this._onAlphaInput = () => this._setAlpha();
2126
+ const wrapper = this._input.parentNode;
2127
+ this._onFocusout = event => {
2128
+ if (event.target === this._input) {
2129
+ this._normalizeInputValue();
2130
+ }
2131
+ if (this._picker.classList.contains('color_picker-open')) {
2132
+ if (event.relatedTarget && !wrapper.contains(event.relatedTarget)) {
2133
+ this._close();
2134
+ }
2135
+ }
2136
+ };
2137
+ this._input.addEventListener('click', this._onInputClick);
2138
+ this._input.addEventListener('keydown', this._onInputKeydown);
2139
+ this._input.addEventListener('input', this._onInputEvent);
2140
+ wrapper.addEventListener('focusout', this._onFocusout);
2141
+ this._picker.addEventListener('mousedown', this._onPickerMousedown);
2142
+ this._colorArea.addEventListener('pointerdown', this._onAreaPointerdown);
2143
+ this._colorArea.addEventListener('pointermove', this._onAreaPointermove);
2144
+ this._colorArea.addEventListener('pointerup', this._onAreaPointerup);
2145
+ this._swatchesContainer.addEventListener('click', this._onSwatchClick);
2146
+ this._colorMarker.addEventListener('keydown', this._onMarkerKeydown);
2147
+ this._colorArea.addEventListener('click', this._onAreaClick);
2148
+ this._hueSlider.addEventListener('input', this._onHueInput);
2149
+ this._alphaSlider.addEventListener('input', this._onAlphaInput);
2150
+ }
2151
+ _unbindEvents() {
2152
+ this._input.removeEventListener('click', this._onInputClick);
2153
+ this._input.removeEventListener('keydown', this._onInputKeydown);
2154
+ this._input.removeEventListener('input', this._onInputEvent);
2155
+ const wrapper = this._input.parentNode;
2156
+ if (wrapper && wrapper.classList.contains('color_picker-field')) {
2157
+ wrapper.removeEventListener('focusout', this._onFocusout);
2158
+ }
2159
+ this._picker.removeEventListener('mousedown', this._onPickerMousedown);
2160
+ this._colorArea.removeEventListener('pointerdown', this._onAreaPointerdown);
2161
+ this._colorArea.removeEventListener('pointermove', this._onAreaPointermove);
2162
+ this._colorArea.removeEventListener('pointerup', this._onAreaPointerup);
2163
+ this._swatchesContainer.removeEventListener('click', this._onSwatchClick);
2164
+ this._colorMarker.removeEventListener('keydown', this._onMarkerKeydown);
2165
+ this._colorArea.removeEventListener('click', this._onAreaClick);
2166
+ this._hueSlider.removeEventListener('input', this._onHueInput);
2167
+ this._alphaSlider.removeEventListener('input', this._onAlphaInput);
2168
+ this._removeDocListeners();
2169
+ }
2170
+ _addDocListeners() {
2171
+ this._onDocMousedown = event => {
2172
+ this._picker.classList.remove('color_picker-keyboard_nav');
2173
+ if (!this._input.parentNode.contains(event.target)) {
2174
+ this._close();
2175
+ }
2176
+ };
2177
+ this._onDocKeydown = event => {
2178
+ if (event.key === 'Escape' || event.key === 'Enter' && event.target.tagName !== 'BUTTON') {
2179
+ this._close();
2180
+ this._input.focus({
2181
+ preventScroll: true
2182
+ });
2183
+ return;
2184
+ }
2185
+ const navKeys = ['Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
2186
+ if (navKeys.includes(event.key)) {
2187
+ this._picker.classList.add('color_picker-keyboard_nav');
2188
+ }
2189
+ };
2190
+ document.addEventListener('mousedown', this._onDocMousedown);
2191
+ document.addEventListener('keydown', this._onDocKeydown);
2192
+ }
2193
+ _removeDocListeners() {
2194
+ if (this._onDocMousedown) {
2195
+ document.removeEventListener('mousedown', this._onDocMousedown);
2196
+ document.removeEventListener('keydown', this._onDocKeydown);
2197
+ }
2198
+ }
2199
+ }
2200
+ function strToRGBA(str) {
2201
+ if (!str) return null;
2202
+ const regex = /^((rgba)|rgb)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i;
2203
+ let match, rgba;
2204
+ if (!ctx) {
2205
+ match = regex.exec(str);
2206
+ if (match) {
2207
+ return {
2208
+ r: match[3] * 1,
2209
+ g: match[4] * 1,
2210
+ b: match[5] * 1,
2211
+ a: match[6] * 1 || 1
2212
+ };
2213
+ }
2214
+ if (!/^#[0-9a-f]{3,8}$/i.test(str)) return null;
2215
+ let hex = str.replace('#', '');
2216
+ if (hex.length === 3 || hex.length === 4) {
2217
+ hex = hex.split('').map(c => c + c).join('');
2218
+ }
2219
+ if (hex.length !== 6 && hex.length !== 8) return null;
2220
+ match = hex.match(/.{2}/g).map(h => parseInt(h, 16));
2221
+ return {
2222
+ r: match[0],
2223
+ g: match[1],
2224
+ b: match[2],
2225
+ a: match[3] !== undefined ? match[3] / 255 : 1
2226
+ };
2227
+ }
2228
+ ctx.fillStyle = '#010101';
2229
+ ctx.fillStyle = str;
2230
+ if (ctx.fillStyle === '#010101') {
2231
+ ctx.fillStyle = '#020202';
2232
+ ctx.fillStyle = str;
2233
+ if (ctx.fillStyle === '#020202') return null;
2234
+ }
2235
+ match = regex.exec(ctx.fillStyle);
2236
+ if (match) {
2237
+ rgba = {
2238
+ r: match[3] * 1,
2239
+ g: match[4] * 1,
2240
+ b: match[5] * 1,
2241
+ a: match[6] * 1
2242
+ };
2243
+ } else {
2244
+ match = ctx.fillStyle.replace('#', '').match(/.{2}/g).map(h => parseInt(h, 16));
2245
+ rgba = {
2246
+ r: match[0],
2247
+ g: match[1],
2248
+ b: match[2],
2249
+ a: 1
2250
+ };
2251
+ }
2252
+ return rgba;
2253
+ }
2254
+ function rgbaToHSVA(rgba) {
2255
+ const red = rgba.r / 255;
2256
+ const green = rgba.g / 255;
2257
+ const blue = rgba.b / 255;
2258
+ const xmax = Math.max(red, green, blue);
2259
+ const xmin = Math.min(red, green, blue);
2260
+ const chroma = xmax - xmin;
2261
+ const value = xmax;
2262
+ let hue = 0;
2263
+ let saturation = 0;
2264
+ if (chroma) {
2265
+ if (xmax === red) {
2266
+ hue = (green - blue) / chroma;
2267
+ }
2268
+ if (xmax === green) {
2269
+ hue = 2 + (blue - red) / chroma;
2270
+ }
2271
+ if (xmax === blue) {
2272
+ hue = 4 + (red - green) / chroma;
2273
+ }
2274
+ if (xmax) {
2275
+ saturation = chroma / xmax;
2276
+ }
2277
+ }
2278
+ hue = Math.floor(hue * 60);
2279
+ return {
2280
+ h: hue < 0 ? hue + 360 : hue,
2281
+ s: Math.round(saturation * 100),
2282
+ v: Math.round(value * 100),
2283
+ a: rgba.a
2284
+ };
2285
+ }
2286
+ function hsvaToRGBA(hsva) {
2287
+ const saturation = hsva.s / 100;
2288
+ const value = hsva.v / 100;
2289
+ let chroma = saturation * value;
2290
+ let hueBy60 = hsva.h / 60;
2291
+ let x = chroma * (1 - Math.abs(hueBy60 % 2 - 1));
2292
+ let m = value - chroma;
2293
+ chroma = chroma + m;
2294
+ x = x + m;
2295
+ const index = Math.floor(hueBy60) % 6;
2296
+ const red = [chroma, x, m, m, x, chroma][index];
2297
+ const green = [x, chroma, chroma, x, m, m][index];
2298
+ const blue = [m, m, x, chroma, chroma, x][index];
2299
+ return {
2300
+ r: Math.round(red * 255),
2301
+ g: Math.round(green * 255),
2302
+ b: Math.round(blue * 255),
2303
+ a: hsva.a
2304
+ };
2305
+ }
2306
+ function rgbaToHex(rgba) {
2307
+ let R = rgba.r.toString(16);
2308
+ let G = rgba.g.toString(16);
2309
+ let B = rgba.b.toString(16);
2310
+ let A = Math.round(rgba.a * 255).toString(16);
2311
+ if (rgba.r < 16) {
2312
+ R = '0' + R;
2313
+ }
2314
+ if (rgba.g < 16) {
2315
+ G = '0' + G;
2316
+ }
2317
+ if (rgba.b < 16) {
2318
+ B = '0' + B;
2319
+ }
2320
+ if (rgba.a * 255 < 16) {
2321
+ A = '0' + A;
2322
+ }
2323
+ return '#' + R + G + B + A;
2324
+ }
2325
+ function getClipRect(element) {
2326
+ const viewport = {
2327
+ top: 0,
2328
+ left: 0,
2329
+ right: document.documentElement.clientWidth,
2330
+ bottom: document.documentElement.clientHeight
2331
+ };
2332
+ let ancestor = element.parentElement;
2333
+ while (ancestor && ancestor !== document.documentElement) {
2334
+ const overflow = getComputedStyle(ancestor).overflow;
2335
+ if (overflow !== 'visible') {
2336
+ const rect = ancestor.getBoundingClientRect();
2337
+ return {
2338
+ top: Math.max(viewport.top, rect.top),
2339
+ left: Math.max(viewport.left, rect.left),
2340
+ right: Math.min(viewport.right, rect.right),
2341
+ bottom: Math.min(viewport.bottom, rect.bottom)
2342
+ };
2343
+ }
2344
+ ancestor = ancestor.parentElement;
2345
+ }
2346
+ return viewport;
2347
+ }
2348
+ function isTranslucentSwatch(str) {
2349
+ return str.length > 7 && str.slice(-2).toLowerCase() !== 'ff';
2350
+ }
2351
+ function normalizeSwatch(swatch) {
2352
+ return typeof swatch === 'string' ? {
2353
+ value: swatch,
2354
+ text: swatch
2355
+ } : {
2356
+ value: swatch.value,
2357
+ text: swatch.text || swatch.value,
2358
+ group: swatch.group
2359
+ };
2360
+ }
2361
+ function groupSwatches(swatches) {
2362
+ const groups = [];
2363
+ const groupsByKey = new Map();
2364
+ swatches.forEach(swatch => {
2365
+ const key = swatch.group || '';
2366
+ let group = groupsByKey.get(key);
2367
+ if (!group) {
2368
+ group = {
2369
+ key,
2370
+ swatches: []
2371
+ };
2372
+ groupsByKey.set(key, group);
2373
+ groups.push(group);
2374
+ }
2375
+ group.swatches.push(swatch);
2376
+ });
2377
+ return groups;
2378
+ }
2379
+
1827
2380
  function template$8(data) {
1828
2381
  var __p = '';
1829
2382
  __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<input type="text" dir="auto" autocomplete="off" />\n';
@@ -1858,75 +2411,75 @@ return __p
1858
2411
  * is used as placeholderColor option, it will be passed the value of the
1859
2412
  * placeholderColorBinding attribute each time it changes.
1860
2413
  *
1861
- * @param {string[]} [options.swatches]
1862
- * Preset color values to be displayed inside the picker drop
1863
- * down. The default value, if present, is always used as the
1864
- * first swatch automatically.
2414
+ * @param {boolean} [options.alpha]
2415
+ * Allow picking colors with alpha channel. When enabled, translucent
2416
+ * colors are stored in `#rrggbbaa` format. Fully opaque colors still
2417
+ * use `#rrggbb`.
2418
+ *
2419
+ * @param {Array<string|{value: string, text: string, group: string}>} [options.swatches]
2420
+ * Preset colors to be displayed inside the picker drop down. Each
2421
+ * entry is either a color string or an object with a `value` color,
2422
+ * a `text` label shown as the swatch's tooltip and an optional
2423
+ * `group` key. Consecutive swatches with a different `group` are
2424
+ * separated by a divider in the picker. The default value, if
2425
+ * present, is always used as the first swatch automatically. Swatches
2426
+ * are deduplicated by value.
1865
2427
  *
1866
2428
  * @class
1867
2429
  */
1868
- var ColorInputView = Marionette.ItemView.extend({
2430
+ const ColorInputView = Marionette.ItemView.extend({
1869
2431
  mixins: [inputView, inputWithPlaceholderText],
1870
2432
  template: template$8,
1871
2433
  className: 'color_input',
1872
2434
  ui: {
1873
2435
  input: 'input'
1874
2436
  },
1875
- events: {
1876
- 'mousedown': 'refreshPicker'
1877
- },
1878
- onRender: function onRender() {
2437
+ onRender: function () {
1879
2438
  this.setupAttributeBinding('placeholderColor', this.updatePlaceholderColor);
1880
- this.ui.input.minicolors({
1881
- changeDelay: 200,
1882
- change: _.bind(function (color) {
1883
- this._saving = true;
1884
- if (color === this.defaultValue()) {
1885
- this.model.unset(this.options.propertyName);
1886
- } else {
1887
- this.model.set(this.options.propertyName, color);
1888
- }
1889
- this._saving = false;
1890
- }, this)
2439
+ this._colorPicker = new ColorPicker(this.ui.input[0], {
2440
+ alpha: this.options.alpha,
2441
+ defaultValue: this.defaultValue(),
2442
+ fallbackColor: this.getAttributeBoundOption('placeholderColor'),
2443
+ fallbackColorDescription: this.options.placeholderColorDescription,
2444
+ swatches: this.getSwatches(),
2445
+ onChange: this._debouncedOnChange()
1891
2446
  });
1892
2447
  this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
1893
2448
  if (this.options.defaultValueBinding) {
1894
2449
  this.listenTo(this.model, 'change:' + this.options.defaultValueBinding, this.updateSettings);
1895
2450
  }
1896
- this.updateSettings();
2451
+ this.load();
1897
2452
  },
1898
- updatePlaceholderColor: function updatePlaceholderColor(value) {
1899
- this.el.style.setProperty('--placeholder-color', value);
2453
+ updatePlaceholderColor(value) {
2454
+ if (this._colorPicker) {
2455
+ this._colorPicker.update({
2456
+ fallbackColor: value
2457
+ });
2458
+ }
1900
2459
  },
1901
- updateSettings: function updateSettings() {
1902
- this.resetSwatchesInStoredSettings();
1903
- this.ui.input.minicolors('settings', {
2460
+ updateSettings: function () {
2461
+ this._colorPicker.update({
1904
2462
  defaultValue: this.defaultValue(),
1905
2463
  swatches: this.getSwatches()
1906
2464
  });
1907
2465
  this.load();
1908
2466
  },
1909
- // see https://github.com/claviska/jquery-minicolors/issues/287
1910
- resetSwatchesInStoredSettings: function resetSwatchesInStoredSettings() {
1911
- var settings = this.ui.input.data('minicolors-settings');
1912
- if (settings) {
1913
- delete settings.swatches;
1914
- this.ui.input.data('minicolors-settings', settings);
1915
- }
1916
- },
1917
- load: function load() {
2467
+ load: function () {
2468
+ var color = this.model.get(this.options.propertyName) || this.defaultValue() || '';
1918
2469
  if (!this._saving) {
1919
- this.ui.input.minicolors('value', this.model.get(this.options.propertyName) || this.defaultValue());
2470
+ this._colorPicker.setValue(color);
1920
2471
  }
1921
2472
  this.$el.toggleClass('is_default', !this.model.has(this.options.propertyName));
1922
2473
  },
1923
- refreshPicker: function refreshPicker() {
1924
- this.ui.input.minicolors('value', {});
2474
+ onBeforeClose: function () {
2475
+ this._colorPicker.destroy();
1925
2476
  },
1926
- getSwatches: function getSwatches() {
1927
- return _.chain([this.defaultValue(), this.options.swatches]).flatten().uniq().compact().value();
2477
+ getSwatches: function () {
2478
+ return _.chain([this.defaultValue(), this.options.swatches]).flatten().compact().map(swatch => typeof swatch === 'string' ? {
2479
+ value: swatch
2480
+ } : swatch).uniq(swatch => swatch.value).value();
1928
2481
  },
1929
- defaultValue: function defaultValue() {
2482
+ defaultValue: function () {
1930
2483
  var bindingValue;
1931
2484
  if (this.options.defaultValueBinding) {
1932
2485
  bindingValue = this.model.get(this.options.defaultValueBinding);
@@ -1938,6 +2491,19 @@ var ColorInputView = Marionette.ItemView.extend({
1938
2491
  } else {
1939
2492
  return bindingValue;
1940
2493
  }
2494
+ },
2495
+ _debouncedOnChange: function () {
2496
+ const handler = _.bind(this._onChange, this);
2497
+ return this.options.disableChangeDebounce ? handler : _.debounce(handler, 200);
2498
+ },
2499
+ _onChange: function (color) {
2500
+ this._saving = true;
2501
+ if (!color || color === this.defaultValue()) {
2502
+ this.model.unset(this.options.propertyName);
2503
+ } else {
2504
+ this.model.set(this.options.propertyName, color);
2505
+ }
2506
+ this._saving = false;
1941
2507
  }
1942
2508
  });
1943
2509
 
@@ -1953,8 +2519,9 @@ return __p
1953
2519
  *
1954
2520
  * @param {Object} [options]
1955
2521
  *
1956
- * @param {string[]} [options.values]
1957
- * List of possible values to persist in the attribute.
2522
+ * @param {string[]|function} [options.values]
2523
+ * List of possible values to persist in the attribute. Pass a
2524
+ * function returning the list to defer reading the values.
1958
2525
  *
1959
2526
  * @param {number} [options.defaultValue]
1960
2527
  * Default value to display if property is not set.
@@ -2031,7 +2598,7 @@ return __p
2031
2598
  *
2032
2599
  * @class
2033
2600
  */
2034
- var SelectInputView = Marionette.ItemView.extend({
2601
+ const SelectInputView = Marionette.ItemView.extend({
2035
2602
  mixins: [inputView],
2036
2603
  template: template$9,
2037
2604
  events: {
@@ -2041,7 +2608,8 @@ var SelectInputView = Marionette.ItemView.extend({
2041
2608
  select: 'select',
2042
2609
  input: 'select'
2043
2610
  },
2044
- initialize: function initialize() {
2611
+ initialize: function () {
2612
+ this.options.values = _.result(this.options, 'values');
2045
2613
  if (this.options.collection) {
2046
2614
  this.options.values = _.pluck(this.options.collection, this.options.valueProperty);
2047
2615
  if (this.options.textProperty) {
@@ -2075,7 +2643,7 @@ var SelectInputView = Marionette.ItemView.extend({
2075
2643
  }
2076
2644
  this.optGroups = {};
2077
2645
  },
2078
- onRender: function onRender() {
2646
+ onRender: function () {
2079
2647
  this.appendBlank();
2080
2648
  this.appendPlaceholder();
2081
2649
  this.appendOptions();
@@ -2085,7 +2653,7 @@ var SelectInputView = Marionette.ItemView.extend({
2085
2653
  this.save();
2086
2654
  }
2087
2655
  },
2088
- appendBlank: function appendBlank() {
2656
+ appendBlank: function () {
2089
2657
  if (!this.options.includeBlank) {
2090
2658
  return;
2091
2659
  }
@@ -2102,7 +2670,7 @@ var SelectInputView = Marionette.ItemView.extend({
2102
2670
  option.text = blankText;
2103
2671
  this.ui.select.append(option);
2104
2672
  },
2105
- appendPlaceholder: function appendPlaceholder() {
2673
+ appendPlaceholder: function () {
2106
2674
  if (!this.options.placeholderModel && !this.options.placeholderValue) {
2107
2675
  return;
2108
2676
  }
@@ -2117,7 +2685,7 @@ var SelectInputView = Marionette.ItemView.extend({
2117
2685
  this.ui.select.append(option);
2118
2686
  }
2119
2687
  },
2120
- appendOptions: function appendOptions() {
2688
+ appendOptions: function () {
2121
2689
  _.each(this.options.values, function (value, index) {
2122
2690
  var option = document.createElement('option');
2123
2691
  var group = this.options.groups[index];
@@ -2134,7 +2702,7 @@ var SelectInputView = Marionette.ItemView.extend({
2134
2702
  }
2135
2703
  }, this);
2136
2704
  },
2137
- findOrCreateOptGroup: function findOrCreateOptGroup(label) {
2705
+ findOrCreateOptGroup: function (label) {
2138
2706
  if (!this.optGroups[label]) {
2139
2707
  this.optGroups[label] = $('<optgroup />', {
2140
2708
  label: label
@@ -2142,15 +2710,15 @@ var SelectInputView = Marionette.ItemView.extend({
2142
2710
  }
2143
2711
  return this.optGroups[label];
2144
2712
  },
2145
- save: function save() {
2146
- var value = this.ui.select.val();
2713
+ save: function () {
2714
+ const value = this.ui.select.val();
2147
2715
  if ('defaultValue' in this.options && value === this.options.defaultValue) {
2148
2716
  this.model.unset(this.options.propertyName);
2149
2717
  } else {
2150
2718
  this.model.set(this.options.propertyName, value);
2151
2719
  }
2152
2720
  },
2153
- load: function load() {
2721
+ load: function () {
2154
2722
  if (!this.isClosed) {
2155
2723
  var value = this.model.get(this.options.propertyName);
2156
2724
  if (this.model.has(this.options.propertyName) && this.ui.select.find('option[value="' + value + '"]:not([disabled])').length) {
@@ -2164,9 +2732,9 @@ var SelectInputView = Marionette.ItemView.extend({
2164
2732
  }
2165
2733
  });
2166
2734
 
2167
- var ExtendedSelectInputView = SelectInputView.extend({
2735
+ const ExtendedSelectInputView = SelectInputView.extend({
2168
2736
  className: 'extended_select_input',
2169
- initialize: function initialize() {
2737
+ initialize: function () {
2170
2738
  SelectInputView.prototype.initialize.apply(this, arguments);
2171
2739
  if (this.options.collection) {
2172
2740
  if (this.options.descriptionProperty) {
@@ -2181,18 +2749,18 @@ var ExtendedSelectInputView = SelectInputView.extend({
2181
2749
  });
2182
2750
  }
2183
2751
  },
2184
- onRender: function onRender() {
2752
+ onRender: function () {
2185
2753
  var view = this,
2186
2754
  options = this.options;
2187
2755
  SelectInputView.prototype.onRender.apply(this, arguments);
2188
2756
  $.widget("custom.extendedselectmenu", $.ui.selectmenu, {
2189
- _renderItem: function _renderItem(ul, item) {
2757
+ _renderItem: function (ul, item) {
2190
2758
  var widget = this;
2191
2759
  var li = $('<li>', {
2192
- "class": item.value
2760
+ class: item.value
2193
2761
  });
2194
2762
  var container = $('<div>', {
2195
- "class": 'text-container'
2763
+ class: 'text-container'
2196
2764
  }).appendTo(li);
2197
2765
  var index = options.values.indexOf(item.value);
2198
2766
  if (item.disabled) {
@@ -2200,16 +2768,16 @@ var ExtendedSelectInputView = SelectInputView.extend({
2200
2768
  }
2201
2769
  if (options.pictogramClass) {
2202
2770
  $('<span>', {
2203
- "class": options.pictogramClass
2771
+ class: options.pictogramClass
2204
2772
  }).prependTo(li);
2205
2773
  }
2206
2774
  $('<p>', {
2207
2775
  text: item.label,
2208
- "class": 'item-text'
2776
+ class: 'item-text'
2209
2777
  }).appendTo(container);
2210
2778
  $('<p>', {
2211
2779
  text: options.descriptions[index],
2212
- "class": 'item-description'
2780
+ class: 'item-description'
2213
2781
  }).appendTo(container);
2214
2782
  if (options.helpLinkClicked) {
2215
2783
  $('<a>', {
@@ -2223,7 +2791,7 @@ var ExtendedSelectInputView = SelectInputView.extend({
2223
2791
  }
2224
2792
  return li.appendTo(ul);
2225
2793
  },
2226
- _resizeMenu: function _resizeMenu() {
2794
+ _resizeMenu: function () {
2227
2795
  this.menuWrap.addClass('extended_select_input_menu');
2228
2796
  var menuHeight = this.menu.height(),
2229
2797
  menuOffset = this.button.offset().top + this.button.outerHeight(),
@@ -2249,7 +2817,7 @@ var ExtendedSelectInputView = SelectInputView.extend({
2249
2817
  }
2250
2818
  });
2251
2819
  },
2252
- select: function select(event, ui) {
2820
+ select: function (event, ui) {
2253
2821
  this.ui.select.val(ui.item.value);
2254
2822
  this.save();
2255
2823
  }
@@ -2312,7 +2880,7 @@ return __p
2312
2880
  *
2313
2881
  * @class
2314
2882
  */
2315
- var TextAreaInputView = Marionette.ItemView.extend({
2883
+ const TextAreaInputView = Marionette.ItemView.extend({
2316
2884
  mixins: [inputView, inputWithPlaceholderText],
2317
2885
  template: template$a,
2318
2886
  className: 'text_area_input',
@@ -2338,7 +2906,7 @@ var TextAreaInputView = Marionette.ItemView.extend({
2338
2906
  'change .open_in_new_tab': 'setTargetFromOpenInNewTabCheckBox',
2339
2907
  'change .display_url': 'setUrlFromDisplayUrl'
2340
2908
  },
2341
- onRender: function onRender() {
2909
+ onRender: function () {
2342
2910
  this.ui.input.addClass(this.options.size);
2343
2911
  this.load();
2344
2912
  this.updatePlaceholder();
@@ -2407,16 +2975,26 @@ var TextAreaInputView = Marionette.ItemView.extend({
2407
2975
  this.editor.on('change', _.bind(this.save, this));
2408
2976
  this.editor.on('aftercommand:composer', _.bind(this.save, this));
2409
2977
  },
2410
- onClose: function onClose() {
2978
+ onClose: function () {
2411
2979
  this.editor.fire('destroy:composer');
2412
2980
  },
2413
- save: function save() {
2981
+ updateDisabled: function () {
2982
+ inputView.updateDisabled.call(this);
2983
+ if (this.isDisabled()) {
2984
+ this.editor.disable();
2985
+ this.$el.attr('inert', true);
2986
+ } else {
2987
+ this.editor.enable();
2988
+ this.$el.removeAttr('inert');
2989
+ }
2990
+ },
2991
+ save: function () {
2414
2992
  this.model.set(this.options.propertyName, this.editor.getValue());
2415
2993
  },
2416
- load: function load() {
2994
+ load: function () {
2417
2995
  this.ui.input.val(this.model.get(this.options.propertyName));
2418
2996
  },
2419
- setupUrlLinkPanel: function setupUrlLinkPanel() {
2997
+ setupUrlLinkPanel: function () {
2420
2998
  this.editor.on('show:dialog', _.bind(function () {
2421
2999
  this.ui.linkDialog.toggleClass('for_existing_link', this.ui.linkButton.hasClass('wysihtml5-command-active'));
2422
3000
  var currentUrl = this.ui.urlInput.val();
@@ -2429,7 +3007,7 @@ var TextAreaInputView = Marionette.ItemView.extend({
2429
3007
  }
2430
3008
  }, this));
2431
3009
  },
2432
- setupFragmentLinkPanel: function setupFragmentLinkPanel() {
3010
+ setupFragmentLinkPanel: function () {
2433
3011
  if (this.options.fragmentLinkInputView) {
2434
3012
  this.fragmentLinkModel = new Backbone.Model();
2435
3013
  this.listenTo(this.fragmentLinkModel, 'change', function (model, options) {
@@ -2457,7 +3035,7 @@ var TextAreaInputView = Marionette.ItemView.extend({
2457
3035
  this.ui.fragmentLinkPanel.hide();
2458
3036
  }
2459
3037
  },
2460
- initLinkTypePanels: function initLinkTypePanels(isUrlLink) {
3038
+ initLinkTypePanels: function (isUrlLink) {
2461
3039
  if (isUrlLink) {
2462
3040
  this.ui.urlLinkRadioButton.prop('checked', true);
2463
3041
  } else {
@@ -2465,23 +3043,23 @@ var TextAreaInputView = Marionette.ItemView.extend({
2465
3043
  }
2466
3044
  this.ui.toolbar.toggleClass('fragment_link_panel_active', !isUrlLink);
2467
3045
  },
2468
- showUrlLinkPanel: function showUrlLinkPanel() {
3046
+ showUrlLinkPanel: function () {
2469
3047
  this.ui.toolbar.removeClass('fragment_link_panel_active');
2470
3048
  this.setUrlFromDisplayUrl();
2471
3049
  this.setTargetFromOpenInNewTabCheckBox();
2472
3050
  },
2473
- showFragmentLinkPanel: function showFragmentLinkPanel() {
3051
+ showFragmentLinkPanel: function () {
2474
3052
  this.ui.toolbar.addClass('fragment_link_panel_active');
2475
3053
  this.setInputsFromFragmentLinkModel();
2476
3054
  },
2477
- setInputsFromFragmentLinkModel: function setInputsFromFragmentLinkModel() {
3055
+ setInputsFromFragmentLinkModel: function () {
2478
3056
  this.ui.urlInput.val('#' + (this.fragmentLinkModel.get('linkId') || ''));
2479
3057
  this.ui.targetInput.val('_self');
2480
3058
  },
2481
- setUrlFromDisplayUrl: function setUrlFromDisplayUrl() {
3059
+ setUrlFromDisplayUrl: function () {
2482
3060
  this.ui.urlInput.val(this.ui.displayUrlInput.val());
2483
3061
  },
2484
- setTargetFromOpenInNewTabCheckBox: function setTargetFromOpenInNewTabCheckBox() {
3062
+ setTargetFromOpenInNewTabCheckBox: function () {
2485
3063
  this.ui.targetInput.val(this.ui.openInNewTabCheckBox.is(':checked') ? '_blank' : '_self');
2486
3064
  }
2487
3065
  });
@@ -2509,7 +3087,7 @@ return __p
2509
3087
  *
2510
3088
  * @class
2511
3089
  */
2512
- var UrlInputView = Marionette.Layout.extend( /** @lends UrlInputView.prototype */{
3090
+ const UrlInputView = Marionette.Layout.extend( /** @lends UrlInputView.prototype */{
2513
3091
  mixins: [inputView],
2514
3092
  template: template$b,
2515
3093
  ui: {
@@ -2519,74 +3097,85 @@ var UrlInputView = Marionette.Layout.extend( /** @lends UrlInputView.prototype *
2519
3097
  events: {
2520
3098
  'change': 'onChange'
2521
3099
  },
2522
- onRender: function onRender() {
3100
+ onRender: function () {
2523
3101
  this.ui.validation.hide();
2524
3102
  this.load();
2525
3103
  this.validate();
3104
+ this.listenTo(this.model, 'change:' + this.options.displayPropertyName, this.load);
2526
3105
  },
2527
- onChange: function onChange() {
2528
- var _this = this;
2529
- this.validate().then(function () {
2530
- return _this.save();
2531
- }, function () {
2532
- return _this.saveDisplayProperty();
2533
- });
3106
+ onChange: function () {
3107
+ var value = this.ui.input.val();
3108
+ this.validate().then(() => this.save(value), () => this.saveDisplayProperty(value));
2534
3109
  },
2535
- saveDisplayProperty: function saveDisplayProperty() {
3110
+ saveDisplayProperty: function (value) {
2536
3111
  this.model.unset(this.options.propertyName, {
2537
3112
  silent: true
2538
3113
  });
2539
- this.model.set(this.options.displayPropertyName, this.ui.input.val());
3114
+ this.model.set(this.options.displayPropertyName, value);
2540
3115
  },
2541
- save: function save() {
2542
- var _this2 = this;
2543
- var value = this.ui.input.val();
2544
- $.when(this.transformPropertyValue(value)).then(function (transformedValue) {
2545
- _this2.model.set(_defineProperty(_defineProperty({}, _this2.options.displayPropertyName, value), _this2.options.propertyName, transformedValue));
3116
+ save: function (value) {
3117
+ $.when(this.transformPropertyValue(value, this.lastValidationResult)).then(transformedValue => {
3118
+ this.model.set({
3119
+ [this.options.displayPropertyName]: value,
3120
+ [this.options.propertyName]: transformedValue
3121
+ });
2546
3122
  });
2547
3123
  },
2548
- load: function load() {
3124
+ load: function () {
2549
3125
  this.ui.input.val(this.model.has(this.options.displayPropertyName) ? this.model.get(this.options.displayPropertyName) : this.model.get(this.options.propertyName));
2550
3126
  this.onLoad();
2551
3127
  },
2552
3128
  /**
2553
3129
  * Override to be notified when the input has been loaded.
2554
3130
  */
2555
- onLoad: function onLoad() {},
3131
+ onLoad: function () {},
2556
3132
  /**
2557
3133
  * Override to validate the untransformed url. Validation error
2558
3134
  * message can be passed as rejected promise. Progress notifications
2559
3135
  * are displayed. Only valid urls are stored in the configuration.
2560
3136
  *
3137
+ * Can optionally return a value when the promise resolves. This
3138
+ * value will be passed to transformPropertyValue as the second
3139
+ * parameter.
3140
+ *
2561
3141
  * @return Promise
2562
3142
  */
2563
- validateUrl: function validateUrl(url) {
3143
+ validateUrl: function (url) {
2564
3144
  return $.Deferred().resolve().promise();
2565
3145
  },
2566
3146
  /**
2567
3147
  * Override to transform the property value before it is stored.
2568
3148
  *
3149
+ * @param {String} value - The URL value to transform
3150
+ * @param {*} validationResult - Optional result returned by validateUrl
2569
3151
  * @return Promise | String
2570
3152
  */
2571
- transformPropertyValue: function transformPropertyValue(value) {
3153
+ transformPropertyValue: function (value, validationResult) {
2572
3154
  return value;
2573
3155
  },
2574
3156
  /**
2575
3157
  * Override to change the list of supported host names.
2576
3158
  */
2577
- supportedHosts: function supportedHosts() {
3159
+ supportedHosts: function () {
2578
3160
  return this.options.supportedHosts;
2579
3161
  },
3162
+ /**
3163
+ * Override to enable HTTPS URLs by default.
3164
+ * @return {boolean}
3165
+ */
3166
+ permitHttps: function () {
3167
+ return this.options.permitHttps;
3168
+ },
2580
3169
  // Host names used to be expected to include protocols. Remove
2581
3170
  // protocols for backwards compatilbity. Since supportedHosts
2582
3171
  // is supposed to be overridden in subclasses, we do it in a
2583
3172
  // separate method.
2584
- supportedHostsWithoutLegacyProtocols: function supportedHostsWithoutLegacyProtocols() {
3173
+ supportedHostsWithoutLegacyProtocols: function () {
2585
3174
  return _.map(this.supportedHosts(), function (host) {
2586
3175
  return host.replace(/^https?:\/\//, '');
2587
3176
  });
2588
3177
  },
2589
- validate: function validate(success) {
3178
+ validate: function (success) {
2590
3179
  var view = this;
2591
3180
  var options = this.options;
2592
3181
  var value = this.ui.input.val();
@@ -2594,7 +3183,7 @@ var UrlInputView = Marionette.Layout.extend( /** @lends UrlInputView.prototype *
2594
3183
  displayValidationError(I18n$1.t('pageflow.ui.views.inputs.url_input_view.required_field'));
2595
3184
  } else if (value && !isValidUrl(value)) {
2596
3185
  var errorMessage = I18n$1.t('pageflow.ui.views.inputs.url_input_view.url_hint');
2597
- if (options.permitHttps) {
3186
+ if (view.permitHttps()) {
2598
3187
  errorMessage = I18n$1.t('pageflow.ui.views.inputs.url_input_view.url_hint_https');
2599
3188
  }
2600
3189
  displayValidationError(errorMessage);
@@ -2604,16 +3193,24 @@ var UrlInputView = Marionette.Layout.extend( /** @lends UrlInputView.prototype *
2604
3193
  }).join(''));
2605
3194
  } else {
2606
3195
  return view.validateUrl(value).progress(function (message) {
2607
- displayValidationPending(message);
2608
- }).done(function () {
2609
- resetValidationError();
3196
+ if (!view.isClosed) {
3197
+ displayValidationPending(message);
3198
+ }
3199
+ }).done(function (result) {
3200
+ view.lastValidationResult = result;
3201
+ if (!view.isClosed) {
3202
+ resetValidationError();
3203
+ }
2610
3204
  }).fail(function (error) {
2611
- displayValidationError(error);
3205
+ view.lastValidationResult = undefined;
3206
+ if (!view.isClosed) {
3207
+ displayValidationError(error);
3208
+ }
2612
3209
  });
2613
3210
  }
2614
3211
  return $.Deferred().reject().promise();
2615
3212
  function isValidUrl(url) {
2616
- return options.permitHttps ? url.match(/^https?:\/\//i) : url.match(/^http:\/\//i);
3213
+ return view.permitHttps() ? url.match(/^https?:\/\//i) : url.match(/^http:\/\//i);
2617
3214
  }
2618
3215
  function hasSupportedHost(url) {
2619
3216
  return _.any(view.supportedHostsWithoutLegacyProtocols(), function (host) {
@@ -2633,6 +3230,7 @@ var UrlInputView = Marionette.Layout.extend( /** @lends UrlInputView.prototype *
2633
3230
  function resetValidationError(message) {
2634
3231
  view.$el.removeClass('invalid');
2635
3232
  view.ui.input.attr('aria-invalid', 'false');
3233
+ view.ui.validation.text('');
2636
3234
  view.ui.validation.hide();
2637
3235
  }
2638
3236
  }
@@ -2684,9 +3282,9 @@ var UrlInputView = Marionette.Layout.extend( /** @lends UrlInputView.prototype *
2684
3282
  *
2685
3283
  * @class
2686
3284
  */
2687
- var ProxyUrlInputView = UrlInputView.extend( /** @lends ProxyUrlInputView.prototype */{
3285
+ const ProxyUrlInputView = UrlInputView.extend( /** @lends ProxyUrlInputView.prototype */{
2688
3286
  // @override
2689
- validateUrl: function validateUrl(url) {
3287
+ validateUrl: function (url) {
2690
3288
  var view = this;
2691
3289
  return $.Deferred(function (deferred) {
2692
3290
  deferred.notify(I18n$1.t('pageflow.ui.views.inputs.proxy_url_input_view.url_validation'));
@@ -2701,14 +3299,14 @@ var ProxyUrlInputView = UrlInputView.extend( /** @lends ProxyUrlInputView.protot
2701
3299
  }).promise();
2702
3300
  },
2703
3301
  // override
2704
- transformPropertyValue: function transformPropertyValue(url) {
3302
+ transformPropertyValue: function (url) {
2705
3303
  return this.rewriteUrl(url);
2706
3304
  },
2707
3305
  // override
2708
- supportedHosts: function supportedHosts() {
3306
+ supportedHosts: function () {
2709
3307
  return _.pluck(this.options.proxies, 'url');
2710
3308
  },
2711
- rewriteUrl: function rewriteUrl(url) {
3309
+ rewriteUrl: function (url) {
2712
3310
  _.each(this.options.proxies, function (proxy) {
2713
3311
  url = url.replace(new RegExp('^' + proxy.url + '/?'), proxy.base_path + '/');
2714
3312
  });
@@ -2717,8 +3315,12 @@ var ProxyUrlInputView = UrlInputView.extend( /** @lends ProxyUrlInputView.protot
2717
3315
  });
2718
3316
 
2719
3317
  function template$c(data) {
2720
- var __p = '';
2721
- __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<div class="value"></div>\n<div class="slider"></div>\n';
3318
+ var __t, __p = '';
3319
+ __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<div class="slider_wrapper">\n ';
3320
+ if (data.icon) {__p += '<img class="icon" src="' +
3321
+ ((__t = ( data.icon )) == null ? '' : __t) +
3322
+ '" width="18" height="18" />';
3323
+ }__p += '\n <div class="value"></div>\n <div class="slider"></div>\n</div>\n';
2722
3324
  return __p
2723
3325
  }
2724
3326
 
@@ -2747,39 +3349,48 @@ return __p
2747
3349
  * Already update the model while dragging the handle - not only after
2748
3350
  * handle has been released.
2749
3351
  *
3352
+ * @param {string} [options.icon]
3353
+ * Path to an icon image to display before the label.
3354
+ *
2750
3355
  * @class
2751
3356
  */
2752
- var SliderInputView = Marionette.ItemView.extend({
3357
+ const SliderInputView = Marionette.ItemView.extend({
2753
3358
  mixins: [inputView],
2754
3359
  className: 'slider_input',
2755
3360
  template: template$c,
3361
+ serializeData() {
3362
+ return {
3363
+ icon: this.options.icon
3364
+ };
3365
+ },
2756
3366
  ui: {
2757
3367
  widget: '.slider',
2758
3368
  value: '.value'
2759
3369
  },
2760
3370
  events: {
3371
+ 'slidestart': 'handleSlideStart',
2761
3372
  'slidechange': 'save',
2762
3373
  'slide': 'handleSlide'
2763
3374
  },
2764
- onRender: function onRender() {
2765
- var _this = this;
3375
+ onRender: function () {
2766
3376
  this.ui.widget.slider({
2767
3377
  animate: 'fast'
2768
3378
  });
2769
- this.setupAttributeBinding('minValue', function (value) {
2770
- return _this.updateSliderOption('min', value || 0);
2771
- });
2772
- this.setupAttributeBinding('maxValue', function (value) {
2773
- return _this.updateSliderOption('max', value !== undefined ? value : 100);
2774
- });
3379
+ if (this.options.values) {
3380
+ this.ui.widget.slider('option', 'min', 0);
3381
+ this.ui.widget.slider('option', 'max', this.options.values.length - 1);
3382
+ } else {
3383
+ this.setupAttributeBinding('minValue', value => this.updateSliderOption('min', value || 0));
3384
+ this.setupAttributeBinding('maxValue', value => this.updateSliderOption('max', value !== undefined ? value : 100));
3385
+ }
2775
3386
  this.load();
2776
3387
  this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
2777
3388
  },
2778
- updateSliderOption: function updateSliderOption(name, value) {
3389
+ updateSliderOption(name, value) {
2779
3390
  this.ui.widget.slider('option', name, value);
2780
3391
  this.updateText(this.ui.widget.slider('value'));
2781
3392
  },
2782
- updateDisabled: function updateDisabled(disabled) {
3393
+ updateDisabled: function (disabled) {
2783
3394
  this.$el.toggleClass('disabled', !!disabled);
2784
3395
  if (disabled) {
2785
3396
  this.ui.widget.slider('disable');
@@ -2787,33 +3398,54 @@ var SliderInputView = Marionette.ItemView.extend({
2787
3398
  this.ui.widget.slider('enable');
2788
3399
  }
2789
3400
  },
2790
- handleSlide: function handleSlide(event, ui) {
2791
- this.updateText(ui.value);
3401
+ handleSlideStart() {
3402
+ if (this.options.onInteractionStart) {
3403
+ this.options.onInteractionStart();
3404
+ }
3405
+ },
3406
+ handleSlide(event, ui) {
3407
+ var value = this.options.values ? this.options.values[ui.value] : ui.value;
3408
+ this.updateText(value);
2792
3409
  if (this.options.saveOnSlide) {
2793
3410
  this.save(event, ui);
2794
3411
  }
2795
3412
  },
2796
- save: function save(event, ui) {
2797
- this.model.set(this.options.propertyName, ui.value);
3413
+ save: function (event, ui) {
3414
+ if (this.loading) {
3415
+ return;
3416
+ }
3417
+ var value = this.options.values ? this.options.values[ui.value] : ui.value;
3418
+ this.model.set(this.options.propertyName, value);
2798
3419
  },
2799
- load: function load() {
3420
+ load: function () {
2800
3421
  var value;
2801
3422
  if (this.model.has(this.options.propertyName)) {
2802
3423
  value = this.model.get(this.options.propertyName);
2803
3424
  } else {
2804
3425
  value = 'defaultValue' in this.options ? this.options.defaultValue : 0;
2805
3426
  }
2806
- this.ui.widget.slider('option', 'value', this.clampValue(value));
3427
+ var sliderValue = this.options.values ? this.options.values.indexOf(value) : value;
3428
+ this.loading = true;
3429
+ this.ui.widget.slider('option', 'value', this.clampValue(sliderValue));
3430
+ this.loading = false;
2807
3431
  this.updateText(value);
2808
3432
  },
2809
- clampValue: function clampValue(value) {
2810
- var min = this.ui.widget.slider('option', 'min');
2811
- var max = this.ui.widget.slider('option', 'max');
3433
+ clampValue(value) {
3434
+ const min = this.ui.widget.slider('option', 'min');
3435
+ const max = this.ui.widget.slider('option', 'max');
2812
3436
  return Math.min(max, Math.max(min, value));
2813
3437
  },
2814
- updateText: function updateText(value) {
2815
- var unit = 'unit' in this.options ? this.options.unit : '%';
2816
- var text = 'displayText' in this.options ? this.options.displayText(value) : value + unit;
3438
+ updateText: function (value) {
3439
+ var text;
3440
+ if (this.options.texts) {
3441
+ var index = this.options.values.indexOf(value);
3442
+ text = this.options.texts[index];
3443
+ } else if ('displayText' in this.options) {
3444
+ text = this.options.displayText(value);
3445
+ } else {
3446
+ var unit = 'unit' in this.options ? this.options.unit : '%';
3447
+ text = value + unit;
3448
+ }
2817
3449
  this.ui.value.text(text);
2818
3450
  }
2819
3451
  });
@@ -2824,7 +3456,7 @@ __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></spa
2824
3456
  return __p
2825
3457
  }
2826
3458
 
2827
- var JsonInputView = Marionette.ItemView.extend({
3459
+ const JsonInputView = Marionette.ItemView.extend({
2828
3460
  mixins: [inputView],
2829
3461
  template: template$d,
2830
3462
  className: 'json_input',
@@ -2835,30 +3467,30 @@ var JsonInputView = Marionette.ItemView.extend({
2835
3467
  'change': 'onChange',
2836
3468
  'keyup': 'validate'
2837
3469
  },
2838
- onRender: function onRender() {
3470
+ onRender: function () {
2839
3471
  this.load();
2840
3472
  this.validate();
2841
3473
  this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
2842
3474
  },
2843
- onChange: function onChange() {
3475
+ onChange: function () {
2844
3476
  if (this.validate()) {
2845
3477
  this.save();
2846
3478
  }
2847
3479
  },
2848
- onClose: function onClose() {
3480
+ onClose: function () {
2849
3481
  if (this.validate()) {
2850
3482
  this.save();
2851
3483
  }
2852
3484
  },
2853
- save: function save() {
3485
+ save: function () {
2854
3486
  this.model.set(this.options.propertyName, this.ui.input.val() ? JSON.parse(this.ui.input.val()) : null);
2855
3487
  },
2856
- load: function load() {
3488
+ load: function () {
2857
3489
  var input = this.ui.input;
2858
3490
  var value = this.model.get(this.options.propertyName);
2859
3491
  input.val(value ? JSON.stringify(value, null, 2) : '');
2860
3492
  },
2861
- validate: function validate() {
3493
+ validate: function () {
2862
3494
  var input = this.ui.input;
2863
3495
  if (input.val() && !this.isValidJson(input.val())) {
2864
3496
  this.displayValidationError(I18n$1.t('pageflow.ui.views.inputs.json_input_view.invalid'));
@@ -2868,15 +3500,15 @@ var JsonInputView = Marionette.ItemView.extend({
2868
3500
  return true;
2869
3501
  }
2870
3502
  },
2871
- displayValidationError: function displayValidationError(message) {
3503
+ displayValidationError: function (message) {
2872
3504
  this.$el.addClass('invalid');
2873
3505
  this.ui.input.attr('title', message);
2874
3506
  },
2875
- resetValidationError: function resetValidationError(message) {
3507
+ resetValidationError: function (message) {
2876
3508
  this.$el.removeClass('invalid');
2877
3509
  this.ui.input.attr('title', '');
2878
3510
  },
2879
- isValidJson: function isValidJson(text) {
3511
+ isValidJson: function (text) {
2880
3512
  try {
2881
3513
  JSON.parse(text);
2882
3514
  return true;
@@ -2913,7 +3545,7 @@ return __p
2913
3545
  *
2914
3546
  * @class
2915
3547
  */
2916
- var CheckBoxInputView = Marionette.ItemView.extend({
3548
+ const CheckBoxInputView = Marionette.ItemView.extend({
2917
3549
  mixins: [inputView],
2918
3550
  template: template$e,
2919
3551
  className: 'check_box_input',
@@ -2924,18 +3556,18 @@ var CheckBoxInputView = Marionette.ItemView.extend({
2924
3556
  input: 'input',
2925
3557
  label: 'label'
2926
3558
  },
2927
- onRender: function onRender() {
3559
+ onRender: function () {
2928
3560
  this.ui.label.attr('for', this.cid);
2929
3561
  this.ui.input.attr('id', this.cid);
2930
3562
  this.load();
2931
3563
  this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
2932
3564
  },
2933
- updateDisabled: function updateDisabled() {
3565
+ updateDisabled: function () {
2934
3566
  this.load();
2935
3567
  },
2936
- save: function save() {
3568
+ save: function () {
2937
3569
  if (!this.isDisabled()) {
2938
- var value = this.ui.input.is(':checked');
3570
+ const value = this.ui.input.is(':checked');
2939
3571
  if (this.options.storeInverted) {
2940
3572
  this.model.set(this.options.storeInverted, !value);
2941
3573
  } else {
@@ -2943,12 +3575,12 @@ var CheckBoxInputView = Marionette.ItemView.extend({
2943
3575
  }
2944
3576
  }
2945
3577
  },
2946
- load: function load() {
3578
+ load: function () {
2947
3579
  if (!this.isClosed) {
2948
3580
  this.ui.input.prop('checked', !!this.displayValue());
2949
3581
  }
2950
3582
  },
2951
- displayValue: function displayValue() {
3583
+ displayValue: function () {
2952
3584
  if (this.isDisabled() && this.options.displayUncheckedIfDisabled) {
2953
3585
  return false;
2954
3586
  } else if (this.isDisabled() && this.options.displayCheckedIfDisabled) {
@@ -2961,6 +3593,86 @@ var CheckBoxInputView = Marionette.ItemView.extend({
2961
3593
  }
2962
3594
  });
2963
3595
 
3596
+ /**
3597
+ * Input view for selecting one value from a small set of options
3598
+ * using radio buttons.
3599
+ * See {@link inputView} for further options.
3600
+ *
3601
+ * @param {Object} [options]
3602
+ *
3603
+ * @param {Array} options.values
3604
+ * Array of possible values the property can be set to.
3605
+ *
3606
+ * @param {Array} [options.texts]
3607
+ * Array of display texts for the values. If not provided,
3608
+ * translations are looked up based on the model's i18nKey.
3609
+ *
3610
+ * @class
3611
+ */
3612
+ const RadioButtonGroupInputView = Marionette.ItemView.extend({
3613
+ mixins: [inputView],
3614
+ template: () => `
3615
+ <label>
3616
+ <span class="name"></span>
3617
+ <span class="inline_help"></span>
3618
+ </label>
3619
+ `,
3620
+ className: 'radio_button_input',
3621
+ events: {
3622
+ 'change': 'save'
3623
+ },
3624
+ initialize: function () {
3625
+ if (!this.options.texts) {
3626
+ var translationKeyPrefix = findKeyWithTranslation(this.attributeTranslationKeys('values', {
3627
+ fallbackPrefix: 'activerecord.values'
3628
+ }));
3629
+ this.options.texts = _.map(this.options.values, function (value) {
3630
+ return I18n$1.t(translationKeyPrefix + '.' + value);
3631
+ });
3632
+ }
3633
+ },
3634
+ onRender: function () {
3635
+ this.appendOptions();
3636
+ this.load();
3637
+ this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);
3638
+ },
3639
+ updateDisabled: function () {
3640
+ this.$el.find('input').prop('disabled', this.isDisabled());
3641
+ },
3642
+ save: function () {
3643
+ var index = this.$el.find('input').index(this.$el.find('input:checked'));
3644
+ this.model.set(this.options.propertyName, this.options.values[index]);
3645
+ },
3646
+ appendOptions: function () {
3647
+ _.each(this.options.values, function (value, index) {
3648
+ var id = this.cid + '_' + value;
3649
+ var wrapper = document.createElement('div');
3650
+ wrapper.className = 'radio_button';
3651
+ var input = document.createElement('input');
3652
+ input.type = 'radio';
3653
+ input.name = this.options.propertyName;
3654
+ input.value = value;
3655
+ input.id = id;
3656
+ var label = document.createElement('label');
3657
+ label.htmlFor = id;
3658
+ var nameSpan = document.createElement('span');
3659
+ nameSpan.className = 'name';
3660
+ nameSpan.textContent = this.options.texts[index];
3661
+ label.appendChild(nameSpan);
3662
+ wrapper.appendChild(input);
3663
+ wrapper.appendChild(document.createTextNode(' '));
3664
+ wrapper.appendChild(label);
3665
+ this.$el.append(wrapper);
3666
+ }, this);
3667
+ },
3668
+ load: function () {
3669
+ if (!this.isClosed) {
3670
+ var value = this.model.get(this.options.propertyName);
3671
+ this.$el.find('input[value="' + value + '"]').prop('checked', true);
3672
+ }
3673
+ }
3674
+ });
3675
+
2964
3676
  /**
2965
3677
  * Render a separator in a {@link ConfigurationEditorView} tab.
2966
3678
  *
@@ -2970,7 +3682,7 @@ var CheckBoxInputView = Marionette.ItemView.extend({
2970
3682
  *
2971
3683
  * @class
2972
3684
  */
2973
- var SeparatorView = Marionette.View.extend({
3685
+ const SeparatorView = Marionette.View.extend({
2974
3686
  className: 'separator'
2975
3687
  });
2976
3688
 
@@ -2982,11 +3694,14 @@ var SeparatorView = Marionette.View.extend({
2982
3694
  *
2983
3695
  * @class
2984
3696
  */
2985
- var LabelOnlyView = Marionette.ItemView.extend({
3697
+ const LabelOnlyView = Marionette.ItemView.extend({
2986
3698
  mixins: [inputView],
2987
- template: function template() {
2988
- return "\n <label>\n <span class=\"name\"></span>\n <span class=\"inline_help\"></span>\n </label>\n ";
2989
- },
3699
+ template: () => `
3700
+ <label>
3701
+ <span class="name"></span>
3702
+ <span class="inline_help"></span>
3703
+ </label>
3704
+ `,
2990
3705
  ui: {
2991
3706
  label: 'label'
2992
3707
  }
@@ -3007,9 +3722,9 @@ var LabelOnlyView = Marionette.ItemView.extend({
3007
3722
  *
3008
3723
  * @since 12.0
3009
3724
  */
3010
- var EnumTableCellView = TableCellView.extend({
3725
+ const EnumTableCellView = TableCellView.extend({
3011
3726
  className: 'enum_table_cell',
3012
- update: function update() {
3727
+ update: function () {
3013
3728
  this.$el.text(this.attributeTranslation('cell_text.' + (this.attributeValue() || 'blank')));
3014
3729
  this.$el.attr('title', this.attributeTranslation('cell_title.' + (this.attributeValue() || 'blank'), {
3015
3730
  defaultValue: ''
@@ -3047,7 +3762,7 @@ return __p
3047
3762
  *
3048
3763
  * @since 12.0
3049
3764
  */
3050
- var DeleteRowTableCellView = TableCellView.extend({
3765
+ const DeleteRowTableCellView = TableCellView.extend({
3051
3766
  className: 'delete_row_table_cell',
3052
3767
  template: template$f,
3053
3768
  ui: {
@@ -3055,11 +3770,11 @@ var DeleteRowTableCellView = TableCellView.extend({
3055
3770
  },
3056
3771
  events: {
3057
3772
  'click .remove': 'destroy',
3058
- 'click': function click() {
3773
+ 'click': function () {
3059
3774
  return false;
3060
3775
  }
3061
3776
  },
3062
- showButton: function showButton() {
3777
+ showButton: function () {
3063
3778
  if (this.options.toggleDeleteButton) {
3064
3779
  var context = this.getModel();
3065
3780
  var toggle = context[this.options.toggleDeleteButton].apply(context);
@@ -3072,11 +3787,11 @@ var DeleteRowTableCellView = TableCellView.extend({
3072
3787
  return true;
3073
3788
  }
3074
3789
  },
3075
- update: function update() {
3790
+ update: function () {
3076
3791
  this.ui.removeButton.toggleClass('remove', this.showButton());
3077
3792
  this.ui.removeButton.attr('title', this.attributeTranslation('cell_title'));
3078
3793
  },
3079
- destroy: function destroy() {
3794
+ destroy: function () {
3080
3795
  this.getModel().destroy();
3081
3796
  }
3082
3797
  });
@@ -3097,9 +3812,9 @@ var DeleteRowTableCellView = TableCellView.extend({
3097
3812
  *
3098
3813
  * @since 12.0
3099
3814
  */
3100
- var PresenceTableCellView = TableCellView.extend({
3815
+ const PresenceTableCellView = TableCellView.extend({
3101
3816
  className: 'presence_table_cell',
3102
- update: function update() {
3817
+ update: function () {
3103
3818
  var isPresent = !!this.attributeValue();
3104
3819
  this.$el.attr('title', isPresent ? this.attributeTranslation('cell_title.present', {
3105
3820
  value: this.attributeValue()
@@ -3120,17 +3835,18 @@ var PresenceTableCellView = TableCellView.extend({
3120
3835
  *
3121
3836
  * @param {Object} [options]
3122
3837
  *
3123
- * @param {string[]} [options.icons]
3838
+ * @param {string[]|function} [options.icons]
3124
3839
  * An array of all possible attribute values to be mapped to HTML
3125
3840
  * classes of the same name. A global mapping from those classes to
3126
3841
  * icon mixins is provided in
3127
- * pageflow/ui/table_cells/icon_table_cell.scss.
3842
+ * pageflow/ui/table_cells/icon_table_cell.scss. Pass a function
3843
+ * returning the array to defer reading the values.
3128
3844
  *
3129
3845
  * @since 12.0
3130
3846
  */
3131
- var IconTableCellView = TableCellView.extend({
3847
+ const IconTableCellView = TableCellView.extend({
3132
3848
  className: 'icon_table_cell',
3133
- update: function update() {
3849
+ update: function () {
3134
3850
  var icon = this.attributeValue();
3135
3851
  var isPresent = !!this.attributeValue();
3136
3852
  this.removeExistingIcons();
@@ -3139,8 +3855,8 @@ var IconTableCellView = TableCellView.extend({
3139
3855
  }) : this.attributeTranslation('cell_title.blank'));
3140
3856
  this.$el.addClass(icon);
3141
3857
  },
3142
- removeExistingIcons: function removeExistingIcons() {
3143
- this.$el.removeClass(this.options.icons.join(' '));
3858
+ removeExistingIcons: function () {
3859
+ this.$el.removeClass(_.result(this.options, 'icons').join(' '));
3144
3860
  }
3145
3861
  });
3146
3862
 
@@ -3163,15 +3879,15 @@ var IconTableCellView = TableCellView.extend({
3163
3879
  *
3164
3880
  * @since 12.0
3165
3881
  */
3166
- var TextTableCellView = TableCellView.extend({
3882
+ const TextTableCellView = TableCellView.extend({
3167
3883
  className: 'text_table_cell',
3168
- update: function update() {
3884
+ update: function () {
3169
3885
  this.$el.text(this._updateText());
3170
3886
  },
3171
- _updateText: function _updateText() {
3887
+ _updateText: function () {
3172
3888
  if (this.attributeValue()) {
3173
3889
  return this.attributeValue();
3174
- } else if (typeof this.options.column["default"] === 'function') {
3890
+ } else if (typeof this.options.column.default === 'function') {
3175
3891
  var options = {};
3176
3892
  if (this.options.column.contentBinding) {
3177
3893
  options = {
@@ -3179,27 +3895,27 @@ var TextTableCellView = TableCellView.extend({
3179
3895
  model: this.getModel()
3180
3896
  };
3181
3897
  }
3182
- return this.options.column["default"](options);
3898
+ return this.options.column.default(options);
3183
3899
  } else if ('default' in this.options.column) {
3184
- return this.options.column["default"];
3900
+ return this.options.column.default;
3185
3901
  } else {
3186
3902
  return I18n$1.t('pageflow.ui.text_table_cell_view.empty');
3187
3903
  }
3188
3904
  }
3189
3905
  });
3190
3906
 
3191
- var subviewContainer = {
3192
- subview: function subview(view) {
3907
+ const subviewContainer = {
3908
+ subview: function (view) {
3193
3909
  this.subviews = this.subviews || new ChildViewContainer();
3194
3910
  this.subviews.add(view.render());
3195
3911
  return view;
3196
3912
  },
3197
- appendSubview: function appendSubview(view) {
3198
- var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
3199
- to = _ref.to;
3913
+ appendSubview: function (view, {
3914
+ to
3915
+ } = {}) {
3200
3916
  return (to || this.$el).append(this.subview(view).el);
3201
3917
  },
3202
- onClose: function onClose() {
3918
+ onClose: function () {
3203
3919
  if (this.subviews) {
3204
3920
  this.subviews.call('close');
3205
3921
  }
@@ -3209,9 +3925,9 @@ if (!Marionette.View.prototype.appendSubview) {
3209
3925
  Cocktail.mixin(Marionette.View, subviewContainer);
3210
3926
  }
3211
3927
 
3212
- var tooltipContainer = {
3928
+ const tooltipContainer = {
3213
3929
  events: {
3214
- 'mouseover [data-tooltip]': function mouseoverDataTooltip(event) {
3930
+ 'mouseover [data-tooltip]': function (event) {
3215
3931
  if (!this.tooltip.visible) {
3216
3932
  var target = $(event.currentTarget);
3217
3933
  var key = target.attr('data-tooltip');
@@ -3242,13 +3958,13 @@ var tooltipContainer = {
3242
3958
  });
3243
3959
  }
3244
3960
  },
3245
- 'mouseleave [data-tooltip]': function mouseleaveDataTooltip() {
3961
+ 'mouseleave [data-tooltip]': function () {
3246
3962
  this.tooltip.hide();
3247
3963
  }
3248
3964
  },
3249
- onRender: function onRender() {
3965
+ onRender: function () {
3250
3966
  this.appendSubview(this.tooltip = new TooltipView());
3251
3967
  }
3252
3968
  };
3253
3969
 
3254
- export { CheckBoxGroupInputView, CheckBoxInputView, CollectionView, ColorInputView, ConfigurationEditorTabView, ConfigurationEditorView, DeleteRowTableCellView, EnumTableCellView, ExtendedSelectInputView, FileNameInputView, IconTableCellView, JsonInputView, LabelOnlyView, NumberInputView, BaseObject as Object, PresenceTableCellView, ProxyUrlInputView, SelectInputView, SeparatorView, SliderInputView, SortableCollectionView, TableCellView, TableHeaderCellView, TableRowView, TableView, TabsView, TextAreaInputView, TextInputView, TextTableCellView, TooltipView, UrlDisplayView, UrlInputView, attributeBinding, cssModulesUtils, i18nUtils, inputView, inputWithPlaceholderText, serverSideValidation, subviewContainer, tooltipContainer, viewWithValidationErrorMessages };
3970
+ export { CheckBoxGroupInputView, CheckBoxInputView, CollectionView, ColorInputView, ColorPicker, ConfigurationEditorTabView, ConfigurationEditorView, DeleteRowTableCellView, EnumTableCellView, ExtendedSelectInputView, FileNameInputView, IconTableCellView, JsonInputView, LabelOnlyView, NumberInputView, BaseObject as Object, PresenceTableCellView, ProxyUrlInputView, RadioButtonGroupInputView, SelectInputView, SeparatorView, SliderInputView, SortableCollectionView, TableCellView, TableHeaderCellView, TableRowView, TableView, TabsView, TextAreaInputView, TextInputView, TextTableCellView, TooltipView, UrlDisplayView, UrlInputView, attributeBinding, attributeBindingUtils, cssModulesUtils, i18nUtils, inputView, inputWithPlaceholderText, serverSideValidation, subviewContainer, tooltipContainer, viewWithValidationErrorMessages };