@brightspot/ui 3.0.1-cms-ui-migration.2 → 3.0.1-cms-ui-migration.4
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.
- package/README.md +56 -37
- package/dist/custom-elements.json +1807 -1807
- package/dist/storybook/assets/{ActionBar.stories-hJ_5cm-P.js → ActionBar.stories-BZAVK1QG.js} +1 -1
- package/dist/storybook/assets/{ActionItem.stories-Bjx2803w.js → ActionItem.stories-BqiWvlWi.js} +1 -1
- package/dist/storybook/assets/{Avatar.stories-Cj0YgZ6f.js → Avatar.stories-DkdB6hd_.js} +1 -1
- package/dist/storybook/assets/{AvatarGroup.stories-Lh_sQFCU.js → AvatarGroup.stories-DODqaIix.js} +1 -1
- package/dist/storybook/assets/{Badge.stories-BL7RUibx.js → Badge.stories-DZoum08S.js} +1 -1
- package/dist/storybook/assets/{Button-BPHNcxqK.js → Button-BkmBgNO-.js} +1 -1
- package/dist/storybook/assets/{Button.stories-CAYO4gdU.js → Button.stories-BFhCL2dp.js} +1 -1
- package/dist/storybook/assets/{ButtonGroup.stories-Cd13Us5K.js → ButtonGroup.stories-DlTGTvkq.js} +1 -1
- package/dist/storybook/assets/{Celebrate.stories-D_KE3Qze.js → Celebrate.stories-m4d4zTEz.js} +1 -1
- package/dist/storybook/assets/{Checkbox.stories-Aj1xgZVn.js → Checkbox.stories-Y253YeU7.js} +1 -1
- package/dist/storybook/assets/{CircularProgress.stories-BecV_v6d.js → CircularProgress.stories-6BD8uV5G.js} +1 -1
- package/dist/storybook/assets/{ClipboardMixin.stories-DU-WiZ2f.js → ClipboardMixin.stories-BkH66rIU.js} +1 -1
- package/dist/storybook/assets/{Color-6BZIO3FS-BYl4KZZn.js → Color-6BZIO3FS-iG0OjPBU.js} +1 -1
- package/dist/storybook/assets/{Colors.stories-BMUVUy2q.js → Colors.stories-BxiyQnEg.js} +1 -1
- package/dist/storybook/assets/{CombinedEffects.stories-FHcPKFm6.js → CombinedEffects.stories-Bs3U7qRl.js} +1 -1
- package/dist/storybook/assets/{ComponentStatesMixin-EMUnfT5y.js → ComponentStatesMixin-DampYb5c.js} +1 -1
- package/dist/storybook/assets/{ComponentStatesMixin.stories-SXq0kzS9.js → ComponentStatesMixin.stories-CZ2OW7as.js} +1 -1
- package/dist/storybook/assets/{CopyToClipboard.stories-u43lhvcI.js → CopyToClipboard.stories-CdlghjaE.js} +1 -1
- package/dist/storybook/assets/{Debounce.stories-BdCn5qgO.js → Debounce.stories-CymT8PnT.js} +1 -1
- package/dist/storybook/assets/{DocsRenderer-LL677BLK-DxiEJ_jx.js → DocsRenderer-LL677BLK-dqHCo-GE.js} +3 -3
- package/dist/storybook/assets/{Dropdown.stories-BWSRwjIF.js → Dropdown.stories-B6JwKg-I.js} +1 -1
- package/dist/storybook/assets/{EmptyState.stories-BpobeZL5.js → EmptyState.stories-Bk229lPH.js} +1 -1
- package/dist/storybook/assets/{Events.stories-CP5kMzpr.js → Events.stories-C8-k9cx8.js} +1 -1
- package/dist/storybook/assets/{Heading.stories-CbJD-oTB.js → Heading.stories-DIdnAQRG.js} +1 -1
- package/dist/storybook/assets/{HueRipple.stories-BOABJ7zw.js → HueRipple.stories-B1gXAEaH.js} +1 -1
- package/dist/storybook/assets/{Icon.stories-CWlUHL4j.js → Icon.stories--1VJ0_yt.js} +1 -1
- package/dist/storybook/assets/{IconButton.stories-BWBs-OLT.js → IconButton.stories-CS2rEIir.js} +1 -1
- package/dist/storybook/assets/{LinearProgress.stories-LZ0GZoxF.js → LinearProgress.stories-Bx0CQQls.js} +1 -1
- package/dist/storybook/assets/{Pagination.stories-CbLaR3P9.js → Pagination.stories-B4AogTXB.js} +1 -1
- package/dist/storybook/assets/{Popover.stories-JHrWqYZw.js → Popover.stories-BR88DPgU.js} +1 -1
- package/dist/storybook/assets/{ReadyMixin-B1H2a9x8.js → ReadyMixin-n6qZO39Y.js} +1 -1
- package/dist/storybook/assets/{RovingTabindexMixin.stories-UMHpYG73.js → RovingTabindexMixin.stories-1xTJSMSv.js} +1 -1
- package/dist/storybook/assets/{Rtc.stories-VQtNSlls.js → Rtc.stories-Rt0A_rUZ.js} +1 -1
- package/dist/storybook/assets/{ScrollShadow.stories-CYdi8Tgp.js → ScrollShadow.stories-CjLdJyYu.js} +1 -1
- package/dist/storybook/assets/{Switch.stories-D8F2hZCf.js → Switch.stories-CF8wO4v2.js} +1 -1
- package/dist/storybook/assets/{Tab.stories-wgBP0lTj.js → Tab.stories-C8XNshog.js} +1 -1
- package/dist/storybook/assets/{Tabs.stories-C00rr5sf.js → Tabs.stories-B01l4rQx.js} +1 -1
- package/dist/storybook/assets/{Throttle.stories-BhQEfJbS.js → Throttle.stories-Bpi0K5j9.js} +1 -1
- package/dist/storybook/assets/{Tooltip.stories-CGoZ5qTn.js → Tooltip.stories-BC4zMiZ1.js} +1 -1
- package/dist/storybook/assets/{Upload.stories-B3K-HAXw.js → Upload.stories-2pWv_fZ1.js} +1 -1
- package/dist/storybook/assets/{UploadItem.stories-71ArSoUh.js → UploadItem.stories-aLeAUM7y.js} +1 -1
- package/dist/storybook/assets/{Welcome.stories-CihlfFXS.js → Welcome.stories-DJV83eb7.js} +1 -1
- package/dist/storybook/assets/{Widget.stories-1u4KbiJM.js → Widget.stories-Cz0i2o6L.js} +1 -1
- package/dist/storybook/assets/{WithTooltip-65CFNBJE-B3Jitxw9.js → WithTooltip-65CFNBJE-D8QwVYG8.js} +1 -1
- package/dist/storybook/assets/{blocks-C1HaXuQB.js → blocks-BFmpEZRy.js} +5 -5
- package/dist/storybook/assets/{formatter-EIJCOSYU-Dy9Lt9fs.js → formatter-EIJCOSYU-CdwdJOPy.js} +1 -1
- package/dist/storybook/assets/if-defined-gbJXriW-.js +1 -0
- package/dist/storybook/assets/{iframe-Dx6IxWXF.js → iframe-CyssRDCd.js} +4 -4
- package/dist/storybook/assets/{index-OrjedSVh.js → index-B82i8dhg.js} +1 -1
- package/dist/storybook/assets/{onFind-YTqjw6W0.js → onFind-BFI1uIxr.js} +1 -1
- package/dist/storybook/assets/{onFind.stories-DEvwTrmx.js → onFind.stories-zZjQs_Gn.js} +1 -1
- package/dist/storybook/assets/{onRemove.stories-D5mO-Lin.js → onRemove.stories-pFBwAIex.js} +1 -1
- package/dist/storybook/assets/{onVisible.stories-C3Rcz0Eb.js → onVisible.stories-5dErOVRq.js} +1 -1
- package/dist/storybook/assets/{style-map-CiMHry7H.js → style-map-D0EcLEWO.js} +1 -1
- package/dist/storybook/assets/{syntaxhighlighter-ED5Y7EFY-DIZnuhb2.js → syntaxhighlighter-ED5Y7EFY--2h_dVga.js} +1 -1
- package/dist/storybook/iframe.html +1 -1
- package/dist/storybook/project.json +1 -1
- package/package.json +16 -2
- package/src/legacy/tool-ui/src/AnalyticsWidget.css +1 -1
- package/src/legacy/tool-ui/src/Board.css +1 -1
- package/src/legacy/tool-ui/src/BulkUpload.css +1 -1
- package/src/legacy/tool-ui/src/ComboInput.css +1 -1
- package/src/legacy/tool-ui/src/Compat.css +5 -5
- package/src/legacy/tool-ui/src/ContentEditDrawer.css +1 -1
- package/src/legacy/tool-ui/src/Dialog.css +1 -1
- package/src/legacy/tool-ui/src/FormFilter.css +1 -1
- package/src/legacy/tool-ui/src/Icon/index.css +1 -1
- package/src/legacy/tool-ui/src/ImageEditor.css +1 -1
- package/src/legacy/tool-ui/src/Incompatible.css +2 -2
- package/src/legacy/tool-ui/src/LinkCarousel.css +1 -1
- package/src/legacy/tool-ui/src/Page.css +1 -1
- package/src/legacy/tool-ui/src/RepeatableContentInputGroup.css +1 -1
- package/src/legacy/tool-ui/src/RichText.css +1 -1
- package/src/legacy/tool-ui/src/SearchWidget.css +2 -2
- package/src/legacy/tool-ui/src/SearchWidgetAdvanced.css +1 -1
- package/src/legacy/tool-ui/src/Widget.css +1 -1
- package/src/legacy/tool-ui/src/main/webapp/dist/v5.5e5d7f655e174ddd85f5.css +5 -0
- package/dist/storybook/assets/if-defined-CA2KmTqA.js +0 -1
- package/docs/adr/0001-retire-cms-ui-package-fold-under-src-legacy.md +0 -78
- package/docs/adr/0002-yarn-workspaces-preserve-cms-ui-deps.md +0 -130
- package/docs/adr/0003-bundle-equivalence-as-fold-acceptance-criterion.md +0 -286
- package/src/legacy/tool-ui/src/main/resources/settings.properties +0 -1
- package/src/legacy/tool-ui/src/main/webapp/WEB-INF/web.xml +0 -81
- package/src/legacy/tool-ui/src/main/webapp/dist/v5.5e3fdf0f0b20b4e3c170.css +0 -5
- package/src/legacy/tool-ui/src/main/webapp/script/bsp-uploader.js +0 -170
- package/src/legacy/tool-ui/src/main/webapp/script/bsp-utils.js +0 -393
- package/src/legacy/tool-ui/src/main/webapp/script/content/layout-element.js +0 -141
- package/src/legacy/tool-ui/src/main/webapp/script/input/query.js +0 -78
- package/src/legacy/tool-ui/src/main/webapp/script/input/workflow.js +0 -718
- package/src/legacy/tool-ui/src/main/webapp/script/jquery.extra.js +0 -633
- package/src/legacy/tool-ui/src/main/webapp/script/v3/Dropbox.js +0 -18
- package/src/legacy/tool-ui/src/main/webapp/script/v3/EditFieldUpdate.js +0 -406
- package/src/legacy/tool-ui/src/main/webapp/script/v3/EditFieldUpdateCache.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/script/v3/Notification.js +0 -151
- package/src/legacy/tool-ui/src/main/webapp/script/v3/content/edit.js +0 -194
- package/src/legacy/tool-ui/src/main/webapp/script/v3/content/state.js +0 -785
- package/src/legacy/tool-ui/src/main/webapp/script/v3/csrf.js +0 -35
- package/src/legacy/tool-ui/src/main/webapp/script/v3/dashboard.js +0 -65
- package/src/legacy/tool-ui/src/main/webapp/script/v3/input/dataTransfer.js +0 -129
- package/src/legacy/tool-ui/src/main/webapp/script/v3/input/file.js +0 -433
- package/src/legacy/tool-ui/src/main/webapp/script/v3/input/object.js +0 -743
- package/src/legacy/tool-ui/src/main/webapp/script/v3/input/read-only.js +0 -17
- package/src/legacy/tool-ui/src/main/webapp/script/v3/jquery.frame.js +0 -478
- package/src/legacy/tool-ui/src/main/webapp/script/v3/jquery.repeatable.js +0 -2406
- package/src/legacy/tool-ui/src/main/webapp/script/v3/plugin/popup.d.ts +0 -2
- package/src/legacy/tool-ui/src/main/webapp/script/v3/plugin/popup.js +0 -446
- package/src/legacy/tool-ui/src/main/webapp/script/v3/search-filters.js +0 -62
- package/src/legacy/tool-ui/src/main/webapp/script/v3/search.js +0 -53
- package/src/legacy/tool-ui/src/main/webapp/script/v3.js +0 -1049
- package/src/legacy/tool-ui/src/main/webapp/v4/Admin.js +0 -16
- package/src/legacy/tool-ui/src/main/webapp/v4/AutoExpand.js +0 -84
- package/src/legacy/tool-ui/src/main/webapp/v4/AutoSubmit.js +0 -68
- package/src/legacy/tool-ui/src/main/webapp/v4/Bridge.js +0 -536
- package/src/legacy/tool-ui/src/main/webapp/v4/CheckboxInput.js +0 -22
- package/src/legacy/tool-ui/src/main/webapp/v4/ColorInput.js +0 -5
- package/src/legacy/tool-ui/src/main/webapp/v4/ColorInputSpectrum.js +0 -107
- package/src/legacy/tool-ui/src/main/webapp/v4/ComboInput.js +0 -1491
- package/src/legacy/tool-ui/src/main/webapp/v4/CommunityWidget.js +0 -29
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentEdit.js +0 -2427
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentLock.js +0 -470
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentReporting.js +0 -32
- package/src/legacy/tool-ui/src/main/webapp/v4/DataTable.js +0 -31
- package/src/legacy/tool-ui/src/main/webapp/v4/DateStringField.js +0 -485
- package/src/legacy/tool-ui/src/main/webapp/v4/Entry.js +0 -264
- package/src/legacy/tool-ui/src/main/webapp/v4/ExternalItemAuth.js +0 -16
- package/src/legacy/tool-ui/src/main/webapp/v4/Form.js +0 -31
- package/src/legacy/tool-ui/src/main/webapp/v4/Hierarchy.js +0 -100
- package/src/legacy/tool-ui/src/main/webapp/v4/Icon.ts +0 -49
- package/src/legacy/tool-ui/src/main/webapp/v4/ImageEditor.js +0 -2403
- package/src/legacy/tool-ui/src/main/webapp/v4/ImageEditorBundle.js +0 -5
- package/src/legacy/tool-ui/src/main/webapp/v4/LinkCarousel.js +0 -40
- package/src/legacy/tool-ui/src/main/webapp/v4/LinkList.js +0 -14
- package/src/legacy/tool-ui/src/main/webapp/v4/LinkTable.js +0 -123
- package/src/legacy/tool-ui/src/main/webapp/v4/Location.js +0 -19
- package/src/legacy/tool-ui/src/main/webapp/v4/LocationMap.js +0 -148
- package/src/legacy/tool-ui/src/main/webapp/v4/LookingGlass.js +0 -24
- package/src/legacy/tool-ui/src/main/webapp/v4/Message.js +0 -14
- package/src/legacy/tool-ui/src/main/webapp/v4/NumberBar.js +0 -32
- package/src/legacy/tool-ui/src/main/webapp/v4/Page.js +0 -890
- package/src/legacy/tool-ui/src/main/webapp/v4/Preview.js +0 -758
- package/src/legacy/tool-ui/src/main/webapp/v4/PreviewEditor.js +0 -86
- package/src/legacy/tool-ui/src/main/webapp/v4/PreviewOverlay.js +0 -1005
- package/src/legacy/tool-ui/src/main/webapp/v4/PubSub.js +0 -47
- package/src/legacy/tool-ui/src/main/webapp/v4/QueryField.js +0 -211
- package/src/legacy/tool-ui/src/main/webapp/v4/RegionMap.js +0 -215
- package/src/legacy/tool-ui/src/main/webapp/v4/RepeatableContentInputGroup.js +0 -160
- package/src/legacy/tool-ui/src/main/webapp/v4/RichTextEditor.js +0 -154
- package/src/legacy/tool-ui/src/main/webapp/v4/SearchFields.js +0 -281
- package/src/legacy/tool-ui/src/main/webapp/v4/SearchResult.js +0 -255
- package/src/legacy/tool-ui/src/main/webapp/v4/SharePreview.js +0 -56
- package/src/legacy/tool-ui/src/main/webapp/v4/Sortable.js +0 -874
- package/src/legacy/tool-ui/src/main/webapp/v4/StyleEmbeddedContent.js +0 -100
- package/src/legacy/tool-ui/src/main/webapp/v4/StyleguidePresets.js +0 -357
- package/src/legacy/tool-ui/src/main/webapp/v4/TabContainer.js +0 -360
- package/src/legacy/tool-ui/src/main/webapp/v4/Taxonomy.js +0 -27
- package/src/legacy/tool-ui/src/main/webapp/v4/ThemeBundleEditor.js +0 -224
- package/src/legacy/tool-ui/src/main/webapp/v4/TimedContent.js +0 -147
- package/src/legacy/tool-ui/src/main/webapp/v4/TimedContentBundle.js +0 -8
- package/src/legacy/tool-ui/src/main/webapp/v4/VideoEditor.js +0 -2417
- package/src/legacy/tool-ui/src/main/webapp/v4/VideoEditorBundle.js +0 -8
- package/src/legacy/tool-ui/src/main/webapp/v4/ViewMirror.js +0 -52
- package/src/legacy/tool-ui/src/main/webapp/v4/ViewPreview.d.ts +0 -13
- package/src/legacy/tool-ui/src/main/webapp/v4/ViewPreview.js +0 -177
- package/src/legacy/tool-ui/src/main/webapp/v4/Widget.js +0 -90
- package/src/legacy/tool-ui/src/main/webapp/v4/__mocks__/fileMock.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/__mocks__/styleMock.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/__mocks__/textArea.mock.js +0 -20
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/globals.js +0 -770
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/ProseMirror.test.js +0 -16
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/index.html +0 -54
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/comment_manager/CommentManager.test.js +0 -29
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/comment_manager/index.html +0 -35
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/custom_keyboard/CustomKeyboard.js +0 -42
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/custom_keyboard/index.html +0 -37
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/enhancement_manager/EnhancementManager.test.js +0 -288
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/enhancement_manager/block.html +0 -38
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/enhancement_manager/inline.html +0 -38
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/enhancement_manager/no-popups.html +0 -38
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/list_manager/ListManager.js +0 -257
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/list_manager/index.html +0 -38
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/menubar/hierarchal.html +0 -33
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/menubar/index.html +0 -33
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/menubar/menubar.test.js +0 -195
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/menubar/small.html +0 -34
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/menubar/tags.html +0 -34
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/placeholder_manager/PlaceholderManager.test.js +0 -134
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/placeholder_manager/has-editable-placeholder.html +0 -32
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/placeholder_manager/has-text.html +0 -34
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/placeholder_manager/index.html +0 -31
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/table_manager/TableManager.test.js +0 -63
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/table_manager/existing.html +0 -48
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/track_manager/TrackManager.test.js +0 -291
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/track_manager/existing.html +0 -39
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/integration/rte/plugins/track_manager/insert.html +0 -37
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/unit/Sortable.test.js +0 -105
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/unit/rte/ProseMirror.test.js +0 -41
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/unit/rte/codemirror-shim.test.js +0 -72
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/unit/rte/plugins/collab_manager/CollabManager.test.js +0 -46
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/unit/rte/plugins/enhancement_manager/EnhancementManager.test.js +0 -84
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/unit/rte/plugins/list_manager/ListManager.test.js +0 -54
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/unit/rte/plugins/menubar/menubar.test.js +0 -183
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/unit/rte/plugins/spellcheck/SpellCheck.test.js +0 -45
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/unit/rte/settings/BSSerializer.test.js +0 -346
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/unit/rte/settings/menuItemsBuilder.test.js +0 -226
- package/src/legacy/tool-ui/src/main/webapp/v4/__tests__/unit/rte/utilities.test.js +0 -118
- package/src/legacy/tool-ui/src/main/webapp/v4/appetizeio/Appetizeio.js +0 -5
- package/src/legacy/tool-ui/src/main/webapp/v4/appetizeio/AppetizeioEmbedded.js +0 -113
- package/src/legacy/tool-ui/src/main/webapp/v4/compat/Fetch.js +0 -16
- package/src/legacy/tool-ui/src/main/webapp/v4/compat/jquery.js +0 -32
- package/src/legacy/tool-ui/src/main/webapp/v4/compat/requirejs.js +0 -13
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/Tether.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/TetherLayout.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/closest.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/create.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/find.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/findAll.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/ifClick.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/ifMatches.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/ifUnmodified.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/index.js +0 -5
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/insertBefore.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/insertFirst.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/insertLast.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/onFind.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/onFindOnce.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/onRTEReady.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/onRemove.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/onVisible.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/previousUntil.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/dom/resolveIconCompat.js +0 -40
- package/src/legacy/tool-ui/src/main/webapp/v4/rtc/Socket.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/rtc/index.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/ProseMirror.js +0 -909
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/README.md +0 -68
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/codemirror-shim.d.ts +0 -8
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/codemirror-shim.js +0 -274
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/collab-workflow.jpeg +0 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/interchangeable.ts +0 -250
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/mention.js +0 -90
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/PluginProvider.js +0 -124
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/README.md +0 -46
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/ai_inline_manager/AIInlineManager.ts +0 -124
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/ai_inline_manager/views/AIInlineView.ts +0 -1019
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/ai_manager/AiManager.ts +0 -199
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/collab_manager/CollabManager.js +0 -339
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/collab_manager/views/AvatarView.js +0 -96
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/comment_manager/CommentManager.js +0 -348
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/custom_keyboard/CustomKeyboard.js +0 -110
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/custom_keyboard/README.md +0 -29
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/EnhancementManager.js +0 -428
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/README.md +0 -63
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/commands.js +0 -690
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/constants.js +0 -12
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/enhancement-creation.jpeg +0 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/index.js +0 -15
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/rte-flow.jpeg +0 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/views/ActionButtonView.js +0 -86
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/views/BlockSubmenuView.js +0 -60
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/views/EnhancementView.js +0 -208
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/views/PreviewView.js +0 -102
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/views/SubmenuView.js +0 -365
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/find_replace_manager/FindReplaceManager.js +0 -239
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/find_replace_manager/views/FindView.js +0 -604
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/fullscreen_manager/FullscreenManager.js +0 -57
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/fullscreen_manager/README.md +0 -26
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/fullscreen_manager/commands.js +0 -16
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/fullscreen_manager/index.js +0 -4
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/fullscreen_manager/views/FullscreenView.js +0 -474
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/html_editor_manager/htmlEditorManager.js +0 -66
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/html_editor_manager/views/HtmlEditorView.js +0 -97
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/list_manager/ListManager.js +0 -342
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/list_manager/README.md +0 -50
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/list_manager/commands.js +0 -207
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/list_manager/constants.js +0 -26
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/list_manager/index.js +0 -4
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/menubar/Menubar.js +0 -485
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/menubar/README.md +0 -40
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/menubar/views/MenuView.js +0 -842
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/paste_manager/PasteManager.js +0 -368
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/placeholder_manager/PlaceHolderManager.js +0 -128
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/raw_text_manager/README.md +0 -13
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/raw_text_manager/RawTextManager.js +0 -96
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/spellcheck/index.js +0 -3
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/spellcheck/spellcheck-plugin.js +0 -280
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/spellcheck/spellcheck-service.js +0 -94
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/table_manager/TableManager.js +0 -57
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/table_manager/commands.js +0 -97
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/table_manager/views/TableSizerView.js +0 -88
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/table_manager/views/TableView.js +0 -613
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/track_manager/README.md +0 -13
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/track_manager/TrackManager.js +0 -905
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/settings/BSSerializer.js +0 -819
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/settings/README.md +0 -80
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/settings/commands.js +0 -98
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/settings/constants.d.ts +0 -84
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/settings/constants.js +0 -87
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/settings/index.js +0 -13
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/settings/keymapBuilder.js +0 -223
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/settings/menuItemsBuilder.js +0 -559
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/settings/schemaBuilder.js +0 -1281
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/utilities.d.ts +0 -4
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/utilities.js +0 -359
- package/src/legacy/tool-ui/src/main/webapp/v4/theme/ColorRotator.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/util/debounce.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/util/getComponentKey.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/util/noise.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/util/repaint.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/util/storage.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/util/throttle.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/AssignmentContent.js +0 -33
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/AssignmentDeskDashboard.js +0 -217
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/AssociatedContentWidget.js +0 -7
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/BulkUpload.js +0 -19
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/Calendar.js +0 -7
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/ClosableWindow.js +0 -13
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/PitchAssignments.js +0 -25
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/PitchContent.js +0 -33
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/Revisions.js +0 -61
|
@@ -1,2403 +0,0 @@
|
|
|
1
|
-
import closest from './dom/closest'
|
|
2
|
-
import create from './dom/create'
|
|
3
|
-
import Cropper from 'cropperjs'
|
|
4
|
-
import find from './dom/find'
|
|
5
|
-
import Noise from './util/noise'
|
|
6
|
-
import onFind from './dom/onFind'
|
|
7
|
-
import PlainDraggable from 'plain-draggable/plain-draggable.esm.js'
|
|
8
|
-
import TabContainer from './TabContainer'
|
|
9
|
-
import findAll from './dom/findAll'
|
|
10
|
-
import { toggleMenu, setupMenuForAria } from '../../../dom/popupMenu'
|
|
11
|
-
import 'cropperjs/dist/cropper.css'
|
|
12
|
-
import onRemove from './dom/onRemove'
|
|
13
|
-
|
|
14
|
-
if (!window.BRIGHTSPOT?.ui.cms.enableV5UI) {
|
|
15
|
-
await import('./ImageEditor.less')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const tooltips = window.BRIGHTSPOT?.ui.tooltips
|
|
19
|
-
const zIndex = '400'
|
|
20
|
-
const SPOT_SIZE = 40
|
|
21
|
-
const filtersApplied = new Map()
|
|
22
|
-
const FILTERABLE_SELECTORS =
|
|
23
|
-
'.cropper-canvas > img, .cropper-view-box > img, .imageEditor-sizePreview'
|
|
24
|
-
|
|
25
|
-
export default class ImageEditor {
|
|
26
|
-
constructor(element) {
|
|
27
|
-
// Then convert it to a popup.
|
|
28
|
-
const STATE_OPEN = 'is-imageEditorOpen'
|
|
29
|
-
const STATE_LOADING = 'is-imageEditorLoading'
|
|
30
|
-
const STATE_LOADED = 'is-imageEditorLoaded'
|
|
31
|
-
const STATE_READY = 'is-imageEditorReady'
|
|
32
|
-
const row = closest(element, '.inputContainer')
|
|
33
|
-
const fileSelector = find(row, '.fileSelector')
|
|
34
|
-
const large = element.parentNode
|
|
35
|
-
const image = find(element, '.imageEditor-image > img')
|
|
36
|
-
const rcigItem = element.closest('.RCIG-item')
|
|
37
|
-
|
|
38
|
-
if (!large || !image) return
|
|
39
|
-
|
|
40
|
-
this._element = element
|
|
41
|
-
this._renditions = new Map()
|
|
42
|
-
this._focusUI = null
|
|
43
|
-
this._editUI = null
|
|
44
|
-
this._initialContainerHeight = null
|
|
45
|
-
this._form = this._element.closest('form')
|
|
46
|
-
this._implicitFocus = element.dataset.implicitFocus
|
|
47
|
-
this._isContextual = 'contextual' in element.dataset
|
|
48
|
-
|
|
49
|
-
// Hide the whole editor from screen readers.
|
|
50
|
-
this._element.setAttribute('hidden', '')
|
|
51
|
-
|
|
52
|
-
let rootCIG = large
|
|
53
|
-
while (rootCIG.parentElement?.closest('.CIG-row')) {
|
|
54
|
-
rootCIG = rootCIG.parentElement?.closest('.CIG-row')
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Hide hotspot inputs from the content edit UI.
|
|
58
|
-
const fields = this.getForm().querySelector(
|
|
59
|
-
'.inputContainer[data-name$=".hotspots"]',
|
|
60
|
-
)
|
|
61
|
-
if (fields) fields.style.display = 'none'
|
|
62
|
-
|
|
63
|
-
const resizeObserver = new ResizeObserver((entries) => {
|
|
64
|
-
for (let entry of entries) {
|
|
65
|
-
this.repaint(entry.contentRect.height)
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
resizeObserver.observe(document.body)
|
|
70
|
-
|
|
71
|
-
const loader = create('div', { className: 'ImageEditor-preview-loader' })
|
|
72
|
-
const previewImage = image.cloneNode()
|
|
73
|
-
previewImage.onload = () => {
|
|
74
|
-
large.classList.add('is-imagePreview')
|
|
75
|
-
large.classList.add(STATE_LOADING)
|
|
76
|
-
this.getElement().classList.add(STATE_LOADING)
|
|
77
|
-
loader.style.width = `${previewImage.offsetWidth}px`
|
|
78
|
-
loader.style.height = `${previewImage.offsetHeight}px`
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const escapeToClose = (event) => {
|
|
82
|
-
if (event.key === 'Escape') {
|
|
83
|
-
this._element.querySelector('.ImageEditor-closeButton')?.click()
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const editButton = create('button', {
|
|
88
|
-
className:
|
|
89
|
-
this.getElement().getAttribute('editor') === 'true'
|
|
90
|
-
? 'ImageEditor-open'
|
|
91
|
-
: 'ImageEditor-open noEditor',
|
|
92
|
-
'aria-haspopup': true,
|
|
93
|
-
'aria-label': tooltips.imageEditor.open,
|
|
94
|
-
title: tooltips.imageEditor.open,
|
|
95
|
-
type: 'button',
|
|
96
|
-
onclick: (event) => {
|
|
97
|
-
this._element.removeAttribute('hidden')
|
|
98
|
-
large.classList.add(STATE_OPEN)
|
|
99
|
-
rootCIG.style.zIndex = zIndex
|
|
100
|
-
const left = this.getElement().closest('.ContentEdit-left')
|
|
101
|
-
if (left) {
|
|
102
|
-
left.style.position = 'fixed'
|
|
103
|
-
left.style.zIndex = zIndex
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// If this is the first time opening the editor UI...
|
|
107
|
-
if (!large.classList.contains(STATE_READY)) {
|
|
108
|
-
large.classList.add(STATE_READY)
|
|
109
|
-
this._initialContainerHeight = this.getElement().offsetHeight
|
|
110
|
-
this._renditions = this.flattenAndSortRenditions(this.getRenditions())
|
|
111
|
-
this.renderRenditionPreviews(this._renditions)
|
|
112
|
-
this.renderTabs()
|
|
113
|
-
}
|
|
114
|
-
if (rcigItem) {
|
|
115
|
-
rcigItem.classList.add(STATE_OPEN)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
this._element.querySelector('.ImageEditor-edit button')?.focus()
|
|
119
|
-
window.addEventListener('keydown', escapeToClose)
|
|
120
|
-
|
|
121
|
-
// TODO: repaint after open transition completes instead of guessing.
|
|
122
|
-
setTimeout(() => {
|
|
123
|
-
this.repaint(document.body.offsetHeight)
|
|
124
|
-
if (this._hotspotUI) {
|
|
125
|
-
this._hotspotUI.reflow()
|
|
126
|
-
this._hotspotUI.reveal()
|
|
127
|
-
}
|
|
128
|
-
}, 250)
|
|
129
|
-
},
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
const aside = find(this.getElement(), '.imageEditor-aside')
|
|
133
|
-
const editList = find(aside, '.imageEditor-tools ul')
|
|
134
|
-
editList.classList.add('ImageEditor-editTools')
|
|
135
|
-
editList.setAttribute('role', 'menu')
|
|
136
|
-
|
|
137
|
-
const editToggle = create('button', {
|
|
138
|
-
className: 'ImageEditor-editToggle',
|
|
139
|
-
type: 'button',
|
|
140
|
-
title: tooltips.imageEditor.actions,
|
|
141
|
-
'aria-label': tooltips.imageEditor.actions,
|
|
142
|
-
'aria-haspopup': 'menu',
|
|
143
|
-
'aria-expanded': false,
|
|
144
|
-
onclick: (event) => {
|
|
145
|
-
toggleMenu(editList, editToggle)
|
|
146
|
-
},
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
setupMenuForAria(editList, editToggle)
|
|
150
|
-
|
|
151
|
-
const ratios = find(this.getElement(), '.ImageEditor-groups')
|
|
152
|
-
if (ratios) {
|
|
153
|
-
onRemove.pause()
|
|
154
|
-
large.appendChild(ratios)
|
|
155
|
-
requestAnimationFrame(onRemove.resume)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const focusText = this.getElement().getAttribute('data-focus-message')
|
|
159
|
-
const message = create(
|
|
160
|
-
'div',
|
|
161
|
-
{
|
|
162
|
-
className: 'ImageEditor-focusMessage',
|
|
163
|
-
},
|
|
164
|
-
focusText,
|
|
165
|
-
)
|
|
166
|
-
const implicitFocusMessage = create(
|
|
167
|
-
'span',
|
|
168
|
-
{ className: `ImageEditor-implicitMessage is-hidden` },
|
|
169
|
-
this.getElement().dataset.implicitFocusMessage,
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
const showFocusMessage = function (size) {
|
|
173
|
-
message.textContent = focusText + ' ' + size
|
|
174
|
-
message.classList.remove('is-hidden')
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
this._cropSelect = large.querySelector('.ImageEditor-groups > select')
|
|
178
|
-
if (this._cropSelect) {
|
|
179
|
-
this._cropSelect.addEventListener('change', (event) => {
|
|
180
|
-
const index = this._cropSelect.selectedIndex
|
|
181
|
-
const selectedCrop = this._cropSelect.options[index]
|
|
182
|
-
message.classList.remove('is-hidden')
|
|
183
|
-
showFocusMessage(selectedCrop.textContent)
|
|
184
|
-
let coords = this._focusUI.getFocusCoordinatesRelative(
|
|
185
|
-
selectedCrop.value,
|
|
186
|
-
)
|
|
187
|
-
const focusPoint = find(large, '.ImageEditor-focus-point')
|
|
188
|
-
if (coords) {
|
|
189
|
-
const focusPointCoords = this.getFocusCoords(selectedCrop.value)
|
|
190
|
-
// remove implicit info if set.
|
|
191
|
-
if (
|
|
192
|
-
focusPointCoords.xPercent !== null &&
|
|
193
|
-
focusPointCoords.yPercent !== null
|
|
194
|
-
) {
|
|
195
|
-
focusPoint && focusPoint.classList.remove('is-implicit')
|
|
196
|
-
implicitFocusMessage.classList.add('is-hidden')
|
|
197
|
-
} else if (this.getImplicitFocus()) {
|
|
198
|
-
focusPoint && focusPoint.classList.add('is-implicit')
|
|
199
|
-
focusPoint && focusPoint.classList.remove('is-hidden')
|
|
200
|
-
implicitFocusMessage.classList.remove('is-hidden')
|
|
201
|
-
}
|
|
202
|
-
focusPoint && focusPoint.classList.remove('is-hidden')
|
|
203
|
-
this._focusUI.moveTo(coords)
|
|
204
|
-
} else {
|
|
205
|
-
if (this.getImplicitFocus()) {
|
|
206
|
-
const implicitFocus = this.getImplicitFocus().split(',')
|
|
207
|
-
coords = {
|
|
208
|
-
x: implicitFocus[0],
|
|
209
|
-
xPercent: implicitFocus[0] * 100,
|
|
210
|
-
y: implicitFocus[1],
|
|
211
|
-
yPercent: implicitFocus[1] * 100,
|
|
212
|
-
}
|
|
213
|
-
implicitFocusMessage.classList.remove('is-hidden')
|
|
214
|
-
focusPoint && focusPoint.classList.add('is-implicit')
|
|
215
|
-
this._focusUI.moveTo(coords)
|
|
216
|
-
} else {
|
|
217
|
-
focusPoint && focusPoint.classList.add('is-hidden')
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
this.showPreview()
|
|
221
|
-
})
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (fileSelector) {
|
|
225
|
-
find(fileSelector, 'select').addEventListener('change', (e) => {
|
|
226
|
-
editButton.style.display = e.target.value === 'keep' ? '' : 'none'
|
|
227
|
-
editToggle.style.display = e.target.value === 'keep' ? '' : 'none'
|
|
228
|
-
})
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
large.insertBefore(
|
|
232
|
-
create('div', { className: 'ImageEditor-preview' }, previewImage, loader),
|
|
233
|
-
element,
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
large.append(editButton, editToggle, editList)
|
|
237
|
-
|
|
238
|
-
const done = create(
|
|
239
|
-
'button',
|
|
240
|
-
{
|
|
241
|
-
className: 'ImageEditor-closeButton',
|
|
242
|
-
name: 'action-done',
|
|
243
|
-
onclick: (event) => {
|
|
244
|
-
rootCIG.style.zIndex = 'auto'
|
|
245
|
-
const left = this.getElement().closest('.ContentEdit-left')
|
|
246
|
-
if (left) {
|
|
247
|
-
left.style.position = 'initial'
|
|
248
|
-
left.style.zIndex = 'auto'
|
|
249
|
-
}
|
|
250
|
-
this._element.setAttribute('hidden', '')
|
|
251
|
-
|
|
252
|
-
this.renderPreview()
|
|
253
|
-
if (this._focusUI) {
|
|
254
|
-
this.showPreview()
|
|
255
|
-
}
|
|
256
|
-
large.classList.remove(STATE_OPEN)
|
|
257
|
-
if (rcigItem) {
|
|
258
|
-
rcigItem.classList.remove(STATE_OPEN)
|
|
259
|
-
}
|
|
260
|
-
editButton.focus()
|
|
261
|
-
window.removeEventListener('keydown', escapeToClose)
|
|
262
|
-
event.preventDefault()
|
|
263
|
-
},
|
|
264
|
-
},
|
|
265
|
-
'Done',
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
this.getElement().appendChild(
|
|
269
|
-
create('div', { className: 'ActionBar' }, done),
|
|
270
|
-
)
|
|
271
|
-
|
|
272
|
-
const applyEditsOnPreview = (data) => {
|
|
273
|
-
const {
|
|
274
|
-
brightness,
|
|
275
|
-
contrast,
|
|
276
|
-
invert,
|
|
277
|
-
grayscale,
|
|
278
|
-
sepia,
|
|
279
|
-
flipH,
|
|
280
|
-
flipV,
|
|
281
|
-
rotate,
|
|
282
|
-
} = { ...data }
|
|
283
|
-
this.applyFilter(
|
|
284
|
-
'brightness',
|
|
285
|
-
`${this.convertValueToPercent(brightness)}%`,
|
|
286
|
-
)
|
|
287
|
-
this.applyFilter('contrast', `${this.convertValueToPercent(contrast)}%`)
|
|
288
|
-
this.applyFilter('sepia', sepia ? '100%' : null)
|
|
289
|
-
this.applyFilter('invert', invert ? '100%' : null)
|
|
290
|
-
this.applyFilter('grayscale', grayscale ? '100%' : null)
|
|
291
|
-
flipH
|
|
292
|
-
? this._croptool.scale(-1, flipV ? -1 : 1)
|
|
293
|
-
: this._croptool.scale(1, flipV ? -1 : 1)
|
|
294
|
-
|
|
295
|
-
flipV
|
|
296
|
-
? this._croptool.scale(flipH ? -1 : 1, -1)
|
|
297
|
-
: this._croptool.scale(flipH ? -1 : 1, 1)
|
|
298
|
-
|
|
299
|
-
if (rotate) {
|
|
300
|
-
this._croptool.rotateTo(rotate)
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const calculatePreviewRatios = () => {
|
|
305
|
-
const groups = findAll(this._cropSelect, 'option:not([value=""])')
|
|
306
|
-
const sizeInfos = Object.values(this.getRenditions())
|
|
307
|
-
groups.forEach((group) => {
|
|
308
|
-
const filtered = sizeInfos.filter((obj) => {
|
|
309
|
-
return obj.group === group.value && obj.aspectRatio !== 0
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
if (filtered.length > 0) {
|
|
313
|
-
const avgAspectRatio =
|
|
314
|
-
filtered.reduce((a, b) => a + b.aspectRatio, 0) / filtered.length
|
|
315
|
-
group.setAttribute('data-preview-ratio', avgAspectRatio)
|
|
316
|
-
} else {
|
|
317
|
-
this._cropSelect.removeChild(group)
|
|
318
|
-
}
|
|
319
|
-
})
|
|
320
|
-
if (this._cropSelect.children.length === 1) {
|
|
321
|
-
const input = find(ratios, '.ComboInput')
|
|
322
|
-
if (input) {
|
|
323
|
-
input.style.display = 'none'
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const handleImageLoaded = (image) => {
|
|
329
|
-
this.initCropTool(image).then((data) => {
|
|
330
|
-
this.getElement().classList.remove(STATE_LOADING)
|
|
331
|
-
large.classList.remove(STATE_LOADING)
|
|
332
|
-
large.classList.add(STATE_LOADED)
|
|
333
|
-
applyEditsOnPreview(this.getEditValues())
|
|
334
|
-
this.renderPreview()
|
|
335
|
-
if (implicitFocusMessage) {
|
|
336
|
-
large.appendChild(implicitFocusMessage)
|
|
337
|
-
}
|
|
338
|
-
if (this._cropSelect) {
|
|
339
|
-
if (message) {
|
|
340
|
-
large.appendChild(message)
|
|
341
|
-
}
|
|
342
|
-
const index = this._cropSelect.selectedIndex
|
|
343
|
-
const selectedCrop = this._cropSelect.options[index]
|
|
344
|
-
showFocusMessage(selectedCrop.textContent)
|
|
345
|
-
this.imageMask = create('div', {
|
|
346
|
-
className: 'ImageEditor-preview-border',
|
|
347
|
-
})
|
|
348
|
-
large.querySelector('.ImageEditor-preview').append(this.imageMask)
|
|
349
|
-
this._focusUI = this.createFocusUI(large, () => {
|
|
350
|
-
this.updateAllRenditionPreviews()
|
|
351
|
-
})
|
|
352
|
-
calculatePreviewRatios()
|
|
353
|
-
this.showPreview()
|
|
354
|
-
}
|
|
355
|
-
})
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (!image.complete) {
|
|
359
|
-
image.onload = (event) => {
|
|
360
|
-
handleImageLoaded(image)
|
|
361
|
-
}
|
|
362
|
-
} else {
|
|
363
|
-
handleImageLoaded(image)
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Applies a CSS filter to the "filterable" images.
|
|
368
|
-
// Combines filters if multiple are used in the UI.
|
|
369
|
-
// @name: the CSS filter function to use.
|
|
370
|
-
// @val: the CSS filter function percentage to use. *Note: passing null will remove the filter.
|
|
371
|
-
applyFilter(name, val) {
|
|
372
|
-
let cssFilterProp = ''
|
|
373
|
-
const images = [
|
|
374
|
-
...this.getElement().querySelectorAll(FILTERABLE_SELECTORS),
|
|
375
|
-
this.getPreviewContainer(),
|
|
376
|
-
]
|
|
377
|
-
|
|
378
|
-
val ? filtersApplied.set(name, val) : filtersApplied.delete(name)
|
|
379
|
-
|
|
380
|
-
filtersApplied.forEach((val, name) => {
|
|
381
|
-
cssFilterProp += ` ${name}(${val}) `
|
|
382
|
-
})
|
|
383
|
-
|
|
384
|
-
images.forEach((image) => (image.style.filter = `${cssFilterProp.trim()}`))
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// Server values range from -1 to 1 and must be converted to percentage.
|
|
388
|
-
convertValueToPercent(val) {
|
|
389
|
-
let amount = parseFloat(val)
|
|
390
|
-
amount = amount * 100 + 100
|
|
391
|
-
return amount
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
async initCropTool(image) {
|
|
395
|
-
await new Promise((resolve, reject) => {
|
|
396
|
-
this._croptool = new Cropper(image, {
|
|
397
|
-
autoCrop: false,
|
|
398
|
-
autoCropArea: 1,
|
|
399
|
-
movable: false,
|
|
400
|
-
restore: false,
|
|
401
|
-
responsive: true,
|
|
402
|
-
scalable: true, // needed for flipping
|
|
403
|
-
toggleDragModeOnDblclick: false,
|
|
404
|
-
viewMode: 1,
|
|
405
|
-
zoomable: false,
|
|
406
|
-
zoomOnTouch: false,
|
|
407
|
-
zoomOnWheel: false,
|
|
408
|
-
// calculates the correct width & height of the container
|
|
409
|
-
// instead of setting them to default dimensions (200px x 100px) when 'resize' is triggered
|
|
410
|
-
minContainerHeight: -1,
|
|
411
|
-
minContainerWidth: -1,
|
|
412
|
-
|
|
413
|
-
ready: (event) => {
|
|
414
|
-
resolve()
|
|
415
|
-
},
|
|
416
|
-
|
|
417
|
-
crop: (event) => {
|
|
418
|
-
const rendition = this.getCurrentRendition()
|
|
419
|
-
if (rendition) {
|
|
420
|
-
this.updateRenditionPreview(
|
|
421
|
-
rendition,
|
|
422
|
-
this._croptool.getCroppedCanvas(),
|
|
423
|
-
)
|
|
424
|
-
}
|
|
425
|
-
},
|
|
426
|
-
|
|
427
|
-
// synonymous with "the crop was changed"
|
|
428
|
-
cropend: (event) => {
|
|
429
|
-
const rendition = this.getCurrentRendition()
|
|
430
|
-
const cropData = this._croptool.getCropBoxData()
|
|
431
|
-
if (rendition) {
|
|
432
|
-
rendition.modifiedCropData = cropData
|
|
433
|
-
this.showRenditionResetButton(rendition)
|
|
434
|
-
const renditionEl = this.getRenditionElFromRendition(rendition)
|
|
435
|
-
renditionEl.classList.add('ImageEditor-sizeModified')
|
|
436
|
-
this.setCropInputValues(rendition)
|
|
437
|
-
}
|
|
438
|
-
},
|
|
439
|
-
})
|
|
440
|
-
})
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// To trigger the (private) resize handler in Cropper.js utility which renders the canvas again in the right size.
|
|
444
|
-
// This is needed because it sometimes incorrectly resizes the canvas within the container
|
|
445
|
-
// causing the image to be cropped off or misaligned within the container, usually when rotating.
|
|
446
|
-
// see: Cropper.js - handlers.resize()
|
|
447
|
-
repaint(height) {
|
|
448
|
-
const imageEditorMain = this.getElement().querySelector('.ImageEditor-main')
|
|
449
|
-
const containerHeight =
|
|
450
|
-
height - document.querySelector('.Page-header').offsetHeight
|
|
451
|
-
const containerWidth = imageEditorMain.getBoundingClientRect().width
|
|
452
|
-
window.dispatchEvent(new Event('resize'))
|
|
453
|
-
if (containerHeight === this._croptool?.getCanvasData().height) {
|
|
454
|
-
this._croptool.setCanvasData({
|
|
455
|
-
top: this._croptool.getCanvasData().top + 24,
|
|
456
|
-
left: this._croptool.getCanvasData().left + 24,
|
|
457
|
-
height: this._croptool.getCanvasData().height - 48,
|
|
458
|
-
})
|
|
459
|
-
}
|
|
460
|
-
if (containerWidth === this._croptool?.getCanvasData().width) {
|
|
461
|
-
this._croptool.setCanvasData({
|
|
462
|
-
top: this._croptool.getCanvasData().top + 24,
|
|
463
|
-
left: this._croptool.getCanvasData().left + 24,
|
|
464
|
-
width: this._croptool.getCanvasData().width - 48,
|
|
465
|
-
})
|
|
466
|
-
}
|
|
467
|
-
this.updateAllRenditionPreviews()
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Returns the x,y whole percentage values eg: x: 75.33 (%)
|
|
471
|
-
// If a focus point is not set this returns null values.
|
|
472
|
-
getFocusCoords(cropRatio) {
|
|
473
|
-
let inputX, inputY
|
|
474
|
-
if (this._cropSelect) {
|
|
475
|
-
const index = this._cropSelect.selectedIndex
|
|
476
|
-
if (!cropRatio) {
|
|
477
|
-
cropRatio = this._cropSelect.options[index].value
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
if (!cropRatio || cropRatio === '' || cropRatio === 'independent') {
|
|
481
|
-
inputX = this.getElement().querySelector('input[name$=".focusX"]')
|
|
482
|
-
inputY = this.getElement().querySelector('input[name$=".focusY"]')
|
|
483
|
-
} else {
|
|
484
|
-
inputX = this.getElement().querySelector(
|
|
485
|
-
'input[name$=".focusX.' + cropRatio + '"]',
|
|
486
|
-
)
|
|
487
|
-
inputY = this.getElement().querySelector(
|
|
488
|
-
'input[name$=".focusY.' + cropRatio + '"]',
|
|
489
|
-
)
|
|
490
|
-
if (inputX.value === '' || inputY.value === '') {
|
|
491
|
-
inputX = this.getElement().querySelector('input[name$=".focusX"]')
|
|
492
|
-
inputY = this.getElement().querySelector('input[name$=".focusY"]')
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
return {
|
|
496
|
-
xPercent: inputX.value !== '' ? inputX.value * 100 : null,
|
|
497
|
-
yPercent: inputY.value !== '' ? inputY.value * 100 : null,
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
getDefaults() {
|
|
502
|
-
let defaults = {
|
|
503
|
-
brightness: 0,
|
|
504
|
-
contrast: 0,
|
|
505
|
-
flipH: false,
|
|
506
|
-
flipV: false,
|
|
507
|
-
grayscale: false,
|
|
508
|
-
invert: false,
|
|
509
|
-
rotate: '0',
|
|
510
|
-
sepia: false,
|
|
511
|
-
'no-filter': true,
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
if (!this._isContextual) return defaults
|
|
515
|
-
// In contextual image editing
|
|
516
|
-
// Update the image defaults with original shared image edits (if there are any)
|
|
517
|
-
let defaultMetadata = this._element.dataset.defaultEdits
|
|
518
|
-
if (!defaultMetadata) return defaults
|
|
519
|
-
const parentDefaults = JSON.parse(defaultMetadata)
|
|
520
|
-
const { grayscale, sepia, invert } = parentDefaults
|
|
521
|
-
if (grayscale || sepia || invert) {
|
|
522
|
-
parentDefaults['no-filter'] = false
|
|
523
|
-
}
|
|
524
|
-
defaults = Object.assign(defaults, parentDefaults)
|
|
525
|
-
return defaults
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
getEditValues() {
|
|
529
|
-
const editorAccess = this.getElement().getAttribute('editor')
|
|
530
|
-
return [
|
|
531
|
-
...(editorAccess === 'true'
|
|
532
|
-
? this.getElement().querySelectorAll('.ImageEditor-edit input[name]')
|
|
533
|
-
: Object.entries(JSON.parse(this.getElement().getAttribute('edits')))),
|
|
534
|
-
].reduce((accum, val) => {
|
|
535
|
-
if (editorAccess === 'true') {
|
|
536
|
-
const propname = val.name.substring(val.name.lastIndexOf('.') + 1)
|
|
537
|
-
Object.assign(accum, {
|
|
538
|
-
[propname]: val.type === 'checkbox' ? val.checked : val.value,
|
|
539
|
-
})
|
|
540
|
-
} else {
|
|
541
|
-
const propname = val[0]
|
|
542
|
-
Object.assign(accum, {
|
|
543
|
-
[propname]: val[1],
|
|
544
|
-
})
|
|
545
|
-
}
|
|
546
|
-
return accum
|
|
547
|
-
}, {})
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
createHotspotUI(tab, areaWidthUnscaled, areaHeightUnscaled, callback) {
|
|
551
|
-
let isDragging = false
|
|
552
|
-
|
|
553
|
-
const inputAddHotspot = this.getForm().querySelector(
|
|
554
|
-
'.inputContainer[data-name$=".hotspots"]',
|
|
555
|
-
)
|
|
556
|
-
|
|
557
|
-
// Abort creating the Hotspot UI if the hotspot form controls aren't present.
|
|
558
|
-
if (!inputAddHotspot) return
|
|
559
|
-
|
|
560
|
-
// Reveal hotspot inputs within the image editor hotspot tab UI.
|
|
561
|
-
inputAddHotspot.style.display = 'block'
|
|
562
|
-
|
|
563
|
-
const hotspotContainer = create('div', {
|
|
564
|
-
class: 'ImageEditor-hotspot-container',
|
|
565
|
-
onclick: (event) => {
|
|
566
|
-
event.preventDefault()
|
|
567
|
-
},
|
|
568
|
-
})
|
|
569
|
-
|
|
570
|
-
const hotspotArea = create('div', {
|
|
571
|
-
class: 'ImageEditor-hotspotableArea',
|
|
572
|
-
onclick: (event) => {
|
|
573
|
-
// Allow positioning of hotspot overlays when hotspots tab is active.
|
|
574
|
-
if (!hotspotContainer.classList.contains('is-hidden')) {
|
|
575
|
-
const clickCoords = this.getClickCoordsLocal(event.target, event)
|
|
576
|
-
clickCoords.xPercent *= 100
|
|
577
|
-
clickCoords.yPercent *= 100
|
|
578
|
-
// NOTE: this is where we could add the feature to create a new hotspot overlay when clicking the image.
|
|
579
|
-
}
|
|
580
|
-
},
|
|
581
|
-
})
|
|
582
|
-
|
|
583
|
-
hotspotContainer.appendChild(hotspotArea)
|
|
584
|
-
|
|
585
|
-
this.getElement()
|
|
586
|
-
.querySelector('.ImageEditor-main')
|
|
587
|
-
.appendChild(hotspotContainer)
|
|
588
|
-
|
|
589
|
-
tab.append(inputAddHotspot)
|
|
590
|
-
|
|
591
|
-
onFind(tab, '.ImageEditor-hotspot-item', (hotspotItem) => {
|
|
592
|
-
const onClassChange = (mutations) => {
|
|
593
|
-
mutations.forEach((mutation) => {
|
|
594
|
-
const target = mutation.target
|
|
595
|
-
const isRemoving = target.classList.contains('is-removing')
|
|
596
|
-
isRemoving
|
|
597
|
-
? _handleHotspotItemRemove(hotspotItem)
|
|
598
|
-
: _handleHotspotItemRestore(hotspotItem)
|
|
599
|
-
})
|
|
600
|
-
}
|
|
601
|
-
const observer = new MutationObserver(onClassChange)
|
|
602
|
-
observer.observe(hotspotItem, { attributeFilter: ['class'] })
|
|
603
|
-
})
|
|
604
|
-
|
|
605
|
-
// Reflow the hotspot area UI by dispatching a browser-resize event and
|
|
606
|
-
// recalculating the relative pixel coordinates for all hotspot overlays.
|
|
607
|
-
// (needed to support responsive viewport).
|
|
608
|
-
const reflow = () => {
|
|
609
|
-
// Position each hotspot overlay.
|
|
610
|
-
tab
|
|
611
|
-
.querySelectorAll('.ImageEditor-hotspot-item')
|
|
612
|
-
.forEach((hotspotItem) => {
|
|
613
|
-
const spotSizeOffset = SPOT_SIZE / 2
|
|
614
|
-
const { x: offsetX, y: offsetY } = hotspotArea.getBoundingClientRect()
|
|
615
|
-
const areaWidth = hotspotArea.offsetWidth
|
|
616
|
-
const areaHeight = hotspotArea.offsetHeight
|
|
617
|
-
const draggable = hotspotItem.draggableRef
|
|
618
|
-
|
|
619
|
-
// Convert from normalized coordinates back to the relative pixels
|
|
620
|
-
const { x: normalX, y: normalY } = _getInputCoordsByItem(hotspotItem)
|
|
621
|
-
const { xPercent, yPercent } = _getNormalizedPointInPercent({
|
|
622
|
-
x: normalX,
|
|
623
|
-
y: normalY,
|
|
624
|
-
})
|
|
625
|
-
const relX = areaWidth * (xPercent / 100)
|
|
626
|
-
const relY = areaHeight * (yPercent / 100)
|
|
627
|
-
|
|
628
|
-
// Sync the draggable model (specific to the library)
|
|
629
|
-
draggable.position()
|
|
630
|
-
|
|
631
|
-
// Reposition the draggable element to the relative pixels.
|
|
632
|
-
draggable.left = relX + offsetX - spotSizeOffset
|
|
633
|
-
draggable.top = relY + offsetY - spotSizeOffset
|
|
634
|
-
})
|
|
635
|
-
|
|
636
|
-
window.dispatchEvent(new Event('resize'))
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
const _handleHotspotItemRemove = (itemEl) => {
|
|
640
|
-
itemEl.hotspotRefEl.classList.add('is-removing')
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
const _handleHotspotItemRestore = (itemEl) => {
|
|
644
|
-
itemEl.hotspotRefEl.classList.remove('is-removing')
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
const _getInputCoordsByItem = (item) => {
|
|
648
|
-
return {
|
|
649
|
-
x: item.querySelector('input[name$="cms.image.x"]').value,
|
|
650
|
-
y: item.querySelector('input[name$="cms.image.y"]').value,
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// Convert a Point (x,y) from the scaled coordinate space in the editor UI
|
|
655
|
-
// to a new Point (x,y) as a percentage relative to the natural image's coordinate space.
|
|
656
|
-
const _getNormalizedPointInPercent = ({ x, y }) => {
|
|
657
|
-
const xPercent = (x / areaWidthUnscaled) * 100
|
|
658
|
-
const yPercent = (y / areaHeightUnscaled) * 100
|
|
659
|
-
|
|
660
|
-
return {
|
|
661
|
-
xPercent,
|
|
662
|
-
yPercent,
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// Visualize all the Hotspot overlays whether they are pre-existing or are added at runtime.
|
|
667
|
-
const _visualizeHotspots = () => {
|
|
668
|
-
let currentHotspot = null
|
|
669
|
-
|
|
670
|
-
const _setInputCoordsByItem = (item, coords) => {
|
|
671
|
-
const inputX = item.querySelector('input[name$="/cms.image.x"]')
|
|
672
|
-
const inputY = item.querySelector('input[name$="/cms.image.y"]')
|
|
673
|
-
|
|
674
|
-
inputX.value = coords.x
|
|
675
|
-
inputY.value = coords.y
|
|
676
|
-
|
|
677
|
-
inputX.dispatchEvent(new Event('change', { bubbles: true }))
|
|
678
|
-
inputY.dispatchEvent(new Event('change', { bubbles: true }))
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
// Convert a Point (x,y) from the scaled coordinate space in the editor UI
|
|
682
|
-
// to a new Point (x,y) as pixels relative to the natural image's coordinate space.
|
|
683
|
-
const _getNormalizedPointInPixels = ({ x, y }) => {
|
|
684
|
-
const scaleX = hotspotArea.offsetWidth / areaWidthUnscaled
|
|
685
|
-
const scaleY = hotspotArea.offsetHeight / areaHeightUnscaled
|
|
686
|
-
|
|
687
|
-
x = parseInt(x / scaleX)
|
|
688
|
-
y = parseInt(y / scaleY)
|
|
689
|
-
|
|
690
|
-
return {
|
|
691
|
-
x,
|
|
692
|
-
y,
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const _updatePosition = (spot, position) => {
|
|
697
|
-
// Get the offset coords since drag API returns global coords.
|
|
698
|
-
const parentCoords = spot.parentNode.getBoundingClientRect()
|
|
699
|
-
let localLeft = position.left - parentCoords.left
|
|
700
|
-
let localTop = position.top - parentCoords.top
|
|
701
|
-
|
|
702
|
-
// convert from top-left origin to center origin based on spot size
|
|
703
|
-
localLeft = localLeft + spot.offsetWidth / 2
|
|
704
|
-
localTop = localTop + spot.offsetHeight / 2
|
|
705
|
-
|
|
706
|
-
_setInputCoordsByItem(
|
|
707
|
-
spot.itemRefEl,
|
|
708
|
-
_getNormalizedPointInPixels({ x: localLeft, y: localTop }),
|
|
709
|
-
)
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
const _collapseAllHotspotItems = (item) => {
|
|
713
|
-
const hotspotItemList = item.parentNode
|
|
714
|
-
|
|
715
|
-
// Collapse all hotspot items in the list.
|
|
716
|
-
hotspotItemList
|
|
717
|
-
.querySelectorAll(
|
|
718
|
-
'.ImageEditor-hotspot-item:not(.collapsed):not(.is-collapsed)',
|
|
719
|
-
)
|
|
720
|
-
.forEach((item) => {
|
|
721
|
-
item.classList.add('is-collapsed')
|
|
722
|
-
item.classList.add('collapsed')
|
|
723
|
-
})
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
// Create a single hotspot overlay given the item that has the form inputs and an x/y initial position.
|
|
727
|
-
// Returns a reference to the newly created element.
|
|
728
|
-
const _visualizeHotspot = ({ item, x, y, label }) => {
|
|
729
|
-
const _handleClick = (spot) => {
|
|
730
|
-
const hotspotItem = spot.itemRefEl
|
|
731
|
-
|
|
732
|
-
spot.classList.remove('is-added')
|
|
733
|
-
|
|
734
|
-
if (hotspotItem.classList.contains('is-collapsed')) {
|
|
735
|
-
hotspotItem.querySelector(':scope .repeatableLabel')?.click()
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
// Delayed to provide a better UX.
|
|
739
|
-
setTimeout(() => {
|
|
740
|
-
hotspotItem.scrollIntoView({
|
|
741
|
-
behavior: 'smooth',
|
|
742
|
-
block: 'start',
|
|
743
|
-
inline: 'nearest',
|
|
744
|
-
})
|
|
745
|
-
}, 100)
|
|
746
|
-
|
|
747
|
-
currentHotspot = spot
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
const spot = create('div', {
|
|
751
|
-
className: 'ImageEditor-hotspot',
|
|
752
|
-
onclick: (event) => {
|
|
753
|
-
_handleClick(event.target)
|
|
754
|
-
},
|
|
755
|
-
onmouseenter: (event) => {
|
|
756
|
-
if (!isDragging) {
|
|
757
|
-
event.target.classList.remove('is-added')
|
|
758
|
-
event.target.classList.add('is-hovered')
|
|
759
|
-
event.target.itemRefEl.classList.add('is-hovered')
|
|
760
|
-
event.preventDefault()
|
|
761
|
-
event.stopPropagation()
|
|
762
|
-
}
|
|
763
|
-
},
|
|
764
|
-
onmouseleave: (event) => {
|
|
765
|
-
if (!isDragging) {
|
|
766
|
-
event.target.classList.remove('is-hovered')
|
|
767
|
-
event.target.itemRefEl.classList.remove('is-hovered')
|
|
768
|
-
event.preventDefault()
|
|
769
|
-
event.stopPropagation()
|
|
770
|
-
}
|
|
771
|
-
},
|
|
772
|
-
})
|
|
773
|
-
|
|
774
|
-
spot.innerHTML = label
|
|
775
|
-
spot.itemRefEl = item
|
|
776
|
-
hotspotArea.appendChild(spot)
|
|
777
|
-
|
|
778
|
-
// Make the hotspot overlay draggable.
|
|
779
|
-
const draggable = new PlainDraggable(spot, {
|
|
780
|
-
leftTop: false,
|
|
781
|
-
containment: hotspotArea,
|
|
782
|
-
})
|
|
783
|
-
|
|
784
|
-
PlainDraggable.draggableClass = 'is-draggable'
|
|
785
|
-
PlainDraggable.draggingClass = 'is-dragging'
|
|
786
|
-
PlainDraggable.movingClass = 'is-moving'
|
|
787
|
-
|
|
788
|
-
draggable.onMove = (position) => {
|
|
789
|
-
hotspotArea.classList.add('is-dragging')
|
|
790
|
-
isDragging = true
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
draggable.onDragEnd = (position) => {
|
|
794
|
-
isDragging = false
|
|
795
|
-
hotspotArea.classList.remove('is-dragging')
|
|
796
|
-
_updatePosition(spot, position)
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
// keep a reference to the draggable object on the DOM node.
|
|
800
|
-
item.draggableRef = draggable
|
|
801
|
-
|
|
802
|
-
// Initially position a runtime added spot.
|
|
803
|
-
if (x === '' && y === '') {
|
|
804
|
-
// Gentle randomization of starting coordinates.
|
|
805
|
-
// This positions the overlay around the center of the image.
|
|
806
|
-
const noise = new Noise()
|
|
807
|
-
const distribution = 0.3
|
|
808
|
-
const plusOrMinusX = Math.random() < 0.5 ? -1 : 1
|
|
809
|
-
const plusOrMinusY = Math.random() < 0.5 ? -1 : 1
|
|
810
|
-
const parentCoords = spot.parentNode.getBoundingClientRect()
|
|
811
|
-
const globalCenterX = parentCoords.left + parentCoords.width / 2
|
|
812
|
-
const globalCenterY = parentCoords.top + parentCoords.height / 2
|
|
813
|
-
const globalLeft =
|
|
814
|
-
globalCenterX +
|
|
815
|
-
plusOrMinusX *
|
|
816
|
-
(SPOT_SIZE * noise.getVal(plusOrMinusX * distribution))
|
|
817
|
-
const globalTop =
|
|
818
|
-
globalCenterY +
|
|
819
|
-
plusOrMinusY *
|
|
820
|
-
(SPOT_SIZE * noise.getVal(plusOrMinusY * distribution))
|
|
821
|
-
_updatePosition(spot, { left: globalLeft, top: globalTop })
|
|
822
|
-
reflow()
|
|
823
|
-
spot.classList.add('is-added')
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
return spot
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
// Process Hotspot items from the list.
|
|
830
|
-
onFind(tab, '.RCIG-item[data-hotspot-point]', (item) => {
|
|
831
|
-
const { x, y } = _getInputCoordsByItem(item)
|
|
832
|
-
|
|
833
|
-
// Collapse hotspot items.
|
|
834
|
-
_collapseAllHotspotItems(item)
|
|
835
|
-
|
|
836
|
-
// Scroll to this item in the list.
|
|
837
|
-
// Delayed to provide a better UX.
|
|
838
|
-
setTimeout(() => {
|
|
839
|
-
item.scrollIntoView({
|
|
840
|
-
behavior: 'smooth',
|
|
841
|
-
block: 'start',
|
|
842
|
-
inline: 'nearest',
|
|
843
|
-
})
|
|
844
|
-
}, 100)
|
|
845
|
-
|
|
846
|
-
item.classList.add('ImageEditor-hotspot-item')
|
|
847
|
-
item.hotspotRefEl = _visualizeHotspot({
|
|
848
|
-
item,
|
|
849
|
-
x,
|
|
850
|
-
y,
|
|
851
|
-
label: Array.from(item.parentElement.children).indexOf(item) + 1,
|
|
852
|
-
})
|
|
853
|
-
|
|
854
|
-
item.onmouseenter = (event) => {
|
|
855
|
-
event.target.hotspotRefEl.classList.remove('is-added')
|
|
856
|
-
event.target.hotspotRefEl.classList.add('is-hovered')
|
|
857
|
-
event.preventDefault()
|
|
858
|
-
event.stopPropagation()
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
item.onmouseleave = (event) => {
|
|
862
|
-
event.target.hotspotRefEl.classList.remove('is-hovered')
|
|
863
|
-
event.preventDefault()
|
|
864
|
-
event.stopPropagation()
|
|
865
|
-
}
|
|
866
|
-
})
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
// Observe size changes to the cropped image to keep the hotspot area
|
|
870
|
-
// dimensions and position in sync (mostly to support browser resize).
|
|
871
|
-
const mutations = new MutationObserver((mutations) => {
|
|
872
|
-
for (const mutation of mutations) {
|
|
873
|
-
if (mutation.attributeName === 'style') {
|
|
874
|
-
_hotspotAreaResize(mutation.target)
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
})
|
|
878
|
-
mutations.observe(this.getElement().querySelector('.cropper-canvas'), {
|
|
879
|
-
attributes: true,
|
|
880
|
-
})
|
|
881
|
-
|
|
882
|
-
// Resize the hotspot area according to the reference element
|
|
883
|
-
const _hotspotAreaResize = (referenceEl) => {
|
|
884
|
-
hotspotArea.style.height = `${referenceEl.offsetHeight}px`
|
|
885
|
-
hotspotArea.style.width = `${referenceEl.offsetWidth}px`
|
|
886
|
-
hotspotArea.style.transform = referenceEl.style.transform
|
|
887
|
-
|
|
888
|
-
reflow()
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
// Show all hotspot overlays.
|
|
892
|
-
const reveal = () => {
|
|
893
|
-
tab
|
|
894
|
-
.querySelectorAll('.ImageEditor-hotspot-item')
|
|
895
|
-
.forEach((hotspotItem) => {
|
|
896
|
-
hotspotItem.hotspotRefEl.classList.remove('is-hidden')
|
|
897
|
-
})
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
// Initialize the size of the hotspot area to match the croppable canvas.
|
|
901
|
-
_hotspotAreaResize(this.getElement().querySelector('.cropper-canvas'))
|
|
902
|
-
|
|
903
|
-
_visualizeHotspots()
|
|
904
|
-
|
|
905
|
-
// Hide all the initial hotspot overlays while they are being positioned.
|
|
906
|
-
hotspotArea
|
|
907
|
-
.querySelectorAll('.ImageEditor-hotspot')
|
|
908
|
-
.forEach((hotspotOverlay) => {
|
|
909
|
-
hotspotOverlay.classList.add('is-hidden')
|
|
910
|
-
})
|
|
911
|
-
|
|
912
|
-
callback()
|
|
913
|
-
|
|
914
|
-
return {
|
|
915
|
-
reflow,
|
|
916
|
-
reveal,
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
createFocusUI(preview, syncRenditions) {
|
|
921
|
-
const focusPoint = create('div', {
|
|
922
|
-
className: 'ImageEditor-focus-point',
|
|
923
|
-
title: tooltips.imageEditor.focus,
|
|
924
|
-
})
|
|
925
|
-
|
|
926
|
-
let initialFocusX = this.getElement().querySelector(
|
|
927
|
-
'input[name$=".focusX"]',
|
|
928
|
-
).value
|
|
929
|
-
let initialFocusY = this.getElement().querySelector(
|
|
930
|
-
'input[name$=".focusY"]',
|
|
931
|
-
).value
|
|
932
|
-
|
|
933
|
-
if (!initialFocusX && !initialFocusY) {
|
|
934
|
-
if (this.getImplicitFocus()) {
|
|
935
|
-
const implicitFocus = this.getImplicitFocus().split(',')
|
|
936
|
-
initialFocusX = implicitFocus[0]
|
|
937
|
-
initialFocusY = implicitFocus[1]
|
|
938
|
-
focusPoint.classList.add('is-implicit')
|
|
939
|
-
preview
|
|
940
|
-
.querySelector('.ImageEditor-implicitMessage')
|
|
941
|
-
.classList.remove('is-hidden')
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
const focusRender = (xPercent, yPercent) => {
|
|
946
|
-
focusPoint.style.left = `${xPercent}%`
|
|
947
|
-
focusPoint.style.top = `${yPercent}%`
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
const enable = () => {
|
|
951
|
-
focusContainer.style.pointerEvents = 'initial'
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
const disable = () => {
|
|
955
|
-
focusContainer.style.pointerEvents = 'none'
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
const resetFocusValues = () => {
|
|
959
|
-
const cropSizes = this._cropSelect.querySelectorAll(
|
|
960
|
-
'option:not([value=""])',
|
|
961
|
-
)
|
|
962
|
-
cropSizes.forEach((size) => {
|
|
963
|
-
const inputX = this.getElement().querySelector(
|
|
964
|
-
'input[name$=".focusX.' + size.value + '"]',
|
|
965
|
-
)
|
|
966
|
-
const inputY = this.getElement().querySelector(
|
|
967
|
-
'input[name$=".focusY.' + size.value + '"]',
|
|
968
|
-
)
|
|
969
|
-
if (inputX && inputY) {
|
|
970
|
-
inputX.value = ''
|
|
971
|
-
inputY.value = ''
|
|
972
|
-
}
|
|
973
|
-
})
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
const setFocusInputValues = ({ xPercent, yPercent }) => {
|
|
977
|
-
const index = this._cropSelect.selectedIndex
|
|
978
|
-
const selectedCrop = this._cropSelect.options[index]
|
|
979
|
-
const inputX =
|
|
980
|
-
selectedCrop.value === ''
|
|
981
|
-
? this.getElement().querySelector('input[name$=".focusX"]')
|
|
982
|
-
: this.getElement().querySelector(
|
|
983
|
-
'input[name$=".focusX.' + selectedCrop.value + '"]',
|
|
984
|
-
)
|
|
985
|
-
const inputY =
|
|
986
|
-
selectedCrop.value === ''
|
|
987
|
-
? this.getElement().querySelector('input[name$=".focusY"]')
|
|
988
|
-
: this.getElement().querySelector(
|
|
989
|
-
'input[name$=".focusY.' + selectedCrop.value + '"]',
|
|
990
|
-
)
|
|
991
|
-
const coords = getFocusCoordinatesNormalized({ xPercent, yPercent })
|
|
992
|
-
|
|
993
|
-
inputX.value = coords.xPercent / 100
|
|
994
|
-
inputY.value = coords.yPercent / 100
|
|
995
|
-
|
|
996
|
-
if (selectedCrop.value === '') {
|
|
997
|
-
resetFocusValues()
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
inputX.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1001
|
-
inputY.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
const moveTo = ({ xPercent, yPercent }) => {
|
|
1005
|
-
if (!find(preview, '.ImageEditor-focus-point')) {
|
|
1006
|
-
preview.append(focusPoint)
|
|
1007
|
-
}
|
|
1008
|
-
focusPoint.classList.remove('is-hidden')
|
|
1009
|
-
focusRender(xPercent, yPercent)
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
// Normalizing the coordinates undoes the client-side transformations (eg: rotation and flipping)
|
|
1013
|
-
// This is needed for storing the x/y coordinates on the backend.
|
|
1014
|
-
const getFocusCoordinatesNormalized = ({ xPercent, yPercent }) => {
|
|
1015
|
-
let focusPointData = {}
|
|
1016
|
-
|
|
1017
|
-
// no focus point is set; exit early.
|
|
1018
|
-
if (xPercent === '' && yPercent === '') {
|
|
1019
|
-
return null
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
const { rotate, scaleX, scaleY } = this._croptool.getImageData()
|
|
1023
|
-
const { width, height } = this._croptool.getImageData()
|
|
1024
|
-
const { width: nWidth, height: nHeight } = this._croptool.getCanvasData()
|
|
1025
|
-
|
|
1026
|
-
const x = (width * xPercent) / 100
|
|
1027
|
-
const y = (height * yPercent) / 100
|
|
1028
|
-
|
|
1029
|
-
focusPointData = { xPercent, yPercent }
|
|
1030
|
-
|
|
1031
|
-
const rotateCoords = (r, x, y, width, height, focusPointData) => {
|
|
1032
|
-
let newX
|
|
1033
|
-
let newY
|
|
1034
|
-
|
|
1035
|
-
if (r === 90 || r === -270) {
|
|
1036
|
-
newX = y
|
|
1037
|
-
newY = width - x
|
|
1038
|
-
} else if (r === 180 || r === -180) {
|
|
1039
|
-
newX = width - x
|
|
1040
|
-
newY = height - y
|
|
1041
|
-
} else if (r === -90 || r === 270) {
|
|
1042
|
-
newX = height - y
|
|
1043
|
-
newY = x
|
|
1044
|
-
} else {
|
|
1045
|
-
newX = x
|
|
1046
|
-
newY = y
|
|
1047
|
-
}
|
|
1048
|
-
focusPointData.xPercent = (newX / nWidth) * 100
|
|
1049
|
-
focusPointData.yPercent = (newY / nHeight) * 100
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
rotateCoords(rotate, x, y, width, height, focusPointData)
|
|
1053
|
-
|
|
1054
|
-
// Translate point for flip:
|
|
1055
|
-
if (scaleX === -1 && scaleY === -1) {
|
|
1056
|
-
// if both have flipped, just calculate the new rotation from that.
|
|
1057
|
-
let newRotate = rotate - 180
|
|
1058
|
-
rotateCoords(newRotate, x, y, width, height, focusPointData)
|
|
1059
|
-
} else {
|
|
1060
|
-
// image flipped horizontally?
|
|
1061
|
-
if (scaleX === -1) {
|
|
1062
|
-
if (rotate === 90 || rotate === -270) {
|
|
1063
|
-
focusPointData.xPercent = 100 - focusPointData.xPercent
|
|
1064
|
-
} else if (rotate === 180 || rotate === -180) {
|
|
1065
|
-
focusPointData.xPercent = xPercent
|
|
1066
|
-
} else if (rotate === -90 || rotate === 270) {
|
|
1067
|
-
focusPointData.xPercent = 100 - focusPointData.xPercent
|
|
1068
|
-
} else {
|
|
1069
|
-
focusPointData.xPercent = 100 - xPercent
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
// image flipped vertically?
|
|
1074
|
-
if (scaleY === -1) {
|
|
1075
|
-
if (rotate === 90 || rotate === -270) {
|
|
1076
|
-
focusPointData.xPercent = yPercent
|
|
1077
|
-
focusPointData.yPercent = xPercent
|
|
1078
|
-
} else if (rotate === 180 || rotate === -180) {
|
|
1079
|
-
focusPointData.xPercent = 100 - xPercent
|
|
1080
|
-
focusPointData.yPercent = yPercent
|
|
1081
|
-
} else if (rotate === -90 || rotate === 270) {
|
|
1082
|
-
focusPointData.xPercent = 100 - focusPointData.yPercent
|
|
1083
|
-
focusPointData.yPercent = focusPointData.xPercent
|
|
1084
|
-
} else {
|
|
1085
|
-
focusPointData.yPercent = 100 - yPercent
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
return {
|
|
1091
|
-
x: focusPointData.xPercent / 100,
|
|
1092
|
-
y: focusPointData.yPercent / 100,
|
|
1093
|
-
xPercent: focusPointData.xPercent,
|
|
1094
|
-
yPercent: focusPointData.yPercent,
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
// returns normalized x/y percentages which factor in rotation.
|
|
1099
|
-
// typically used for the realtime/client positioning.
|
|
1100
|
-
const getFocusCoordinatesRelative = (cropRatio) => {
|
|
1101
|
-
let focusPointData = {}
|
|
1102
|
-
|
|
1103
|
-
const focusPointCoords = this.getFocusCoords(cropRatio)
|
|
1104
|
-
|
|
1105
|
-
// no focus point is set; exit early.
|
|
1106
|
-
if (
|
|
1107
|
-
focusPointCoords.xPercent === null &&
|
|
1108
|
-
focusPointCoords.yPercent === null
|
|
1109
|
-
) {
|
|
1110
|
-
if (this.getImplicitFocus()) {
|
|
1111
|
-
const implicitFocus = this.getImplicitFocus().split(',')
|
|
1112
|
-
focusPointCoords.xPercent = implicitFocus[0] * 100
|
|
1113
|
-
focusPointCoords.yPercent = implicitFocus[1] * 100
|
|
1114
|
-
} else {
|
|
1115
|
-
return null
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
const { rotate, scaleX, scaleY } = this._croptool.getImageData()
|
|
1120
|
-
const { width, height } = this._croptool.getImageData()
|
|
1121
|
-
const { width: nWidth, height: nHeight } = this._croptool.getCanvasData()
|
|
1122
|
-
const x = (width * focusPointCoords.xPercent) / 100
|
|
1123
|
-
const y = (height * focusPointCoords.yPercent) / 100
|
|
1124
|
-
|
|
1125
|
-
focusPointData = { ...focusPointCoords }
|
|
1126
|
-
|
|
1127
|
-
const rotateCoords = (r, x, y, width, height, focusPointData) => {
|
|
1128
|
-
let newX
|
|
1129
|
-
let newY
|
|
1130
|
-
|
|
1131
|
-
if (r === 90 || r === -270) {
|
|
1132
|
-
newX = height - y
|
|
1133
|
-
newY = x
|
|
1134
|
-
} else if (r === 180 || r === -180) {
|
|
1135
|
-
newX = width - x
|
|
1136
|
-
newY = height - y
|
|
1137
|
-
} else if (r === -90 || r === 270) {
|
|
1138
|
-
newX = y
|
|
1139
|
-
newY = width - x
|
|
1140
|
-
} else {
|
|
1141
|
-
newX = x
|
|
1142
|
-
newY = y
|
|
1143
|
-
}
|
|
1144
|
-
focusPointData.xPercent = (newX / nWidth) * 100
|
|
1145
|
-
focusPointData.yPercent = (newY / nHeight) * 100
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
rotateCoords(rotate, x, y, width, height, focusPointData)
|
|
1149
|
-
|
|
1150
|
-
// Translate point for flip:
|
|
1151
|
-
if (scaleX === -1 && scaleY === -1) {
|
|
1152
|
-
// if both have flipped, just calculate the new rotation from that.
|
|
1153
|
-
let newRotate = rotate - 180
|
|
1154
|
-
rotateCoords(newRotate, x, y, width, height, focusPointData)
|
|
1155
|
-
} else {
|
|
1156
|
-
// image flipped horizontally?
|
|
1157
|
-
if (scaleX === -1) {
|
|
1158
|
-
if (rotate === 90 || rotate === -270) {
|
|
1159
|
-
focusPointData.yPercent = 100 - focusPointCoords.xPercent
|
|
1160
|
-
} else if (rotate === 180 || rotate === -180) {
|
|
1161
|
-
focusPointData.xPercent = focusPointCoords.xPercent
|
|
1162
|
-
} else if (rotate === -90 || rotate === 270) {
|
|
1163
|
-
focusPointData.yPercent = focusPointCoords.xPercent
|
|
1164
|
-
} else {
|
|
1165
|
-
focusPointData.xPercent = 100 - focusPointCoords.xPercent
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
// image flipped vertically?
|
|
1170
|
-
if (scaleY === -1) {
|
|
1171
|
-
if (rotate === 90 || rotate === -270) {
|
|
1172
|
-
focusPointData.xPercent = focusPointCoords.yPercent
|
|
1173
|
-
} else if (rotate === 180 || rotate === -180) {
|
|
1174
|
-
focusPointData.xPercent = 100 - focusPointCoords.xPercent
|
|
1175
|
-
focusPointData.yPercent = focusPointCoords.yPercent
|
|
1176
|
-
} else if (rotate === 270) {
|
|
1177
|
-
focusPointData.xPercent = 100 - focusPointData.xPercent
|
|
1178
|
-
} else {
|
|
1179
|
-
focusPointData.yPercent = 100 - focusPointCoords.yPercent
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
return {
|
|
1185
|
-
x: focusPointData.xPercent / 100,
|
|
1186
|
-
y: focusPointData.yPercent / 100,
|
|
1187
|
-
xPercent: focusPointData.xPercent,
|
|
1188
|
-
yPercent: focusPointData.yPercent,
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
const focusContainer = create('div', {
|
|
1193
|
-
class: 'ImageEditor-focus-container',
|
|
1194
|
-
dblclick: (event) => event.preventDefault(),
|
|
1195
|
-
onclick: (event) => {
|
|
1196
|
-
event.preventDefault()
|
|
1197
|
-
const focusPoint = find(preview, '.ImageEditor-focus-point')
|
|
1198
|
-
const implicitMessage = find(preview, '.ImageEditor-implicitMessage')
|
|
1199
|
-
if (focusPoint) {
|
|
1200
|
-
focusPoint.classList.remove('is-implicit')
|
|
1201
|
-
}
|
|
1202
|
-
if (implicitMessage) {
|
|
1203
|
-
implicitMessage.classList.add('is-hidden')
|
|
1204
|
-
}
|
|
1205
|
-
const message = find(preview, '.ImageEditor-focusMessage')
|
|
1206
|
-
message.classList.add('is-hidden')
|
|
1207
|
-
const clickCoords = this.getClickCoordsLocal(event.target, event)
|
|
1208
|
-
clickCoords.xPercent *= 100
|
|
1209
|
-
clickCoords.yPercent *= 100
|
|
1210
|
-
const { xPercent, yPercent } = clickCoords
|
|
1211
|
-
setFocusInputValues({ xPercent, yPercent })
|
|
1212
|
-
moveTo({ xPercent, yPercent })
|
|
1213
|
-
this.showPreview()
|
|
1214
|
-
syncRenditions()
|
|
1215
|
-
},
|
|
1216
|
-
})
|
|
1217
|
-
|
|
1218
|
-
const imageContainer = this.getPreviewContainer()
|
|
1219
|
-
imageContainer.appendChild(focusContainer)
|
|
1220
|
-
|
|
1221
|
-
if (initialFocusX !== '' && initialFocusY !== '') {
|
|
1222
|
-
const xPercent = initialFocusX * 100
|
|
1223
|
-
const yPercent = initialFocusY * 100
|
|
1224
|
-
moveTo({ xPercent, yPercent })
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
return {
|
|
1228
|
-
disable,
|
|
1229
|
-
enable,
|
|
1230
|
-
moveTo,
|
|
1231
|
-
getFocusCoordinatesRelative,
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
applyAllEdits(data) {
|
|
1236
|
-
const {
|
|
1237
|
-
brightness,
|
|
1238
|
-
contrast,
|
|
1239
|
-
invert,
|
|
1240
|
-
grayscale,
|
|
1241
|
-
sepia,
|
|
1242
|
-
flipH,
|
|
1243
|
-
flipV,
|
|
1244
|
-
rotate,
|
|
1245
|
-
} = { ...data }
|
|
1246
|
-
|
|
1247
|
-
this.applyFilter('brightness', `${this.convertValueToPercent(brightness)}%`)
|
|
1248
|
-
this.applyFilter('contrast', `${this.convertValueToPercent(contrast)}%`)
|
|
1249
|
-
this.applyFilter('sepia', sepia ? '100%' : null)
|
|
1250
|
-
this.applyFilter('invert', invert ? '100%' : null)
|
|
1251
|
-
this.applyFilter('grayscale', grayscale ? '100%' : null)
|
|
1252
|
-
|
|
1253
|
-
flipH
|
|
1254
|
-
? this._croptool.scale(-1, flipV ? -1 : 1)
|
|
1255
|
-
: this._croptool.scale(1, flipV ? -1 : 1)
|
|
1256
|
-
|
|
1257
|
-
flipV
|
|
1258
|
-
? this._croptool.scale(flipH ? -1 : 1, -1)
|
|
1259
|
-
: this._croptool.scale(flipH ? -1 : 1, 1)
|
|
1260
|
-
|
|
1261
|
-
if (rotate) {
|
|
1262
|
-
this._croptool.rotateTo(rotate)
|
|
1263
|
-
this.moveFocusCoords()
|
|
1264
|
-
this.updateAllRenditionPreviews()
|
|
1265
|
-
this.repaint(document.body.offsetHeight)
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
createEditUI(target, ui, croptool, focusUI, syncRenditions, repaintHandler) {
|
|
1270
|
-
const defaults = this.getDefaults()
|
|
1271
|
-
const tools = target.querySelector('.imageEditor-tools')
|
|
1272
|
-
const adjustments = target.querySelector('.imageEditor-edit')
|
|
1273
|
-
|
|
1274
|
-
const editableArea = create('div', {
|
|
1275
|
-
class: 'ImageEditor-editableArea',
|
|
1276
|
-
onclick: (event) => event.preventDefault(),
|
|
1277
|
-
dblclick: (event) => event.preventDefault(),
|
|
1278
|
-
})
|
|
1279
|
-
|
|
1280
|
-
this.getElement()
|
|
1281
|
-
.querySelector('.ImageEditor-main')
|
|
1282
|
-
.appendChild(editableArea)
|
|
1283
|
-
|
|
1284
|
-
// Server values range from -1 to 1 and must be converted to percentage.
|
|
1285
|
-
const convertValueToPercent = (val) => {
|
|
1286
|
-
let amount = parseFloat(val)
|
|
1287
|
-
amount = amount * 100 + 100
|
|
1288
|
-
return amount
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
const e = this.getElement()
|
|
1292
|
-
const brightnessInput = e.querySelector('input[name$=".brightness"]')
|
|
1293
|
-
const contrastInput = e.querySelector('input[name$=".contrast"]')
|
|
1294
|
-
const sepiaToggle = e.querySelector('input[name$=".sepia"]')
|
|
1295
|
-
const invertToggle = e.querySelector('input[name$=".invert"]')
|
|
1296
|
-
const grayscaleToggle = e.querySelector('input[name$=".grayscale"]')
|
|
1297
|
-
const noFilterToggle = e.querySelector('input[name$=".no-filter"]')
|
|
1298
|
-
|
|
1299
|
-
const updateAllInputs = (data) => {
|
|
1300
|
-
const context = this.getElement()
|
|
1301
|
-
Object.entries(data).forEach(([key, val]) => {
|
|
1302
|
-
const input = context.querySelector(
|
|
1303
|
-
`.ImageEditor-edit input[name$='.${key}']`,
|
|
1304
|
-
)
|
|
1305
|
-
input.type === 'checkbox' ? (input.checked = val) : (input.value = val)
|
|
1306
|
-
})
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
brightnessInput.onchange = (event) => {
|
|
1310
|
-
this.applyFilter(
|
|
1311
|
-
'brightness',
|
|
1312
|
-
`${convertValueToPercent(event.target.value)}%`,
|
|
1313
|
-
)
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
contrastInput.onchange = (event) => {
|
|
1317
|
-
this.applyFilter(
|
|
1318
|
-
'contrast',
|
|
1319
|
-
`${convertValueToPercent(event.target.value)}%`,
|
|
1320
|
-
)
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
const filters = {
|
|
1324
|
-
grayscale: grayscaleToggle,
|
|
1325
|
-
invert: invertToggle,
|
|
1326
|
-
sepia: sepiaToggle,
|
|
1327
|
-
['no-filter']: noFilterToggle,
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
/* The checkbox inputs function as radio buttons, meaning when an input is clicked, it should not be uncheckable if it was already checked */
|
|
1331
|
-
const toggleCheckedFilter = (filterName) => {
|
|
1332
|
-
if (!filters[filterName].checked) {
|
|
1333
|
-
filters[filterName].checked = true
|
|
1334
|
-
} else if (filterName !== 'no-filter') {
|
|
1335
|
-
this.applyFilter(
|
|
1336
|
-
filterName,
|
|
1337
|
-
filters[filterName].checked ? '100%' : null,
|
|
1338
|
-
)
|
|
1339
|
-
}
|
|
1340
|
-
for (const filter in filters) {
|
|
1341
|
-
if (filter !== filterName) {
|
|
1342
|
-
this.applyFilter(filter, null)
|
|
1343
|
-
filters[filter].checked = false
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
sepiaToggle.onchange = () => {
|
|
1349
|
-
toggleCheckedFilter('sepia')
|
|
1350
|
-
}
|
|
1351
|
-
|
|
1352
|
-
invertToggle.onchange = () => {
|
|
1353
|
-
toggleCheckedFilter('invert')
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
grayscaleToggle.onchange = () => {
|
|
1357
|
-
toggleCheckedFilter('grayscale')
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
noFilterToggle.onchange = () => {
|
|
1361
|
-
toggleCheckedFilter('no-filter')
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
const rotateLeftButton = create(
|
|
1365
|
-
'button',
|
|
1366
|
-
{
|
|
1367
|
-
class: 'ImageEditor-rotate-left',
|
|
1368
|
-
'aria-label': tooltips.imageEditor.rotateLeft,
|
|
1369
|
-
title: tooltips.imageEditor.rotateLeft,
|
|
1370
|
-
type: 'button',
|
|
1371
|
-
onclick: (event) => {
|
|
1372
|
-
croptool.rotate(-90)
|
|
1373
|
-
this.moveFocusCoords()
|
|
1374
|
-
syncRenditions()
|
|
1375
|
-
repaintHandler()
|
|
1376
|
-
setRotationInputValue(croptool.getData().rotate)
|
|
1377
|
-
},
|
|
1378
|
-
},
|
|
1379
|
-
tooltips.imageEditor.rotateLeft,
|
|
1380
|
-
)
|
|
1381
|
-
|
|
1382
|
-
const rotateRightButton = create(
|
|
1383
|
-
'button',
|
|
1384
|
-
{
|
|
1385
|
-
class: 'ImageEditor-rotate-right',
|
|
1386
|
-
'aria-label': tooltips.imageEditor.rotateRight,
|
|
1387
|
-
title: tooltips.imageEditor.rotateRight,
|
|
1388
|
-
type: 'button',
|
|
1389
|
-
onclick: (event) => {
|
|
1390
|
-
croptool.rotate(90)
|
|
1391
|
-
this.moveFocusCoords()
|
|
1392
|
-
syncRenditions()
|
|
1393
|
-
repaintHandler()
|
|
1394
|
-
setRotationInputValue(croptool.getData().rotate)
|
|
1395
|
-
},
|
|
1396
|
-
},
|
|
1397
|
-
tooltips.imageEditor.rotateRight,
|
|
1398
|
-
)
|
|
1399
|
-
|
|
1400
|
-
const flipHorizontalButton = create(
|
|
1401
|
-
'button',
|
|
1402
|
-
{
|
|
1403
|
-
class: 'ImageEditor-flip-h',
|
|
1404
|
-
'aria-label': tooltips.imageEditor.flipH,
|
|
1405
|
-
title: tooltips.imageEditor.flipH,
|
|
1406
|
-
type: 'button',
|
|
1407
|
-
onclick: (event) => {
|
|
1408
|
-
const rotation = croptool.getData().rotate
|
|
1409
|
-
|
|
1410
|
-
// When the image is rotated onto it's side, the flip behavior has to change since it uses the source
|
|
1411
|
-
// image (non-rotated) to base the transformation off of. So, if the image is rotated on it's side, flip vertically.
|
|
1412
|
-
rotation === 0 || rotation === 180 || rotation === -180
|
|
1413
|
-
? flipH()
|
|
1414
|
-
: flipV()
|
|
1415
|
-
|
|
1416
|
-
syncRenditions()
|
|
1417
|
-
repaintHandler()
|
|
1418
|
-
},
|
|
1419
|
-
},
|
|
1420
|
-
tooltips.imageEditor.flipH,
|
|
1421
|
-
)
|
|
1422
|
-
|
|
1423
|
-
const flipVerticalButton = create(
|
|
1424
|
-
'button',
|
|
1425
|
-
{
|
|
1426
|
-
class: 'ImageEditor-flip-v',
|
|
1427
|
-
'aria-label': tooltips.imageEditor.flipV,
|
|
1428
|
-
title: tooltips.imageEditor.flipV,
|
|
1429
|
-
type: 'button',
|
|
1430
|
-
onclick: (event) => {
|
|
1431
|
-
const rotation = croptool.getData().rotate
|
|
1432
|
-
|
|
1433
|
-
// When the image is rotated onto it's side, the flip behavior has to change since it uses the source
|
|
1434
|
-
// image (non-rotated) to base the transformation off of. So, if the image is rotated on it's side, flip horizontally.
|
|
1435
|
-
rotation === 0 || rotation === 180 || rotation === -180
|
|
1436
|
-
? flipV()
|
|
1437
|
-
: flipH()
|
|
1438
|
-
|
|
1439
|
-
syncRenditions()
|
|
1440
|
-
repaintHandler()
|
|
1441
|
-
},
|
|
1442
|
-
},
|
|
1443
|
-
tooltips.imageEditor.flipV,
|
|
1444
|
-
)
|
|
1445
|
-
|
|
1446
|
-
const resetButton = create(
|
|
1447
|
-
'button',
|
|
1448
|
-
{
|
|
1449
|
-
class: 'ImageEditor-resetButton',
|
|
1450
|
-
'aria-label': tooltips.reset,
|
|
1451
|
-
type: 'button',
|
|
1452
|
-
onclick: (event) => {
|
|
1453
|
-
event.preventDefault()
|
|
1454
|
-
event.stopPropagation()
|
|
1455
|
-
this.applyAllEdits(defaults)
|
|
1456
|
-
updateAllInputs(defaults)
|
|
1457
|
-
this.resetAllRenditionPreviews()
|
|
1458
|
-
repaintHandler()
|
|
1459
|
-
},
|
|
1460
|
-
onkeydown: (event) => {
|
|
1461
|
-
if (event.key === 'Tab' && !event.shiftKey) {
|
|
1462
|
-
event.preventDefault()
|
|
1463
|
-
this._element.querySelector('.TabBar-item.is-selected').focus()
|
|
1464
|
-
}
|
|
1465
|
-
},
|
|
1466
|
-
},
|
|
1467
|
-
tooltips.reset,
|
|
1468
|
-
)
|
|
1469
|
-
|
|
1470
|
-
const actionBar = find(this.getElement(), '.ActionBar')
|
|
1471
|
-
actionBar.appendChild(resetButton)
|
|
1472
|
-
|
|
1473
|
-
const flipH = () => {
|
|
1474
|
-
const isFlippedH = getFlipHInputValue()
|
|
1475
|
-
const isFlippedV = getFlipVInputValue()
|
|
1476
|
-
croptool.scale(isFlippedH ? 1 : -1, isFlippedV ? -1 : 1)
|
|
1477
|
-
this.moveFocusCoords()
|
|
1478
|
-
setFlipHInputValue()
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
const flipV = () => {
|
|
1482
|
-
const isFlippedV = getFlipVInputValue()
|
|
1483
|
-
const isFlippedH = getFlipHInputValue()
|
|
1484
|
-
croptool.scale(isFlippedH ? -1 : 1, isFlippedV ? 1 : -1)
|
|
1485
|
-
this.moveFocusCoords()
|
|
1486
|
-
setFlipVInputValue()
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
const getFlipHInputValue = () => {
|
|
1490
|
-
const name = 'flipH'
|
|
1491
|
-
const input = this.getElement().querySelector(
|
|
1492
|
-
`.ImageEditor-edit input[name$='.${name}']`,
|
|
1493
|
-
)
|
|
1494
|
-
|
|
1495
|
-
return input ? input.checked : false
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
const setFlipHInputValue = () => {
|
|
1499
|
-
const name = 'flipH'
|
|
1500
|
-
const input = this.getElement().querySelector(
|
|
1501
|
-
`.ImageEditor-edit input[name$='.${name}']`,
|
|
1502
|
-
)
|
|
1503
|
-
if (input) {
|
|
1504
|
-
input.checked = !input.checked
|
|
1505
|
-
input.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
const getFlipVInputValue = () => {
|
|
1510
|
-
const name = 'flipV'
|
|
1511
|
-
const input = this.getElement().querySelector(
|
|
1512
|
-
`.ImageEditor-edit input[name$='.${name}']`,
|
|
1513
|
-
)
|
|
1514
|
-
|
|
1515
|
-
return input ? input.checked : false
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
const setFlipVInputValue = () => {
|
|
1519
|
-
const name = 'flipV'
|
|
1520
|
-
const input = this.getElement().querySelector(
|
|
1521
|
-
`.ImageEditor-edit input[name$='.${name}']`,
|
|
1522
|
-
)
|
|
1523
|
-
if (input) {
|
|
1524
|
-
input.checked = !input.checked
|
|
1525
|
-
input.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
const setRotationInputValue = (val) => {
|
|
1530
|
-
const name = 'rotate'
|
|
1531
|
-
const input = this.getElement().querySelector(
|
|
1532
|
-
`.ImageEditor-edit input[name$='.${name}']`,
|
|
1533
|
-
)
|
|
1534
|
-
|
|
1535
|
-
let adjustedVal = val
|
|
1536
|
-
|
|
1537
|
-
// convert values to stay within backend range of -90 -> 180
|
|
1538
|
-
if (val === -180) {
|
|
1539
|
-
adjustedVal = 180
|
|
1540
|
-
} else if (val === -270) {
|
|
1541
|
-
adjustedVal = 90
|
|
1542
|
-
} else if (val === 270) {
|
|
1543
|
-
adjustedVal = -90
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
if (input) {
|
|
1547
|
-
input.value = adjustedVal
|
|
1548
|
-
input.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
const brightnessUI = create(
|
|
1553
|
-
'div',
|
|
1554
|
-
{
|
|
1555
|
-
class: 'CIG-row',
|
|
1556
|
-
},
|
|
1557
|
-
create(
|
|
1558
|
-
'label',
|
|
1559
|
-
{ class: 'CIG-label' },
|
|
1560
|
-
tooltips.imageEditor.brightness,
|
|
1561
|
-
brightnessInput,
|
|
1562
|
-
),
|
|
1563
|
-
)
|
|
1564
|
-
|
|
1565
|
-
const contrastUI = create(
|
|
1566
|
-
'div',
|
|
1567
|
-
{
|
|
1568
|
-
class: 'CIG-row',
|
|
1569
|
-
},
|
|
1570
|
-
create(
|
|
1571
|
-
'label',
|
|
1572
|
-
{ class: 'CIG-label' },
|
|
1573
|
-
tooltips.imageEditor.contrast,
|
|
1574
|
-
contrastInput,
|
|
1575
|
-
),
|
|
1576
|
-
)
|
|
1577
|
-
|
|
1578
|
-
const filtersUI = create(
|
|
1579
|
-
'div',
|
|
1580
|
-
{
|
|
1581
|
-
class: 'CIG-row',
|
|
1582
|
-
role: 'radiogroup',
|
|
1583
|
-
},
|
|
1584
|
-
create('div', { class: 'CIG-label' }, 'Filter'),
|
|
1585
|
-
create(
|
|
1586
|
-
'div',
|
|
1587
|
-
{
|
|
1588
|
-
class: 'CIG-small',
|
|
1589
|
-
role: 'radio',
|
|
1590
|
-
tabIndex: 0,
|
|
1591
|
-
},
|
|
1592
|
-
create('label', noFilterToggle, tooltips.imageEditor.filters),
|
|
1593
|
-
),
|
|
1594
|
-
create(
|
|
1595
|
-
'div',
|
|
1596
|
-
{
|
|
1597
|
-
class: 'CIG-small',
|
|
1598
|
-
role: 'radio',
|
|
1599
|
-
tabIndex: 0,
|
|
1600
|
-
},
|
|
1601
|
-
create('label', sepiaToggle, tooltips.imageEditor.sepia),
|
|
1602
|
-
),
|
|
1603
|
-
create(
|
|
1604
|
-
'div',
|
|
1605
|
-
{
|
|
1606
|
-
class: 'CIG-small',
|
|
1607
|
-
role: 'radio',
|
|
1608
|
-
tabIndex: 0,
|
|
1609
|
-
},
|
|
1610
|
-
create('label', invertToggle, tooltips.imageEditor.invert),
|
|
1611
|
-
),
|
|
1612
|
-
create(
|
|
1613
|
-
'div',
|
|
1614
|
-
{
|
|
1615
|
-
class: 'CIG-small',
|
|
1616
|
-
role: 'radio',
|
|
1617
|
-
tabIndex: 0,
|
|
1618
|
-
},
|
|
1619
|
-
create('label', grayscaleToggle, tooltips.imageEditor.grayscale),
|
|
1620
|
-
),
|
|
1621
|
-
)
|
|
1622
|
-
|
|
1623
|
-
const rotationUI = create(
|
|
1624
|
-
'div',
|
|
1625
|
-
{
|
|
1626
|
-
class: 'CIG-row',
|
|
1627
|
-
},
|
|
1628
|
-
create(
|
|
1629
|
-
'label',
|
|
1630
|
-
{ class: 'CIG-label' },
|
|
1631
|
-
tooltips.imageEditor.rotate + '/' + tooltips.imageEditor.flip,
|
|
1632
|
-
),
|
|
1633
|
-
create('div', { class: 'CIG-small' }, [
|
|
1634
|
-
rotateLeftButton,
|
|
1635
|
-
rotateRightButton,
|
|
1636
|
-
flipHorizontalButton,
|
|
1637
|
-
flipVerticalButton,
|
|
1638
|
-
]),
|
|
1639
|
-
)
|
|
1640
|
-
|
|
1641
|
-
adjustments.append(rotationUI, brightnessUI, contrastUI, filtersUI)
|
|
1642
|
-
|
|
1643
|
-
ui.appendChild(tools)
|
|
1644
|
-
ui.appendChild(adjustments)
|
|
1645
|
-
|
|
1646
|
-
const initialEditValues = this.getEditValues()
|
|
1647
|
-
this.applyAllEdits(initialEditValues)
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
filterRenditionPreviews() {
|
|
1651
|
-
const index = this._cropSelect.selectedIndex
|
|
1652
|
-
const selectedCrop = this._cropSelect.options[index]
|
|
1653
|
-
this.hideCropUI()
|
|
1654
|
-
this._renditions.forEach((rendition) => {
|
|
1655
|
-
if (rendition.current) {
|
|
1656
|
-
this.toggleRendition(rendition)
|
|
1657
|
-
}
|
|
1658
|
-
const renditionElm = this.getRenditionElFromRendition(rendition)
|
|
1659
|
-
renditionElm.classList.toggle(
|
|
1660
|
-
'is-visible',
|
|
1661
|
-
selectedCrop.value === ''
|
|
1662
|
-
? true
|
|
1663
|
-
: rendition.cropRatio === selectedCrop.value,
|
|
1664
|
-
)
|
|
1665
|
-
})
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
moveFocusCoords() {
|
|
1669
|
-
if (!this._focusUI) {
|
|
1670
|
-
return
|
|
1671
|
-
}
|
|
1672
|
-
const coords = this._focusUI.getFocusCoordinatesRelative()
|
|
1673
|
-
if (coords) {
|
|
1674
|
-
this._focusUI.moveTo(coords)
|
|
1675
|
-
}
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
showPreview() {
|
|
1679
|
-
const index = this._cropSelect.selectedIndex
|
|
1680
|
-
const selectedCrop = this._cropSelect.options[index]
|
|
1681
|
-
const group = selectedCrop.value
|
|
1682
|
-
const focusData = this._focusUI.getFocusCoordinatesRelative(group)
|
|
1683
|
-
|
|
1684
|
-
const { width, height } = this.getPreviewContainer().getBoundingClientRect()
|
|
1685
|
-
const inputAspectRatio = Math.floor((width / height) * 100) / 100
|
|
1686
|
-
let outputWidth, outputHeight
|
|
1687
|
-
let left = 0
|
|
1688
|
-
let right = 0
|
|
1689
|
-
let top = 0
|
|
1690
|
-
let bottom = 0
|
|
1691
|
-
|
|
1692
|
-
if (group !== '' && focusData) {
|
|
1693
|
-
const aspectRatio = selectedCrop.getAttribute('data-preview-ratio')
|
|
1694
|
-
if (inputAspectRatio > aspectRatio) {
|
|
1695
|
-
outputWidth = height * aspectRatio
|
|
1696
|
-
left = width * focusData.x - outputWidth / 2
|
|
1697
|
-
if (left <= 0) {
|
|
1698
|
-
left = 0
|
|
1699
|
-
}
|
|
1700
|
-
right = width - outputWidth - left
|
|
1701
|
-
if (right <= 0) {
|
|
1702
|
-
right = 0
|
|
1703
|
-
left = width - outputWidth
|
|
1704
|
-
}
|
|
1705
|
-
} else if (inputAspectRatio < aspectRatio) {
|
|
1706
|
-
outputHeight = width / aspectRatio
|
|
1707
|
-
top = height * focusData.y - outputHeight / 2
|
|
1708
|
-
if (top <= 0) {
|
|
1709
|
-
top = 0
|
|
1710
|
-
}
|
|
1711
|
-
bottom = height - outputHeight - top
|
|
1712
|
-
if (bottom <= 0) {
|
|
1713
|
-
bottom = 0
|
|
1714
|
-
top = height - outputHeight
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
this.imageMask.style.borderLeftWidth = `${left}px`
|
|
1719
|
-
this.imageMask.style.borderRightWidth = `${right}px`
|
|
1720
|
-
this.imageMask.style.borderTopWidth = `${top}px`
|
|
1721
|
-
this.imageMask.style.borderBottomWidth = `${bottom}px`
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
getClickCoordsLocal(element, clickEvent) {
|
|
1725
|
-
const { width, height, top, left } = element.getBoundingClientRect()
|
|
1726
|
-
const offset = {
|
|
1727
|
-
top: top + document.body.scrollTop,
|
|
1728
|
-
left: left + document.body.scrollLeft,
|
|
1729
|
-
}
|
|
1730
|
-
let x = Math.ceil(clickEvent.pageX - offset.left) || 1
|
|
1731
|
-
let y = Math.ceil(clickEvent.pageY - offset.top) || 1
|
|
1732
|
-
|
|
1733
|
-
// bound values
|
|
1734
|
-
if (x <= 0) {
|
|
1735
|
-
x = 1
|
|
1736
|
-
}
|
|
1737
|
-
if (y <= 0) {
|
|
1738
|
-
y = 1
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
return {
|
|
1742
|
-
x,
|
|
1743
|
-
y,
|
|
1744
|
-
xPercent: x / width,
|
|
1745
|
-
yPercent: y / height,
|
|
1746
|
-
width,
|
|
1747
|
-
height,
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
|
|
1751
|
-
getCropTool() {
|
|
1752
|
-
return this._croptool
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
getElement() {
|
|
1756
|
-
return this._element
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
|
-
getForm() {
|
|
1760
|
-
return this._form
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
getImplicitFocus() {
|
|
1764
|
-
return this._implicitFocus
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
getPreviewCanvas() {
|
|
1768
|
-
return this.getPreviewContainer().querySelector('canvas')
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
getPreviewContainer() {
|
|
1772
|
-
return this.getElement().parentNode.querySelector('.ImageEditor-preview')
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
getRenditions() {
|
|
1776
|
-
const sizeInfos = {}
|
|
1777
|
-
const INPUT_NAMES = [
|
|
1778
|
-
'x',
|
|
1779
|
-
'y',
|
|
1780
|
-
'width',
|
|
1781
|
-
'height',
|
|
1782
|
-
'texts',
|
|
1783
|
-
'textSizes',
|
|
1784
|
-
'textXs',
|
|
1785
|
-
'textYs',
|
|
1786
|
-
'textWidths',
|
|
1787
|
-
]
|
|
1788
|
-
|
|
1789
|
-
const imageSizes = this.getElement().querySelector(
|
|
1790
|
-
'.imageEditor-sizes table',
|
|
1791
|
-
)
|
|
1792
|
-
|
|
1793
|
-
if (imageSizes) {
|
|
1794
|
-
imageSizes.querySelectorAll('th').forEach((heading) => {
|
|
1795
|
-
const row = heading.closest('tr')
|
|
1796
|
-
const group = row.getAttribute('data-size-group')
|
|
1797
|
-
const name = row.getAttribute('data-size-name')
|
|
1798
|
-
const displayName = row.getAttribute('data-display-name')
|
|
1799
|
-
const description = row.getAttribute('data-description')
|
|
1800
|
-
const width = parseFloat(row.getAttribute('data-size-width'))
|
|
1801
|
-
const height = parseFloat(row.getAttribute('data-size-height'))
|
|
1802
|
-
let independent = row.getAttribute('data-size-independent') === 'true'
|
|
1803
|
-
let aspectRatio = width / height
|
|
1804
|
-
|
|
1805
|
-
// If the width or height is zero, then this is a special case
|
|
1806
|
-
// and there is no fixed aspect ratio.
|
|
1807
|
-
// Instead the other dimension specifies the size, and the
|
|
1808
|
-
// width or height should be adjusted automatically.
|
|
1809
|
-
if (width === 0 || height === 0) {
|
|
1810
|
-
aspectRatio = 0
|
|
1811
|
-
independent = true
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
|
-
// Create pointers to each of the hidden inputs that this size uses.
|
|
1815
|
-
const inputs = {}
|
|
1816
|
-
INPUT_NAMES.forEach((name) => {
|
|
1817
|
-
inputs[name] = row.querySelector(`input[name$='.${name}']`)
|
|
1818
|
-
})
|
|
1819
|
-
|
|
1820
|
-
const focusCrop = this.getFocusCoords()
|
|
1821
|
-
|
|
1822
|
-
// Save the size information so we can use it later
|
|
1823
|
-
sizeInfos[name] = {
|
|
1824
|
-
group,
|
|
1825
|
-
name,
|
|
1826
|
-
description,
|
|
1827
|
-
displayName,
|
|
1828
|
-
inputs,
|
|
1829
|
-
independent,
|
|
1830
|
-
width,
|
|
1831
|
-
height,
|
|
1832
|
-
aspectRatio,
|
|
1833
|
-
focusCrop,
|
|
1834
|
-
}
|
|
1835
|
-
})
|
|
1836
|
-
}
|
|
1837
|
-
|
|
1838
|
-
return sizeInfos
|
|
1839
|
-
}
|
|
1840
|
-
|
|
1841
|
-
flattenAndSortRenditions(renditions) {
|
|
1842
|
-
const renditionsMap = new Map()
|
|
1843
|
-
|
|
1844
|
-
const createRenditionEntry = (rendition) => {
|
|
1845
|
-
return {
|
|
1846
|
-
name: rendition.name,
|
|
1847
|
-
cropRatio: rendition.group,
|
|
1848
|
-
sizeInfos: { [rendition.name]: { ...renditions[rendition.name] } },
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
|
|
1852
|
-
const sort = (map) => {
|
|
1853
|
-
return new Map(
|
|
1854
|
-
Array.from(map).sort((a, b) => {
|
|
1855
|
-
return a[0] - b[0]
|
|
1856
|
-
}),
|
|
1857
|
-
)
|
|
1858
|
-
}
|
|
1859
|
-
|
|
1860
|
-
for (const key in renditions) {
|
|
1861
|
-
const rendition = renditions[key]
|
|
1862
|
-
renditionsMap.set(rendition.name, createRenditionEntry(rendition))
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
return sort(renditionsMap)
|
|
1866
|
-
}
|
|
1867
|
-
|
|
1868
|
-
hideCropUI() {
|
|
1869
|
-
this._croptool.clear()
|
|
1870
|
-
// this._croptool.disable()
|
|
1871
|
-
}
|
|
1872
|
-
|
|
1873
|
-
showCropUI() {
|
|
1874
|
-
this._croptool.enable()
|
|
1875
|
-
const currRendition = this.getCurrentRendition()
|
|
1876
|
-
if (currRendition) {
|
|
1877
|
-
currRendition.current = false
|
|
1878
|
-
this.setCurrentRendition(currRendition)
|
|
1879
|
-
}
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
hideEditUI() {
|
|
1883
|
-
this.getElement()
|
|
1884
|
-
.querySelector('.ImageEditor-editableArea')
|
|
1885
|
-
.classList.add('is-hidden')
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
|
-
showEditUI() {
|
|
1889
|
-
this.getElement()
|
|
1890
|
-
.querySelector('.ImageEditor-editableArea')
|
|
1891
|
-
.classList.remove('is-hidden')
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
hideHotspotUI() {
|
|
1895
|
-
this.getElement()
|
|
1896
|
-
.querySelector('.ImageEditor-hotspot-container')
|
|
1897
|
-
.classList.add('is-hidden')
|
|
1898
|
-
}
|
|
1899
|
-
|
|
1900
|
-
showHotspotUI() {
|
|
1901
|
-
this.getElement()
|
|
1902
|
-
.querySelector('.ImageEditor-hotspot-container')
|
|
1903
|
-
.classList.remove('is-hidden')
|
|
1904
|
-
this._hotspotUI.reflow()
|
|
1905
|
-
}
|
|
1906
|
-
|
|
1907
|
-
renderPreview() {
|
|
1908
|
-
this.getPreviewContainer().classList.add('is-hidden')
|
|
1909
|
-
const lastCropData = this._croptool.getCropBoxData()
|
|
1910
|
-
this._croptool.clear()
|
|
1911
|
-
const uncroppedCanvas = this._croptool.getCroppedCanvas()
|
|
1912
|
-
this._croptool.setCropBoxData(lastCropData)
|
|
1913
|
-
const existingPreview = this.getPreviewCanvas()
|
|
1914
|
-
existingPreview
|
|
1915
|
-
? this.getPreviewContainer().replaceChild(
|
|
1916
|
-
uncroppedCanvas,
|
|
1917
|
-
existingPreview,
|
|
1918
|
-
)
|
|
1919
|
-
: this.getPreviewContainer().appendChild(uncroppedCanvas)
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
renderRenditionPreviews(renditions) {
|
|
1923
|
-
const renditionList = create('ol', {
|
|
1924
|
-
class: 'ImageEditor-sizeSelectors',
|
|
1925
|
-
})
|
|
1926
|
-
|
|
1927
|
-
const createLabel = (rendition) => {
|
|
1928
|
-
return (rendition.sizeInfos[rendition.name].displayName ??= '')
|
|
1929
|
-
}
|
|
1930
|
-
|
|
1931
|
-
const createDescription = (rendition) => {
|
|
1932
|
-
const elements = new Map()
|
|
1933
|
-
for (const key in rendition.sizeInfos) {
|
|
1934
|
-
const size = rendition.sizeInfos[key]
|
|
1935
|
-
let description = size.description
|
|
1936
|
-
if (description) {
|
|
1937
|
-
elements.set(description, create('span', description))
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
return [...elements.values()]
|
|
1941
|
-
}
|
|
1942
|
-
|
|
1943
|
-
const createRenditionPreview = (rendition) => {
|
|
1944
|
-
const croptool = this.getCropTool()
|
|
1945
|
-
|
|
1946
|
-
const preview = create('div', {
|
|
1947
|
-
class: 'imageEditor-sizePreview',
|
|
1948
|
-
})
|
|
1949
|
-
|
|
1950
|
-
this.positionCrop(rendition)
|
|
1951
|
-
|
|
1952
|
-
const canvas = croptool.getCroppedCanvas()
|
|
1953
|
-
preview.appendChild(canvas)
|
|
1954
|
-
return preview
|
|
1955
|
-
}
|
|
1956
|
-
|
|
1957
|
-
renditions.forEach((val, key) => {
|
|
1958
|
-
const cropGroupElement = create('li', {
|
|
1959
|
-
class: `imageEditor-sizeButton ${
|
|
1960
|
-
val.current ? 'imageEditor-sizeSelected' : ''
|
|
1961
|
-
}`,
|
|
1962
|
-
onclick: (event) => {
|
|
1963
|
-
event.preventDefault()
|
|
1964
|
-
event.stopPropagation()
|
|
1965
|
-
this.setCurrentRendition(val)
|
|
1966
|
-
},
|
|
1967
|
-
})
|
|
1968
|
-
|
|
1969
|
-
cropGroupElement.setAttribute('data-group-name', val.sizeInfos[key].group)
|
|
1970
|
-
cropGroupElement.setAttribute('data-size-name', key)
|
|
1971
|
-
|
|
1972
|
-
if (val.sizeInfos[key] && val.sizeInfos[key].independent) {
|
|
1973
|
-
cropGroupElement.setAttribute('data-independent', 'true')
|
|
1974
|
-
}
|
|
1975
|
-
|
|
1976
|
-
const sizeLabel = create(
|
|
1977
|
-
'div',
|
|
1978
|
-
{ className: 'imageEditor-sizeLabel' },
|
|
1979
|
-
createLabel(val),
|
|
1980
|
-
)
|
|
1981
|
-
|
|
1982
|
-
const groupLabel = create('div', {
|
|
1983
|
-
className: 'ImageEditor-sizeContainer',
|
|
1984
|
-
})
|
|
1985
|
-
|
|
1986
|
-
groupLabel.append(sizeLabel)
|
|
1987
|
-
|
|
1988
|
-
const groupDescription = create('div', {
|
|
1989
|
-
className: 'ImageEditor-sizeDescription',
|
|
1990
|
-
})
|
|
1991
|
-
|
|
1992
|
-
groupDescription.append(...createDescription(val))
|
|
1993
|
-
|
|
1994
|
-
const inheritLabel = this.getElement().getAttribute('data-inherit')
|
|
1995
|
-
const overrideLabel = this.getElement().getAttribute('data-override')
|
|
1996
|
-
|
|
1997
|
-
if (val.cropRatio && val.cropRatio !== 'independent') {
|
|
1998
|
-
groupDescription.append(
|
|
1999
|
-
create(
|
|
2000
|
-
'span',
|
|
2001
|
-
{ className: 'is-inherit' },
|
|
2002
|
-
inheritLabel + ' ' + val.cropRatio,
|
|
2003
|
-
),
|
|
2004
|
-
)
|
|
2005
|
-
groupDescription.append(
|
|
2006
|
-
create(
|
|
2007
|
-
'span',
|
|
2008
|
-
{ className: 'is-override' },
|
|
2009
|
-
overrideLabel + ' ' + val.cropRatio,
|
|
2010
|
-
),
|
|
2011
|
-
)
|
|
2012
|
-
}
|
|
2013
|
-
|
|
2014
|
-
const resetButton = create(
|
|
2015
|
-
'a',
|
|
2016
|
-
{
|
|
2017
|
-
class: 'ImageEditor-renditionResetButton',
|
|
2018
|
-
onclick: (event) => {
|
|
2019
|
-
event.preventDefault()
|
|
2020
|
-
event.stopPropagation()
|
|
2021
|
-
if (!val.current) {
|
|
2022
|
-
this.setCurrentRendition(val)
|
|
2023
|
-
}
|
|
2024
|
-
this.resetCropInputValues(this.getCurrentRendition())
|
|
2025
|
-
this.resetRendition(this.getCurrentRendition())
|
|
2026
|
-
},
|
|
2027
|
-
},
|
|
2028
|
-
'Reset',
|
|
2029
|
-
)
|
|
2030
|
-
|
|
2031
|
-
cropGroupElement.append(
|
|
2032
|
-
groupLabel,
|
|
2033
|
-
groupDescription,
|
|
2034
|
-
createRenditionPreview(val),
|
|
2035
|
-
)
|
|
2036
|
-
groupLabel.append(resetButton)
|
|
2037
|
-
renditionList.appendChild(cropGroupElement)
|
|
2038
|
-
})
|
|
2039
|
-
|
|
2040
|
-
const imageSizes = this.getElement().querySelector('.ImageEditor-sizes')
|
|
2041
|
-
if (imageSizes) {
|
|
2042
|
-
imageSizes.appendChild(renditionList)
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
|
|
2046
|
-
renderTabs() {
|
|
2047
|
-
const target = this.getElement().querySelector('.imageEditor-aside')
|
|
2048
|
-
const previewImage = this.getPreviewContainer().querySelector('img')
|
|
2049
|
-
|
|
2050
|
-
const editTab = create('div', {
|
|
2051
|
-
class: 'ImageEditor-editGroup',
|
|
2052
|
-
dataset: { tab: 'Edit' },
|
|
2053
|
-
})
|
|
2054
|
-
if (this.getElement().getAttribute('editor') === 'true') {
|
|
2055
|
-
target.appendChild(editTab)
|
|
2056
|
-
}
|
|
2057
|
-
|
|
2058
|
-
const sizesTab = this.getElement().querySelector('.imageEditor-sizes')
|
|
2059
|
-
if (sizesTab) {
|
|
2060
|
-
sizesTab.dataset.tab = 'Sizes'
|
|
2061
|
-
target.appendChild(sizesTab)
|
|
2062
|
-
}
|
|
2063
|
-
|
|
2064
|
-
if (this.getElement().getAttribute('editor') === 'true') {
|
|
2065
|
-
this._editUI = this.createEditUI(
|
|
2066
|
-
target,
|
|
2067
|
-
editTab,
|
|
2068
|
-
this._croptool,
|
|
2069
|
-
this._focusUI,
|
|
2070
|
-
() => {
|
|
2071
|
-
this.updateAllRenditionPreviews()
|
|
2072
|
-
},
|
|
2073
|
-
() => {
|
|
2074
|
-
this.repaint(document.body.offsetHeight)
|
|
2075
|
-
},
|
|
2076
|
-
)
|
|
2077
|
-
} else {
|
|
2078
|
-
this.applyAllEdits(this.getEditValues())
|
|
2079
|
-
}
|
|
2080
|
-
|
|
2081
|
-
const hotspottingEnabled = !!this.getForm().querySelector(
|
|
2082
|
-
'.inputContainer[data-name$=".hotspots"]',
|
|
2083
|
-
)
|
|
2084
|
-
let hotspotTab
|
|
2085
|
-
|
|
2086
|
-
if (hotspottingEnabled) {
|
|
2087
|
-
hotspotTab = create('div', {
|
|
2088
|
-
class: 'ImageEditor-hotspots',
|
|
2089
|
-
dataset: { tab: 'Hotspots' },
|
|
2090
|
-
})
|
|
2091
|
-
target.appendChild(hotspotTab)
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
this._hotspotUI = hotspottingEnabled
|
|
2095
|
-
? this.createHotspotUI(
|
|
2096
|
-
hotspotTab,
|
|
2097
|
-
previewImage.getAttribute('data-width'),
|
|
2098
|
-
previewImage.getAttribute('data-height'),
|
|
2099
|
-
() => {},
|
|
2100
|
-
)
|
|
2101
|
-
: null
|
|
2102
|
-
|
|
2103
|
-
const mutations = new MutationObserver((mutations) => {
|
|
2104
|
-
for (const mutation of mutations) {
|
|
2105
|
-
if (mutation.attributeName === 'class') {
|
|
2106
|
-
const element = mutation.target
|
|
2107
|
-
const tab = element.dataset.tab
|
|
2108
|
-
if (element.classList.contains('TC-unselected')) {
|
|
2109
|
-
// the previous tab
|
|
2110
|
-
if (tab === 'Sizes') {
|
|
2111
|
-
this.hideCropUI()
|
|
2112
|
-
} else if (tab === 'Edit') {
|
|
2113
|
-
this.hideEditUI()
|
|
2114
|
-
} else if (tab === 'Hotspots') {
|
|
2115
|
-
this.hideHotspotUI()
|
|
2116
|
-
}
|
|
2117
|
-
} else {
|
|
2118
|
-
if (tab === 'Sizes') {
|
|
2119
|
-
this.showCropUI()
|
|
2120
|
-
} else if (tab === 'Edit') {
|
|
2121
|
-
this.showEditUI()
|
|
2122
|
-
} else if (tab === 'Hotspots') {
|
|
2123
|
-
this.showHotspotUI()
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
}
|
|
2128
|
-
})
|
|
2129
|
-
|
|
2130
|
-
mutations.observe(editTab, {
|
|
2131
|
-
attributes: true,
|
|
2132
|
-
})
|
|
2133
|
-
if (sizesTab) {
|
|
2134
|
-
mutations.observe(sizesTab, {
|
|
2135
|
-
attributes: true,
|
|
2136
|
-
})
|
|
2137
|
-
}
|
|
2138
|
-
if (hotspotTab) {
|
|
2139
|
-
mutations.observe(hotspotTab, {
|
|
2140
|
-
attributes: true,
|
|
2141
|
-
})
|
|
2142
|
-
}
|
|
2143
|
-
|
|
2144
|
-
new TabContainer(target) // eslint-disable-line no-new
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
updateRenditionPreview(rendition, canvas) {
|
|
2148
|
-
const renditionEl = this.getRenditionElFromRendition(rendition)
|
|
2149
|
-
const oldPreview = renditionEl.querySelector(
|
|
2150
|
-
'.imageEditor-sizePreview canvas',
|
|
2151
|
-
)
|
|
2152
|
-
const parent = renditionEl.querySelector('.imageEditor-sizePreview')
|
|
2153
|
-
parent.replaceChild(canvas, oldPreview)
|
|
2154
|
-
}
|
|
2155
|
-
|
|
2156
|
-
// @focusPointData should be decimal values for {x, y}
|
|
2157
|
-
updateAllRenditionPreviews() {
|
|
2158
|
-
this._renditions.forEach((rendition) => {
|
|
2159
|
-
this.positionCrop(rendition)
|
|
2160
|
-
const canvas = this._croptool.getCroppedCanvas()
|
|
2161
|
-
this._croptool.clear()
|
|
2162
|
-
this.updateRenditionPreview(rendition, canvas)
|
|
2163
|
-
})
|
|
2164
|
-
}
|
|
2165
|
-
|
|
2166
|
-
// Converts pixel values to percentages before updating the input fields.
|
|
2167
|
-
setCropInputValues(rendition, data = null) {
|
|
2168
|
-
const canvasData = this._croptool.getCanvasData()
|
|
2169
|
-
let cropData = data || this._croptool.getCropBoxData()
|
|
2170
|
-
let { left, top, width, height } = cropData
|
|
2171
|
-
|
|
2172
|
-
left = left !== 0 ? (left - canvasData.left) / canvasData.width : 0
|
|
2173
|
-
top = top !== 0 ? (top - canvasData.top) / canvasData.height : 0
|
|
2174
|
-
width = width / canvasData.width
|
|
2175
|
-
height = height / canvasData.height
|
|
2176
|
-
|
|
2177
|
-
cropData = { x: left, y: top, width, height }
|
|
2178
|
-
|
|
2179
|
-
for (const key in rendition.sizeInfos) {
|
|
2180
|
-
for (const prop in cropData) {
|
|
2181
|
-
const input = rendition.sizeInfos[key].inputs[prop]
|
|
2182
|
-
input.value = cropData[prop]
|
|
2183
|
-
input.removeAttribute('disabled')
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2188
|
-
getCropInputValues(rendition) {
|
|
2189
|
-
const props = ['x', 'y', 'width', 'height']
|
|
2190
|
-
let data = {}
|
|
2191
|
-
|
|
2192
|
-
for (const key in rendition.sizeInfos) {
|
|
2193
|
-
props.forEach((prop) => {
|
|
2194
|
-
Object.assign(data, {
|
|
2195
|
-
[prop]: rendition.sizeInfos[key].inputs[prop].value,
|
|
2196
|
-
})
|
|
2197
|
-
})
|
|
2198
|
-
}
|
|
2199
|
-
return data
|
|
2200
|
-
}
|
|
2201
|
-
|
|
2202
|
-
resetCropInputValues(rendition) {
|
|
2203
|
-
this.setCropInputValues(rendition, {
|
|
2204
|
-
left: 0.0,
|
|
2205
|
-
top: 0.0,
|
|
2206
|
-
width: 0.0,
|
|
2207
|
-
height: 0.0,
|
|
2208
|
-
})
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
resetRendition(rendition) {
|
|
2212
|
-
delete rendition.modifiedCropData
|
|
2213
|
-
const renditionEl = this.getRenditionElFromRendition(rendition)
|
|
2214
|
-
renditionEl.classList.remove('ImageEditor-sizeModified')
|
|
2215
|
-
this.hideRenditionResetButton(rendition)
|
|
2216
|
-
this.applyRenditionCrop(rendition)
|
|
2217
|
-
}
|
|
2218
|
-
|
|
2219
|
-
resetAllRenditionPreviews() {
|
|
2220
|
-
if (this.getCurrentRendition()) {
|
|
2221
|
-
this.unsetCurrentRendition(this.getCurrentRendition())
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
|
-
this._renditions.forEach((rendition) => {
|
|
2225
|
-
this.resetCropInputValues(rendition)
|
|
2226
|
-
delete rendition.modifiedCropData
|
|
2227
|
-
const renditionEl = this.getRenditionElFromRendition(rendition)
|
|
2228
|
-
renditionEl.classList.remove('ImageEditor-sizeModified')
|
|
2229
|
-
})
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
showRenditionResetButton(rendition) {
|
|
2233
|
-
this.getRenditionElFromRendition(rendition)
|
|
2234
|
-
?.querySelector(':scope .ImageEditor-renditionResetButton')
|
|
2235
|
-
?.classList.add('is-visible')
|
|
2236
|
-
}
|
|
2237
|
-
|
|
2238
|
-
hideRenditionResetButton(rendition) {
|
|
2239
|
-
this.getRenditionElFromRendition(rendition)
|
|
2240
|
-
?.querySelector(':scope .ImageEditor-renditionResetButton')
|
|
2241
|
-
?.classList.remove('is-visible')
|
|
2242
|
-
}
|
|
2243
|
-
|
|
2244
|
-
getRenditionElFromRendition(rendition) {
|
|
2245
|
-
const name = rendition.name
|
|
2246
|
-
return this.getElement().querySelector(
|
|
2247
|
-
`.imageEditor-sizeButton[data-size-name='${name}']`,
|
|
2248
|
-
)
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
getIsCropCentered() {
|
|
2252
|
-
return (
|
|
2253
|
-
this.getElement()
|
|
2254
|
-
.querySelector('.imageEditor-sizes table')
|
|
2255
|
-
.getAttribute('data-crop-center') === 'true'
|
|
2256
|
-
)
|
|
2257
|
-
}
|
|
2258
|
-
|
|
2259
|
-
getCurrentRendition() {
|
|
2260
|
-
let current = null
|
|
2261
|
-
this._renditions.forEach((rendition) => {
|
|
2262
|
-
if (rendition.current) {
|
|
2263
|
-
current = rendition
|
|
2264
|
-
}
|
|
2265
|
-
})
|
|
2266
|
-
return current
|
|
2267
|
-
}
|
|
2268
|
-
|
|
2269
|
-
toggleRendition(rendition) {
|
|
2270
|
-
this.unsetCurrentRendition(rendition)
|
|
2271
|
-
this._focusUI && this._focusUI.enable()
|
|
2272
|
-
|
|
2273
|
-
this.positionCrop(rendition)
|
|
2274
|
-
|
|
2275
|
-
const canvas = this._croptool.getCroppedCanvas()
|
|
2276
|
-
this._croptool.clear()
|
|
2277
|
-
this.updateRenditionPreview(rendition, canvas)
|
|
2278
|
-
}
|
|
2279
|
-
|
|
2280
|
-
unsetCurrentRendition(rendition) {
|
|
2281
|
-
const renditionEl = this.getRenditionElFromRendition(rendition)
|
|
2282
|
-
renditionEl.classList.remove('imageEditor-sizeSelected')
|
|
2283
|
-
rendition.current = false
|
|
2284
|
-
}
|
|
2285
|
-
|
|
2286
|
-
setCurrentRendition(rendition) {
|
|
2287
|
-
// is this rendition already the current?
|
|
2288
|
-
if (rendition.current) {
|
|
2289
|
-
this.toggleRendition(rendition)
|
|
2290
|
-
return
|
|
2291
|
-
}
|
|
2292
|
-
|
|
2293
|
-
// deselect current
|
|
2294
|
-
this._renditions.forEach((rendition) => {
|
|
2295
|
-
if (rendition.current) {
|
|
2296
|
-
this.unsetCurrentRendition(rendition)
|
|
2297
|
-
this.toggleRendition(rendition)
|
|
2298
|
-
}
|
|
2299
|
-
})
|
|
2300
|
-
|
|
2301
|
-
rendition.current = true
|
|
2302
|
-
this.applyRenditionCrop(rendition)
|
|
2303
|
-
}
|
|
2304
|
-
|
|
2305
|
-
applyRenditionCrop(rendition) {
|
|
2306
|
-
const renditionEl = this.getRenditionElFromRendition(rendition)
|
|
2307
|
-
|
|
2308
|
-
if (renditionEl) {
|
|
2309
|
-
renditionEl.classList.add('imageEditor-sizeSelected')
|
|
2310
|
-
|
|
2311
|
-
this.positionCrop(rendition)
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
|
-
|
|
2315
|
-
positionCrop(rendition) {
|
|
2316
|
-
let ratio
|
|
2317
|
-
let focusPointData
|
|
2318
|
-
let cropData = this.getCropInputValues(rendition)
|
|
2319
|
-
const imageData = this._croptool.getCanvasData()
|
|
2320
|
-
|
|
2321
|
-
if (this._focusUI) {
|
|
2322
|
-
focusPointData = this._focusUI.getFocusCoordinatesRelative(
|
|
2323
|
-
rendition.cropRatio,
|
|
2324
|
-
)
|
|
2325
|
-
}
|
|
2326
|
-
|
|
2327
|
-
for (const key in rendition.sizeInfos) {
|
|
2328
|
-
ratio = rendition.sizeInfos[key].aspectRatio
|
|
2329
|
-
}
|
|
2330
|
-
|
|
2331
|
-
this._croptool.crop()
|
|
2332
|
-
this._croptool.setAspectRatio(ratio)
|
|
2333
|
-
|
|
2334
|
-
// Crop is positioned in upper-left by default.
|
|
2335
|
-
if (!this.getIsCropCentered()) {
|
|
2336
|
-
this.getCropTool().setCropBoxData({
|
|
2337
|
-
left: 0,
|
|
2338
|
-
top: 0,
|
|
2339
|
-
})
|
|
2340
|
-
}
|
|
2341
|
-
|
|
2342
|
-
// Was the crop box adjusted by the User?
|
|
2343
|
-
if (parseFloat(cropData.width) && parseFloat(cropData.height)) {
|
|
2344
|
-
// display as a modified crop w/ reset functionality.
|
|
2345
|
-
const renditionEl = this.getRenditionElFromRendition(rendition)
|
|
2346
|
-
if (renditionEl) {
|
|
2347
|
-
this.showRenditionResetButton(rendition)
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2350
|
-
const left = imageData.width * cropData.x + imageData.left
|
|
2351
|
-
const top = imageData.height * cropData.y + imageData.top
|
|
2352
|
-
const width = imageData.width * cropData.width
|
|
2353
|
-
const height = imageData.height * cropData.height
|
|
2354
|
-
this._croptool.setCropBoxData({
|
|
2355
|
-
left,
|
|
2356
|
-
top,
|
|
2357
|
-
width,
|
|
2358
|
-
height,
|
|
2359
|
-
})
|
|
2360
|
-
} else {
|
|
2361
|
-
// if a focus point has been set, we need to adjust the crop accordingly
|
|
2362
|
-
if (focusPointData) {
|
|
2363
|
-
const canvasData = this._croptool.getCanvasData()
|
|
2364
|
-
const cropboxData = this._croptool.getCropBoxData()
|
|
2365
|
-
const cropboxDataRelativeToFocus = {
|
|
2366
|
-
left:
|
|
2367
|
-
canvasData.width * focusPointData.x -
|
|
2368
|
-
cropboxData.width / 2 +
|
|
2369
|
-
canvasData.left,
|
|
2370
|
-
top:
|
|
2371
|
-
canvasData.height * focusPointData.y -
|
|
2372
|
-
cropboxData.height / 2 +
|
|
2373
|
-
canvasData.top,
|
|
2374
|
-
width: cropboxData.width,
|
|
2375
|
-
height: cropboxData.height,
|
|
2376
|
-
}
|
|
2377
|
-
|
|
2378
|
-
this._croptool.setCropBoxData(cropboxDataRelativeToFocus)
|
|
2379
|
-
|
|
2380
|
-
for (const key in rendition.sizeInfos) {
|
|
2381
|
-
rendition.sizeInfos[key].focusCrop = cropboxDataRelativeToFocus
|
|
2382
|
-
}
|
|
2383
|
-
}
|
|
2384
|
-
}
|
|
2385
|
-
}
|
|
2386
|
-
}
|
|
2387
|
-
|
|
2388
|
-
onFind('.imageEditor', (target) => {
|
|
2389
|
-
new ImageEditor(target)
|
|
2390
|
-
})
|
|
2391
|
-
|
|
2392
|
-
// Keeps focus within image editor when tabbing
|
|
2393
|
-
onFind('.ImageEditor .TabBar-item', (tab) => {
|
|
2394
|
-
tab.addEventListener('keydown', (event) => {
|
|
2395
|
-
if (event.key === 'Tab' && event.shiftKey) {
|
|
2396
|
-
event.preventDefault()
|
|
2397
|
-
tab
|
|
2398
|
-
.closest('.ImageEditor')
|
|
2399
|
-
?.querySelector('.ImageEditor-resetButton')
|
|
2400
|
-
?.focus()
|
|
2401
|
-
}
|
|
2402
|
-
})
|
|
2403
|
-
})
|