From fd97cc97728917f6914a2e819018d8705cf5960b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20Markovi=C4=87?= Date: Sun, 7 Jul 2024 01:00:30 +0200 Subject: [PATCH] Init --- Elementor Pro 3.21.2 | 1 + assets/css/admin-rtl.css | 610 + assets/css/admin-rtl.min.css | 2 + assets/css/admin.css | 610 + assets/css/admin.min.css | 2 + assets/css/app-rtl.css | 359 + assets/css/app-rtl.min.css | 2 + assets/css/app.css | 359 + assets/css/app.min.css | 2 + assets/css/editor-rtl.css | 591 + assets/css/editor-rtl.min.css | 2 + assets/css/editor.css | 590 + assets/css/editor.min.css | 2 + assets/css/frontend-lite-rtl.css | 272 + assets/css/frontend-lite-rtl.min.css | 2 + assets/css/frontend-lite.css | 272 + assets/css/frontend-lite.min.css | 2 + assets/css/frontend-msie.css | 11176 ++ assets/css/frontend-msie.min.css | 2 + assets/css/frontend-rtl.css | 11053 ++ assets/css/frontend-rtl.min.css | 2 + assets/css/frontend.css | 11051 ++ assets/css/frontend.min.css | 2 + assets/css/loop-grid-cta.css | 164 + assets/css/loop-grid-cta.min.css | 2 + assets/css/modules/code-highlight.css | 811 + assets/css/modules/code-highlight.min.css | 1 + assets/css/modules/custom-code-rtl.css | 1776 + assets/css/modules/custom-code-rtl.min.css | 1 + assets/css/modules/custom-code.css | 1785 + assets/css/modules/custom-code.min.css | 1 + .../modules/forms/submissions/admin-rtl.css | 167 + .../forms/submissions/admin-rtl.min.css | 1 + .../css/modules/forms/submissions/admin.css | 167 + .../modules/forms/submissions/admin.min.css | 1 + assets/css/modules/notes/frontend.css | 24 + assets/css/modules/notes/frontend.min.css | 1 + assets/css/preview-rtl.css | 208 + assets/css/preview-rtl.min.css | 2 + assets/css/preview.css | 208 + assets/css/preview.min.css | 2 + assets/css/templates/frontend-lite-rtl.css | 270 + .../css/templates/frontend-lite-rtl.min.css | 1 + assets/css/templates/frontend-lite.css | 270 + assets/css/templates/frontend-lite.min.css | 1 + assets/css/templates/frontend-rtl.css | 11018 ++ assets/css/templates/frontend-rtl.min.css | 1 + assets/css/templates/frontend.css | 11016 ++ assets/css/templates/frontend.min.css | 1 + .../widget-call-to-action-rtl.min.css | 1 + .../templates/widget-call-to-action.min.css | 1 + .../css/templates/widget-flip-box-rtl.min.css | 1 + assets/css/templates/widget-flip-box.min.css | 1 + .../templates/widget-mega-menu-rtl.min.css | 1 + assets/css/templates/widget-mega-menu.min.css | 1 + .../css/templates/widget-nav-menu-rtl.min.css | 1 + assets/css/templates/widget-nav-menu.min.css | 1 + .../css/templates/widget-slides-rtl.min.css | 1 + assets/css/templates/widget-slides.min.css | 1 + .../widget-video-playlist-rtl.min.css | 1 + .../templates/widget-video-playlist.min.css | 1 + .../css/widget-animated-headline-rtl.min.css | 2 + assets/css/widget-animated-headline.min.css | 2 + assets/css/widget-blockquote-rtl.min.css | 2 + assets/css/widget-blockquote.min.css | 2 + assets/css/widget-call-to-action-rtl.min.css | 2 + assets/css/widget-call-to-action.min.css | 2 + assets/css/widget-carousel-rtl.min.css | 2 + assets/css/widget-carousel.min.css | 2 + assets/css/widget-countdown-rtl.min.css | 2 + assets/css/widget-countdown.min.css | 2 + assets/css/widget-flip-box-rtl.min.css | 2 + assets/css/widget-flip-box.min.css | 2 + assets/css/widget-forms-rtl.min.css | 2 + assets/css/widget-forms.min.css | 2 + assets/css/widget-gallery-rtl.min.css | 2 + assets/css/widget-gallery.min.css | 2 + assets/css/widget-global-widget-rtl.min.css | 2 + assets/css/widget-global-widget.min.css | 2 + assets/css/widget-hotspot-rtl.min.css | 2 + assets/css/widget-hotspot.min.css | 2 + assets/css/widget-loop-builder-rtl.min.css | 2 + assets/css/widget-loop-builder.min.css | 2 + assets/css/widget-loop-filter-rtl.min.css | 2 + assets/css/widget-loop-filter.min.css | 2 + assets/css/widget-lottie-rtl.min.css | 2 + assets/css/widget-lottie.min.css | 2 + assets/css/widget-mega-menu-rtl.min.css | 2 + assets/css/widget-mega-menu.min.css | 2 + assets/css/widget-motion-fx-rtl.min.css | 2 + assets/css/widget-motion-fx.min.css | 2 + assets/css/widget-nav-menu-rtl.min.css | 2 + assets/css/widget-nav-menu.min.css | 2 + assets/css/widget-nested-carousel-rtl.min.css | 2 + assets/css/widget-nested-carousel.min.css | 2 + assets/css/widget-notes-rtl.min.css | 2 + assets/css/widget-notes.min.css | 2 + assets/css/widget-payments-rtl.min.css | 2 + assets/css/widget-payments.min.css | 2 + assets/css/widget-popup-rtl.min.css | 2 + assets/css/widget-popup.min.css | 2 + assets/css/widget-posts-rtl.min.css | 2 + assets/css/widget-posts.min.css | 2 + assets/css/widget-pricing-rtl.min.css | 2 + assets/css/widget-pricing.min.css | 2 + .../css/widget-progress-tracker-rtl.min.css | 2 + assets/css/widget-progress-tracker.min.css | 2 + assets/css/widget-share-buttons-rtl.min.css | 2 + assets/css/widget-share-buttons.min.css | 2 + assets/css/widget-slides-rtl.min.css | 2 + assets/css/widget-slides.min.css | 2 + assets/css/widget-social-rtl.min.css | 2 + assets/css/widget-social.min.css | 2 + assets/css/widget-sticky-rtl.min.css | 2 + assets/css/widget-sticky.min.css | 2 + .../css/widget-table-of-contents-rtl.min.css | 2 + assets/css/widget-table-of-contents.min.css | 2 + assets/css/widget-theme-builder-rtl.min.css | 2 + assets/css/widget-theme-builder.min.css | 2 + assets/css/widget-theme-elements-rtl.min.css | 2 + assets/css/widget-theme-elements.min.css | 2 + assets/css/widget-video-playlist-rtl.min.css | 2 + assets/css/widget-video-playlist.min.css | 2 + assets/css/widget-woocommerce-rtl.min.css | 2 + assets/css/widget-woocommerce.min.css | 2 + assets/css/woocommerce-notices.css | 315 + assets/css/woocommerce-notices.min.css | 2 + assets/data/responsive-widgets.json | 1 + .../images/announcements/license-expired.png | Bin 0 -> 15106 bytes assets/images/logo-placeholder.png | Bin 0 -> 4059 bytes assets/js/1b816ba777b14157325b.bundle.min.js | 2 + assets/js/4abfbfd970d6f7680bc7.bundle.js | 166 + assets/js/60745ddf42fde6647dbc.bundle.min.js | 3 + ...5ddf42fde6647dbc.bundle.min.js.LICENSE.txt | 1 + assets/js/98217e0c00e1f53421ef.bundle.js | 1647 + assets/js/admin.js | 1666 + assets/js/admin.min.js | 2 + ...ination.a8dae0f5699fe9733e7d.bundle.min.js | 2 + ...-pagination.bc400e6cb24a14a2ea97.bundle.js | 146 + ...eadline.3efc6517c2a055f6c242.bundle.min.js | 2 + ...ed-headline.e4c2ed3934d0df18c40a.bundle.js | 311 + assets/js/app.js | 7882 ++ assets/js/app.min.js | 2 + ...chive-posts.0aae8c3bd7d196797b6c.bundle.js | 401 + ...e-posts.d30c917134774f65dd6d.bundle.min.js | 2 + .../carousel.998a291abf70435fd698.bundle.js | 294 + ...arousel.9b02b45d7826c1c48f33.bundle.min.js | 2 + ...ghlight.28a979661569ddbbf60d.bundle.min.js | 2 + ...e-highlight.8b676d9a001f56fb77fa.bundle.js | 32 + .../countdown.60cf02eaf22d71d83f3d.bundle.js | 148 + ...untdown.be941c879efa861dbbfa.bundle.min.js | 2 + assets/js/custom-code.js | 2648 + assets/js/custom-code.min.js | 2 + assets/js/display-conditions.js | 5508 + assets/js/display-conditions.min.js | 2 + assets/js/editor.js | 9744 ++ assets/js/editor.min.js | 2 + assets/js/elements-handlers.js | 2475 + assets/js/elements-handlers.min.js | 2 + assets/js/form-submission-admin.js | 7131 ++ assets/js/form-submission-admin.min.js | 2 + .../form.10bf1a6475f0741920ff.bundle.min.js | 2 + assets/js/form.efd3434e4ecbe4dd5fc6.bundle.js | 945 + assets/js/frontend.js | 1514 + assets/js/frontend.min.js | 2 + .../js/gallery.805130d33e18cb04635f.bundle.js | 229 + ...gallery.8ca9a354ce039d1ba641.bundle.min.js | 2 + assets/js/gutenberg-woocommerce-notice.js | 1989 + assets/js/gutenberg-woocommerce-notice.min.js | 2 + ...hotspot.6ab1751404c381bfe390.bundle.min.js | 2 + .../js/hotspot.70886883c622dd8d5eb2.bundle.js | 121 + ....vendor.99a5b769619f50a6cb60.bundle.min.js | 3 + ...b769619f50a6cb60.bundle.min.js.LICENSE.txt | 11 + ...szip.vendor.a3c65615c1de5560962d.bundle.js | 27 + .../load-more.ad89e46f2f6bfd9c27e8.bundle.js | 225 + ...ad-more.bc9573b5d1f73abd80b9.bundle.min.js | 2 + ...arousel.4e8fd6593adbba21698e.bundle.min.js | 2 + ...op-carousel.827a11bd7f1b0343de42.bundle.js | 66 + ...-editor.21982d6e76a4fba12cd5.bundle.min.js | 2 + ...lter-editor.d1bae86a5ed21c0e9981.bundle.js | 191 + assets/js/loop.27d8ba43536f8b76ca41.bundle.js | 456 + .../loop.4f538ab2476dd2d124e6.bundle.min.js | 2 + .../lottie.565b778d23c04461c4ea.bundle.min.js | 2 + .../js/lottie.a00fda0bbf10f9b99eae.bundle.js | 662 + ...arousel.aca2224ef13e6f999011.bundle.min.js | 2 + ...ia-carousel.d8417210e0b731dd32b8.bundle.js | 386 + ...-editor.319a739ef1260a2b2da1.bundle.min.js | 2 + ...menu-editor.bbef3f7412481cbce555.bundle.js | 153 + ...tch-content.4648b25d00c1f94cec4e.bundle.js | 51 + ...content.60ca9e1e97c52ac3bf8c.bundle.min.js | 2 + .../mega-menu.5eea2dda8d43e4430297.bundle.js | 957 + ...ga-menu.611dbb6e55a2c14924ad.bundle.min.js | 2 + ...ard-handler.255f6b16a2f292e5c260.bundle.js | 280 + ...handler.80c53fcbf2fdb487c91d.bundle.min.js | 2 + .../nav-menu.ad2c1632628f619ad9e9.bundle.js | 224 + ...av-menu.d43af66e5000fd109c04.bundle.min.js | 2 + ...usel-editor.2fdc278ce6bc9f6ec2e0.bundle.js | 51 + ...-editor.6337dab68af203be7c04.bundle.min.js | 2 + ...arousel.21c7f0c4423917225bce.bundle.min.js | 2 + ...ed-carousel.a6b8a103e170cb2de9a4.bundle.js | 174 + assets/js/notes/93.min.js | 2 + assets/js/notes/93.min.js.LICENSE.txt | 27 + assets/js/notes/notes-app-initiator.js | 375 + assets/js/notes/notes-app-initiator.min.js | 1 + assets/js/notes/notes-app.js | 8358 ++ assets/js/notes/notes-app.min.js | 1638 + assets/js/notes/notes.js | 4768 + assets/js/notes/notes.min.js | 2 + assets/js/notes/notes.min.js.LICENSE.txt | 17 + ...odule_js-node_modules_radix-ui_r-e4587e.js | 16664 +++ .../editor-documents-extended.asset.php | 20 + .../editor-documents-extended.js | 349 + .../editor-documents-extended.min.js | 1 + .../editor-documents-extended.strings.js | 3 + .../editor-notes/editor-notes.asset.php | 16 + .../js/packages/editor-notes/editor-notes.js | 130 + .../packages/editor-notes/editor-notes.min.js | 1 + .../editor-notes/editor-notes.strings.js | 1 + .../editor-site-navigation-extended.asset.php | 14 + .../editor-site-navigation-extended.js | 104 + .../editor-site-navigation-extended.min.js | 1 + ...editor-site-navigation-extended.strings.js | 0 assets/js/packages/ui/ui.asset.php | 14 + assets/js/packages/ui/ui.js | 87837 ++++++++++++++++ assets/js/packages/ui/ui.min.js | 205 + assets/js/packages/ui/ui.min.js.LICENSE.txt | 48 + assets/js/packages/ui/ui.strings.js | 0 assets/js/page-transitions.js | 769 + assets/js/page-transitions.min.js | 2 + ...ypal-button.3028ea98fc2e17fdfe8f.bundle.js | 51 + ...-button.3d0d5af7df85963df32c.bundle.min.js | 2 + .../popup.085c1727e36940b18f29.bundle.min.js | 2 + .../js/popup.1f90f6cfd0d44ef28772.bundle.js | 51 + .../portfolio.9a52c1f0953359d74119.bundle.js | 358 + ...rtfolio.b5c5e89624dc6b81a11a.bundle.min.js | 2 + .../js/posts.5d2d70b1d6918b6d8205.bundle.js | 170 + .../posts.caaf3e27e57db8207afc.bundle.min.js | 2 + assets/js/preview.js | 2074 + assets/js/preview.min.js | 2 + ...to-cart.023d7d31fbf96c3dbdfc.bundle.min.js | 2 + ...add-to-cart.e099bc90899376d00959.bundle.js | 196 + ...ess-tracker.3ec316715116e9087057.bundle.js | 202 + ...tracker.53951a08af7543da98e6.bundle.min.js | 2 + assets/js/qunit-tests.js | 89 + assets/js/qunit-tests.min.js | 2 + assets/js/screenshot.js | 387 + assets/js/screenshot.min.js | 2 + ...search-form.4beabae7f0e0a3129ef7.bundle.js | 121 + ...ch-form.a25a87283d08dad12f18.bundle.min.js | 2 + ...buttons.08f4daf4a4285a8632b8.bundle.min.js | 2 + ...are-buttons.58e0fcb000aa02df3f24.bundle.js | 118 + .../js/slides.3b185c687f9167dfae0c.bundle.js | 223 + .../slides.fb6b9afd278bb9c5e75b.bundle.min.js | 2 + .../social.2d2e44e8608690943f29.bundle.min.js | 2 + .../js/social.deeefd0e3641200f8239.bundle.js | 68 + ...-button.2acbca466dfeb9585680.bundle.min.js | 2 + ...ripe-button.b00915f9aec396f7b070.bundle.js | 121 + ...ontents.82ad797536446d523057.bundle.min.js | 2 + ...of-contents.e67f9eaf44032e14dc57.bundle.js | 414 + ...nomy-filter.9d41aac2f76c01cfdb42.bundle.js | 416 + ...-filter.b42e9c10a9d0abc3454e.bundle.min.js | 2 + ...laylist.74fca1f2470fa6474595.bundle.min.js | 2 + ...eo-playlist.964a12bbea2078517f07.bundle.js | 1317 + assets/js/webpack-pro.runtime.js | 310 + assets/js/webpack-pro.runtime.min.js | 2 + ...mmerce-cart.73c6990b0b1a1ea18220.bundle.js | 283 + ...ce-cart.fc30c6cb753d4098eff5.bundle.min.js | 2 + ...eckout-page.9b1242f2568f94bb8d5c.bundle.js | 327 + ...ut-page.b18af78282979b6f74e4.bundle.min.js | 2 + ...e-menu-cart.010ec7298aee1fcdc2ea.bundle.js | 236 + ...nu-cart.faa7b80e9ba9e5072070.bundle.min.js | 2 + ...account.3ee10d01e625dad87f73.bundle.min.js | 2 + ...-my-account.6509f179e93231fa2b6a.bundle.js | 291 + ...notices.aaa7a3d06f24f7ea6951.bundle.min.js | 2 + ...rce-notices.d8c0850de1984ac89f33.bundle.js | 76 + ...summary.46445ab1120a8c28c05c.bundle.min.js | 2 + ...ase-summary.8d56a92f38ab4fc4575f.bundle.js | 202 + assets/lib/dom-to-image/js/dom-to-image.js | 777 + .../lib/dom-to-image/js/dom-to-image.min.js | 9 + assets/lib/font-awesome-pro/brands.js | 467 + assets/lib/font-awesome-pro/duotone.js | 1857 + assets/lib/font-awesome-pro/light.js | 1857 + assets/lib/font-awesome-pro/regular.js | 1857 + assets/lib/font-awesome-pro/solid.js | 1857 + assets/lib/html2canvas/js/html2canvas.js | 7033 ++ assets/lib/html2canvas/js/html2canvas.min.js | 20 + assets/lib/instant-page/instant-page.js | 232 + assets/lib/instant-page/instant-page.min.js | 2 + assets/lib/lottie/lottie.js | 14561 +++ assets/lib/lottie/lottie.min.js | 19 + assets/lib/smartmenus/jquery.smartmenus.js | 1225 + .../lib/smartmenus/jquery.smartmenus.min.js | 3 + assets/lib/sticky/jquery.sticky.js | 388 + assets/lib/sticky/jquery.sticky.min.js | 1 + base/base-carousel-trait.php | 1406 + base/base-widget-trait.php | 52 + base/base-widget.php | 14 + base/module-base.php | 12 + base/on-import-trait.php | 134 + changelog.txt | 2388 + core/admin/admin.php | 283 + core/admin/canary-deployment.php | 25 + core/app/app.php | 99 + core/app/assets/js/hooks/use-feature-lock.js | 22 + core/app/assets/js/index.js | 3 + core/app/assets/js/ui/connect-button.js | 48 + core/app/assets/js/utils.js | 29 + core/app/assets/styles/app-imports.scss | 8 + core/app/modules/import-export/module.php | 70 + .../runners/export/templates.php | 65 + .../runners/import/templates.php | 130 + .../runners/revert/templates.php | 62 + core/app/modules/kit-library/module.php | 88 + core/app/modules/onboarding/module.php | 73 + .../assets/js/atoms/indicator-bullet.js | 15 + .../assets/js/atoms/indicator-bullet.scss | 34 + .../assets/js/atoms/preview-iframe.js | 37 + .../assets/js/atoms/preview-iframe.scss | 17 + .../assets/js/context/base-context.js | 48 + .../assets/js/context/conditions.js | 296 + .../assets/js/context/models/condition.js | 72 + .../js/context/services/conditions-config.js | 130 + .../assets/js/context/templates.js | 152 + .../js/data/commands/conditions-config.js | 9 + .../assets/js/data/commands/index.js | 4 + .../templates-conditions-conflicts.js | 9 + .../js/data/commands/templates-conditions.js | 9 + .../assets/js/data/commands/templates.js | 9 + .../site-editor/assets/js/data/component.js | 13 + .../modules/site-editor/assets/js/editor.js | 14 + .../js/hooks/use-templates-screenshot.js | 54 + .../assets/js/molecules/back-button.js | 24 + .../assets/js/molecules/back-button.scss | 8 + .../assets/js/molecules/site-template-body.js | 33 + .../js/molecules/site-template-footer.js | 23 + .../js/molecules/site-template-header.js | 47 + .../js/molecules/site-template-thumbnail.js | 28 + .../assets/js/molecules/site-template.js | 59 + .../assets/js/molecules/site-template.scss | 117 + .../assets/js/organisms/site-templates.js | 97 + .../site-editor/assets/js/pages/add-new.js | 53 + .../site-editor/assets/js/pages/add-new.scss | 7 + .../pages/conditions/condition-conflicts.js | 28 + .../js/pages/conditions/condition-name.js | 28 + .../js/pages/conditions/condition-sub-id.js | 85 + .../js/pages/conditions/condition-sub.js | 28 + .../js/pages/conditions/condition-type.js | 40 + .../js/pages/conditions/conditions-api.scss | 95 + .../js/pages/conditions/conditions-rows.js | 89 + .../assets/js/pages/conditions/conditions.js | 44 + .../js/pages/conditions/conditions.scss | 187 + .../site-editor/assets/js/pages/import.js | 119 + .../assets/js/pages/template-type.js | 37 + .../assets/js/pages/template-type.scss | 13 + .../site-editor/assets/js/pages/templates.js | 22 + .../assets/js/part-actions/dialog-delete.js | 38 + .../assets/js/part-actions/dialog-rename.js | 56 + .../js/part-actions/dialogs-and-buttons.js | 79 + .../site-editor/assets/js/site-editor.js | 90 + .../site-editor/assets/js/site-editor.scss | 14 + .../modules/site-editor/data/controller.php | 26 + .../data/endpoints/base-endpoint.php | 23 + .../data/endpoints/conditions-config.php | 23 + .../data/endpoints/template-types.php | 25 + .../templates-conditions-conflicts.php | 27 + .../data/endpoints/templates-conditions.php | 85 + .../site-editor/data/endpoints/templates.php | 238 + .../data/responses/lock-error-response.php | 26 + core/app/modules/site-editor/module.php | 221 + .../render-mode-template-preview.php | 48 + core/behaviors/feature-lock.php | 58 + core/behaviors/temp-lock-behavior.php | 39 + core/compatibility/compatibility.php | 25 + core/connect/apps/activate.php | 139 + core/connect/manager.php | 28 + core/database/base-database-updater.php | 127 + core/database/base-migration.php | 182 + core/database/join-clause.php | 78 + core/database/model-base.php | 160 + core/database/model-query-builder.php | 100 + core/database/query-builder.php | 1342 + core/editor/editor.php | 189 + core/editor/notice-bar.php | 109 + core/editor/promotion.php | 67 + core/editor/template.php | 16 + core/integrations/actions/action-base.php | 47 + .../actions/email/email-address.php | 50 + .../actions/email/email-message.php | 239 + core/integrations/actions/email/email.php | 128 + .../exceptions/action-failed-exception.php | 18 + .../action-validation-failed-exception.php | 18 + .../exceptions/exception-base.php | 70 + core/integrations/integrations-manager.php | 112 + .../isolation/wordpress-adapter-interface.php | 9 + core/isolation/wordpress-adapter.php | 18 + core/modules-manager.php | 114 + core/notifications/notification.php | 29 + core/notifications/notifications-manager.php | 30 + core/notifications/traits/notifiable.php | 30 + core/php-api.php | 23 + core/preview/preview.php | 32 + core/upgrade/manager.php | 43 + core/upgrade/upgrades.php | 1220 + core/utils.php | 424 + core/utils/collection.php | 79 + core/utils/registrar.php | 68 + data/base/controller.php | 13 + data/http-status.php | 23 + elementor-pro.php | 202 + license.txt | 9 + license/admin.php | 755 + license/api.php | 625 + license/assets/js/admin.js | 33 + license/notices/trial-expired-notice.php | 41 + license/notices/trial-period-notice.php | 53 + license/updater.php | 253 + modules/admin-top-bar/module.php | 53 + modules/animated-headline/module.php | 21 + .../widgets/animated-headline.php | 619 + modules/announcements/module.php | 76 + .../triggers/is-license-expired.php | 37 + .../custom-fonts-menu-item.php | 32 + .../custom-fonts-promotion-menu-item.php | 67 + .../custom-icons-menu-item.php | 32 + .../custom-icons-promotion-menu-item.php | 67 + .../asset-types/fonts-manager.php | 615 + .../asset-types/fonts/custom-fonts.php | 467 + .../asset-types/fonts/typekit-fonts.php | 262 + .../asset-types/icons-manager.php | 259 + .../asset-types/icons/custom-icons.php | 503 + .../asset-types/icons/font-awesome-pro.php | 161 + .../asset-types/icons/icon-sets/fontastic.php | 76 + .../asset-types/icons/icon-sets/fontello.php | 79 + .../asset-types/icons/icon-sets/icomoon.php | 76 + .../icons/icon-sets/icon-set-base.php | 254 + .../asset-types/icons/templates.php | 21 + .../assets-manager/classes/assets-base.php | 378 + modules/assets-manager/classes/font-base.php | 48 + modules/assets-manager/module.php | 51 + modules/blockquote/module.php | 21 + modules/blockquote/widgets/blockquote.php | 1013 + modules/call-to-action/module.php | 21 + .../call-to-action/widgets/call-to-action.php | 1984 + modules/carousel/module.php | 23 + modules/carousel/widgets/base.php | 627 + modules/carousel/widgets/media-carousel.php | 828 + modules/carousel/widgets/reviews.php | 908 + .../carousel/widgets/testimonial-carousel.php | 710 + modules/code-highlight/module.php | 40 + .../code-highlight/widgets/code-highlight.php | 318 + .../compatibility-tag-component.php | 45 + modules/compatibility-tag/module.php | 39 + modules/countdown/module.php | 21 + modules/countdown/widgets/countdown.php | 766 + modules/custom-attributes/module.php | 176 + .../custom-code-menu-item.php | 36 + .../custom-code-promotion-menu-item.php | 90 + modules/custom-code/custom-code-metabox.php | 424 + modules/custom-code/document.php | 101 + modules/custom-code/module.php | 449 + .../settings-custom-css-pro.php | 20 + modules/custom-css/module.php | 189 + .../classes/and-condition.php | 49 + .../classes/cache-notice.php | 22 + .../classes/comparator-provider.php | 58 + .../classes/comparators-checker.php | 127 + .../classes/conditions-manager.php | 203 + .../custom-fields-data-provider.php | 58 + .../classes/dynamic-tags/data-provider.php | 14 + .../dynamic-tags-data-provider.php | 102 + .../classes/experiments.php | 30 + .../classes/or-condition.php | 40 + .../archive-of-author-condition.php | 77 + .../archive-of-category-condition.php | 36 + .../conditions/archive-of-tag-condition.php | 35 + .../base/archive-condition-base.php | 78 + .../conditions/base/condition-base.php | 53 + .../conditions/base/date-condition-base.php | 94 + .../conditions/base/title-condition-base.php | 52 + .../conditions/current-date-condition.php | 37 + .../date-of-modification-condition.php | 41 + .../conditions/date-of-publish-condition.php | 40 + .../conditions/day-of-the-week-condition.php | 78 + .../conditions/dynamic-tags-condition.php | 110 + .../conditions/featured-image-condition.php | 63 + .../conditions/from-url-condition.php | 62 + .../conditions/in-categories-condition.php | 77 + .../conditions/in-tags-condition.php | 73 + .../conditions/login-status-condition.php | 59 + .../conditions/page-author-condition.php | 76 + .../conditions/page-parent-condition.php | 74 + .../conditions/page-title-condition.php | 27 + .../conditions/post-author-condition.php | 13 + .../post-number-of-comments-condition.php | 62 + .../conditions/post-title-condition.php | 26 + .../conditions/time-of-the-day-condition.php | 111 + .../user-registration-date-condition.php | 40 + .../conditions/user-role-condition.php | 70 + modules/display-conditions/module.php | 264 + .../acf/dynamic-value-provider.php | 63 + modules/dynamic-tags/acf/module.php | 157 + modules/dynamic-tags/acf/tags/acf-color.php | 59 + .../dynamic-tags/acf/tags/acf-date-time.php | 97 + modules/dynamic-tags/acf/tags/acf-file.php | 31 + modules/dynamic-tags/acf/tags/acf-gallery.php | 65 + modules/dynamic-tags/acf/tags/acf-image.php | 99 + modules/dynamic-tags/acf/tags/acf-number.php | 58 + modules/dynamic-tags/acf/tags/acf-text.php | 129 + modules/dynamic-tags/acf/tags/acf-url.php | 116 + .../components/author-meta-filter.php | 73 + modules/dynamic-tags/module.php | 164 + .../pods/dynamic-value-provider.php | 59 + modules/dynamic-tags/pods/module.php | 135 + modules/dynamic-tags/pods/tags/pods-base.php | 64 + .../dynamic-tags/pods/tags/pods-date-time.php | 95 + modules/dynamic-tags/pods/tags/pods-date.php | 96 + .../dynamic-tags/pods/tags/pods-gallery.php | 83 + modules/dynamic-tags/pods/tags/pods-image.php | 87 + .../dynamic-tags/pods/tags/pods-numeric.php | 39 + modules/dynamic-tags/pods/tags/pods-text.php | 55 + modules/dynamic-tags/pods/tags/pods-url.php | 95 + .../dynamic-tags/tags/archive-description.php | 46 + modules/dynamic-tags/tags/archive-meta.php | 66 + modules/dynamic-tags/tags/archive-title.php | 76 + modules/dynamic-tags/tags/archive-url.php | 38 + modules/dynamic-tags/tags/author-info.php | 69 + modules/dynamic-tags/tags/author-meta.php | 31 + modules/dynamic-tags/tags/author-name.php | 32 + .../tags/author-profile-picture.php | 41 + modules/dynamic-tags/tags/author-url.php | 64 + modules/dynamic-tags/tags/base/author-tag.php | 77 + modules/dynamic-tags/tags/base/data-tag.php | 14 + modules/dynamic-tags/tags/base/tag-trait.php | 42 + modules/dynamic-tags/tags/base/tag.php | 16 + modules/dynamic-tags/tags/comments-number.php | 102 + modules/dynamic-tags/tags/comments-url.php | 32 + modules/dynamic-tags/tags/contact-url.php | 461 + .../dynamic-tags/tags/current-date-time.php | 120 + .../dynamic-tags/tags/featured-image-data.php | 121 + modules/dynamic-tags/tags/internal-url.php | 143 + modules/dynamic-tags/tags/lightbox.php | 142 + modules/dynamic-tags/tags/page-title.php | 74 + .../dynamic-tags/tags/post-custom-field.php | 104 + modules/dynamic-tags/tags/post-date.php | 102 + modules/dynamic-tags/tags/post-excerpt.php | 102 + .../dynamic-tags/tags/post-featured-image.php | 57 + modules/dynamic-tags/tags/post-gallery.php | 59 + modules/dynamic-tags/tags/post-id.php | 34 + modules/dynamic-tags/tags/post-terms.php | 113 + modules/dynamic-tags/tags/post-time.php | 96 + modules/dynamic-tags/tags/post-title.php | 31 + modules/dynamic-tags/tags/post-url.php | 33 + .../dynamic-tags/tags/request-parameter.php | 82 + modules/dynamic-tags/tags/shortcode.php | 79 + modules/dynamic-tags/tags/site-logo.php | 42 + modules/dynamic-tags/tags/site-tagline.php | 31 + modules/dynamic-tags/tags/site-title.php | 31 + modules/dynamic-tags/tags/site-url.php | 33 + modules/dynamic-tags/tags/user-info.php | 104 + .../tags/user-profile-picture.php | 21 + modules/dynamic-tags/toolset/module.php | 114 + .../toolset/tags/toolset-base.php | 39 + .../toolset/tags/toolset-date.php | 112 + .../toolset/tags/toolset-gallery.php | 113 + .../toolset/tags/toolset-image.php | 91 + .../toolset/tags/toolset-text.php | 70 + .../dynamic-tags/toolset/tags/toolset-url.php | 97 + modules/element-manager/module.php | 83 + modules/element-manager/options.php | 17 + modules/flip-box/module.php | 21 + modules/flip-box/widgets/flip-box.php | 1776 + modules/forms/actions/activecampaign.php | 343 + modules/forms/actions/activity-log.php | 47 + modules/forms/actions/cf7db.php | 33 + modules/forms/actions/convertkit.php | 286 + modules/forms/actions/discord.php | 222 + modules/forms/actions/drip.php | 349 + modules/forms/actions/email.php | 463 + modules/forms/actions/email2.php | 52 + modules/forms/actions/getresponse.php | 334 + modules/forms/actions/mailchimp.php | 490 + modules/forms/actions/mailerlite.php | 303 + modules/forms/actions/mailpoet.php | 129 + modules/forms/actions/mailpoet3.php | 152 + modules/forms/actions/redirect.php | 79 + modules/forms/actions/slack.php | 252 + modules/forms/actions/webhook.php | 123 + modules/forms/classes/action-base.php | 44 + .../forms/classes/activecampaign-handler.php | 149 + modules/forms/classes/ajax-handler.php | 310 + modules/forms/classes/akismet.php | 181 + modules/forms/classes/convertkit-handler.php | 132 + modules/forms/classes/drip-handler.php | 93 + modules/forms/classes/form-base.php | 273 + modules/forms/classes/form-record.php | 347 + modules/forms/classes/getresponse-handler.php | 146 + modules/forms/classes/honeypot-handler.php | 100 + modules/forms/classes/integration-base.php | 76 + modules/forms/classes/mailchimp-handler.php | 179 + modules/forms/classes/mailerlite-handler.php | 123 + modules/forms/classes/recaptcha-handler.php | 301 + .../forms/classes/recaptcha-v3-handler.php | 152 + modules/forms/classes/rest-client.php | 170 + modules/forms/controls/fields-map.php | 45 + modules/forms/controls/fields-repeater.php | 18 + modules/forms/data/controller.php | 0 modules/forms/fields/acceptance.php | 82 + modules/forms/fields/date.php | 118 + modules/forms/fields/field-base.php | 96 + modules/forms/fields/number.php | 95 + modules/forms/fields/step.php | 133 + modules/forms/fields/tel.php | 38 + modules/forms/fields/time.php | 91 + modules/forms/fields/upload.php | 556 + modules/forms/module.php | 232 + .../registrars/form-actions-registrar.php | 96 + .../registrars/form-fields-registrar.php | 55 + .../submissions/actions/save-to-database.php | 181 + .../submissions-menu-item.php | 45 + .../submissions-promotion-menu-item.php | 71 + modules/forms/submissions/component.php | 220 + modules/forms/submissions/data/controller.php | 363 + .../submissions/data/endpoints/export.php | 134 + .../data/endpoints/forms-index.php | 35 + .../submissions/data/endpoints/index.php | 146 + .../submissions/data/endpoints/referer.php | 70 + .../submissions/data/endpoints/restore.php | 136 + .../submissions/data/forms-controller.php | 44 + .../data/responses/query-failed-response.php | 28 + .../database/entities/form-snapshot.php | 92 + .../forms/submissions/database/migration.php | 50 + .../database/migrations/base-migration.php | 47 + .../database/migrations/fix-indexes.php | 101 + .../database/migrations/initial.php | 98 + .../database/migrations/referer-extra.php | 21 + modules/forms/submissions/database/query.php | 904 + .../repositories/form-snapshot-repository.php | 168 + .../forms/submissions/export/csv-export.php | 186 + modules/forms/submissions/personal-data.php | 179 + modules/forms/widgets/form.php | 2641 + modules/forms/widgets/login.php | 1098 + modules/gallery/module.php | 31 + modules/gallery/widgets/gallery.php | 1612 + modules/global-widget/data/controller.php | 42 + modules/global-widget/documents/widget.php | 63 + modules/global-widget/module.php | 266 + .../global-widget/views/panel-template.php | 33 + .../global-widget/widgets/global-widget.php | 244 + modules/hotspot/module.php | 21 + modules/hotspot/widgets/hotspot.php | 1250 + modules/library/classes/shortcode.php | 73 + modules/library/module.php | 158 + modules/library/widgets/template.php | 108 + .../library/wp-widgets/elementor-library.php | 157 + .../loop-builder/assets/images/loop-item.svg | 163 + modules/loop-builder/documents/loop.php | 535 + .../loop-builder/files/css/loop-css-trait.php | 149 + .../files/css/loop-dynamic-css.php | 23 + .../loop-builder/files/css/loop-preview.php | 27 + modules/loop-builder/files/css/loop.php | 66 + modules/loop-builder/module.php | 299 + .../providers/taxonomy-loop-provider.php | 388 + modules/loop-builder/skins/skin-loop-base.php | 259 + .../skins/skin-loop-post-taxonomy.php | 35 + modules/loop-builder/skins/skin-loop-post.php | 19 + .../skins/skin-loop-taxonomy-base.php | 203 + .../traits/alternate-templates-trait.php | 560 + modules/loop-builder/views/cta-template.php | 80 + modules/loop-builder/widgets/base.php | 370 + .../loop-builder/widgets/loop-carousel.php | 249 + modules/loop-builder/widgets/loop-grid.php | 579 + modules/loop-filter/data/controller.php | 38 + modules/loop-filter/data/endpoints/base.php | 28 + .../endpoints/get-post-type-taxonomies.php | 58 + .../data/endpoints/refresh-loop.php | 194 + .../loop-filter/data/interfaces/endpoint.php | 18 + modules/loop-filter/module.php | 395 + .../query/data/query-constants.php | 44 + .../query/interfaces/query-interface.php | 10 + .../query/query-types/hierarchy-and-query.php | 93 + .../query/query-types/hierarchy-or-query.php | 90 + .../query/query-types/single-terms-query.php | 88 + .../loop-filter/query/taxonomy-manager.php | 260 + .../query/taxonomy-query-builder.php | 81 + .../traits/hierarchical-taxonomy-trait.php | 80 + .../traits/taxonomy-filter-trait.php | 93 + .../loop-filter/widgets/taxonomy-filter.php | 809 + modules/lottie/assets/animations/default.json | 1 + modules/lottie/module.php | 77 + modules/lottie/widgets/lottie.php | 875 + modules/mega-menu/module.php | 59 + modules/mega-menu/traits/url-helper-trait.php | 44 + modules/mega-menu/widgets/mega-menu.php | 2631 + modules/motion-fx/controls-group.php | 577 + modules/motion-fx/module.php | 141 + modules/nav-menu/module.php | 21 + modules/nav-menu/widgets/nav-menu.php | 1686 + modules/nested-carousel/module.php | 29 + .../widgets/nested-carousel.php | 465 + modules/notes/admin-bar.php | 31 + modules/notes/admin-page.php | 146 + .../assets/images/elementor-logo-orange.png | Bin 0 -> 3936 bytes modules/notes/data/controller.php | 768 + .../data/endpoints/read-status-endpoint.php | 131 + .../notes/data/endpoints/summary-endpoint.php | 67 + .../notes/data/endpoints/users-endpoint.php | 131 + .../migrations/add-author-display-name.php | 27 + .../database/migrations/add-capabilities.php | 41 + .../database/migrations/add-note-position.php | 27 + .../database/migrations/add-route-post-id.php | 27 + modules/notes/database/migrations/initial.php | 72 + modules/notes/database/models/document.php | 97 + .../notes/database/models/note-summary.php | 81 + modules/notes/database/models/note.php | 471 + modules/notes/database/models/user.php | 130 + .../notes/database/notes-database-updater.php | 45 + .../database/query/note-query-builder.php | 540 + .../database/query/user-query-builder.php | 61 + .../transformers/user-transformer.php | 61 + modules/notes/document-events.php | 122 + modules/notes/module.php | 229 + .../notifications/base-notes-notification.php | 94 + .../user-mentioned-notification.php | 35 + .../user-replied-notification.php | 35 + .../user-resolved-notification.php | 35 + modules/notes/notifications/views/email.php | 108 + modules/notes/usage.php | 317 + modules/notes/user/capabilities.php | 282 + modules/notes/user/delete-user.php | 76 + modules/notes/user/personal-data.php | 122 + modules/notes/user/preferences.php | 113 + modules/notes/utils.php | 114 + modules/page-transitions/module.php | 877 + modules/payments/classes/payment-button.php | 573 + modules/payments/classes/stripe-handler.php | 41 + modules/payments/module.php | 470 + modules/payments/widgets/paypal-button.php | 368 + modules/payments/widgets/stripe-button.php | 534 + .../admin-menu-items/popups-menu-item.php | 32 + .../popups-promotion-menu-item.php | 81 + modules/popup/assets/images/timing-tab.svg | 5 + .../popup/assets/images/timing/browsers.svg | 15 + .../popup/assets/images/timing/devices.svg | 18 + .../popup/assets/images/timing/logged_in.svg | 11 + .../popup/assets/images/timing/page_views.svg | 15 + .../popup/assets/images/timing/schedule.svg | 24 + .../popup/assets/images/timing/sessions.svg | 14 + .../popup/assets/images/timing/sources.svg | 14 + modules/popup/assets/images/timing/times.svg | 11 + modules/popup/assets/images/timing/url.svg | 14 + modules/popup/assets/images/triggers-tab.svg | 9 + .../popup/assets/images/triggers/click.svg | 9 + .../assets/images/triggers/exit_intent.svg | 11 + .../assets/images/triggers/inactivity.svg | 9 + .../assets/images/triggers/page_load.svg | 10 + .../assets/images/triggers/scrolling.svg | 8 + .../assets/images/triggers/scrolling_to.svg | 9 + modules/popup/display-settings/base.php | 62 + modules/popup/display-settings/timing.php | 310 + modules/popup/display-settings/triggers.php | 127 + modules/popup/document.php | 848 + modules/popup/form-action.php | 143 + modules/popup/module.php | 273 + modules/popup/tag.php | 134 + modules/posts/data/controller.php | 61 + modules/posts/module.php | 63 + modules/posts/skins/skin-base.php | 1409 + modules/posts/skins/skin-cards.php | 649 + modules/posts/skins/skin-classic.php | 194 + modules/posts/skins/skin-content-base.php | 333 + modules/posts/skins/skin-full-content.php | 14 + modules/posts/traits/button-widget-trait.php | 509 + modules/posts/traits/pagination-trait.php | 153 + modules/posts/widgets/portfolio.php | 716 + modules/posts/widgets/posts-base.php | 905 + modules/posts/widgets/posts.php | 113 + modules/pricing/module.php | 22 + modules/pricing/widgets/price-list.php | 720 + modules/pricing/widgets/price-table.php | 1820 + modules/progress-tracker/module.php | 21 + .../widgets/progress-tracker.php | 653 + .../classes/elementor-post-query.php | 435 + .../classes/elementor-related-query.php | 169 + .../controls/group-control-posts.php | 315 + .../controls/group-control-query.php | 568 + .../controls/group-control-related.php | 122 + .../controls/group-control-taxonomy.php | 38 + modules/query-control/controls/query.php | 63 + .../query-control/controls/template-query.php | 75 + modules/query-control/module.php | 1010 + modules/role-manager/module.php | 133 + modules/screenshots/module.php | 275 + .../screenshots/render-mode-screenshot.php | 75 + modules/screenshots/screenshot.php | 251 + modules/scroll-snap/module.php | 133 + modules/share-buttons/module.php | 127 + .../share-buttons/widgets/share-buttons.php | 700 + modules/slides/module.php | 21 + modules/slides/widgets/slides.php | 1537 + .../social/classes/facebook-sdk-manager.php | 181 + modules/social/module.php | 35 + modules/social/widgets/facebook-button.php | 217 + modules/social/widgets/facebook-comments.php | 151 + modules/social/widgets/facebook-embed.php | 223 + modules/social/widgets/facebook-page.php | 169 + modules/sticky/module.php | 201 + modules/table-of-contents/module.php | 31 + .../widgets/table-of-contents.php | 924 + .../theme-builder-menu-item.php | 31 + modules/theme-builder/api.php | 21 + .../assets/images/conditions-tab.svg | 19 + .../classes/conditions-cache.php | 143 + .../classes/conditions-manager.php | 558 + .../classes/conditions-repeater.php | 78 + .../classes/control-media-preview.php | 35 + .../classes/locations-manager.php | 595 + .../theme-builder/classes/preview-manager.php | 78 + .../classes/template-conditions.php | 27 + .../classes/templates-types-manager.php | 51 + .../theme-builder/classes/theme-support.php | 130 + .../conditions/any-child-of-term.php | 46 + .../theme-builder/conditions/any-child-of.php | 28 + modules/theme-builder/conditions/archive.php | 64 + modules/theme-builder/conditions/author.php | 47 + .../theme-builder/conditions/by-author.php | 47 + .../conditions/child-of-term.php | 39 + modules/theme-builder/conditions/child-of.php | 62 + .../conditions/condition-base.php | 68 + modules/theme-builder/conditions/date.php | 29 + .../theme-builder/conditions/front-page.php | 29 + modules/theme-builder/conditions/general.php | 34 + .../theme-builder/conditions/in-sub-term.php | 39 + .../theme-builder/conditions/in-taxonomy.php | 65 + .../theme-builder/conditions/not-found404.php | 29 + .../conditions/post-type-archive.php | 73 + .../conditions/post-type-by-author.php | 56 + modules/theme-builder/conditions/post.php | 95 + modules/theme-builder/conditions/search.php | 29 + modules/theme-builder/conditions/singular.php | 62 + modules/theme-builder/conditions/taxonomy.php | 70 + .../documents/archive-single-base.php | 60 + modules/theme-builder/documents/archive.php | 105 + modules/theme-builder/documents/error-404.php | 53 + modules/theme-builder/documents/footer.php | 43 + .../documents/header-footer-base.php | 48 + modules/theme-builder/documents/header.php | 44 + .../documents/search-results.php | 68 + modules/theme-builder/documents/section.php | 113 + .../theme-builder/documents/single-base.php | 182 + .../theme-builder/documents/single-page.php | 46 + .../theme-builder/documents/single-post.php | 43 + modules/theme-builder/documents/single.php | 46 + .../documents/theme-document.php | 682 + .../documents/theme-page-document.php | 131 + .../documents/theme-section-document.php | 43 + modules/theme-builder/module.php | 504 + .../skins/post-comments-skin-classic.php | 492 + .../skins/posts-archive-skin-base.php | 56 + .../skins/posts-archive-skin-cards.php | 34 + .../skins/posts-archive-skin-classic.php | 33 + .../skins/posts-archive-skin-full-content.php | 21 + .../generate-press-theme-support.php | 63 + .../theme-support/safe-mode-theme-support.php | 34 + .../theme-builder/views/comments-template.php | 53 + .../theme-builder/views/panel-template.php | 96 + .../views/theme-support-footer.php | 14 + .../views/theme-support-header.php | 33 + .../theme-builder/widgets/archive-posts.php | 151 + .../theme-builder/widgets/archive-title.php | 43 + modules/theme-builder/widgets/page-title.php | 43 + .../theme-builder/widgets/post-content.php | 115 + .../theme-builder/widgets/post-excerpt.php | 128 + .../widgets/post-featured-image.php | 65 + modules/theme-builder/widgets/post-title.php | 51 + modules/theme-builder/widgets/site-logo.php | 221 + modules/theme-builder/widgets/site-title.php | 99 + .../widgets/title-widget-base.php | 62 + modules/theme-elements/module.php | 39 + modules/theme-elements/widgets/author-box.php | 1590 + modules/theme-elements/widgets/base.php | 17 + .../theme-elements/widgets/breadcrumbs.php | 195 + .../theme-elements/widgets/post-comments.php | 125 + modules/theme-elements/widgets/post-info.php | 1108 + .../widgets/post-navigation.php | 639 + .../theme-elements/widgets/search-form.php | 966 + modules/theme-elements/widgets/sitemap.php | 736 + .../admin-menu-items/base-promotion-item.php | 67 + .../base-promotion-template.php | 103 + modules/tiers/module.php | 53 + modules/usage/features-reporter.php | 34 + modules/usage/integrations-reporter.php | 49 + modules/usage/module.php | 190 + modules/video-playlist/module.php | 23 + .../video-playlist/widgets/video-playlist.php | 2310 + .../classes/base-products-renderer.php | 24 + .../classes/current-query-renderer.php | 85 + .../woocommerce/classes/products-renderer.php | 351 + .../conditions/product-archive.php | 61 + .../woocommerce/conditions/product-search.php | 32 + modules/woocommerce/conditions/shop-page.php | 31 + .../woocommerce/conditions/woocommerce.php | 43 + modules/woocommerce/data/controller.php | 20 + .../data/endpoints/base-endpoint.php | 23 + .../data/endpoints/get-notice-dismissed.php | 35 + .../data/endpoints/set-notice-dismissed.php | 49 + .../woocommerce/documents/product-archive.php | 170 + .../woocommerce/documents/product-post.php | 77 + modules/woocommerce/documents/product.php | 195 + modules/woocommerce/module.php | 1648 + .../settings/settings-woocommerce.php | 899 + modules/woocommerce/skins/skin-classic.php | 89 + .../skins/skin-loop-product-taxonomy.php | 31 + .../woocommerce/skins/skin-loop-product.php | 58 + modules/woocommerce/tags/base-data-tag.php | 21 + modules/woocommerce/tags/base-tag.php | 25 + modules/woocommerce/tags/category-image.php | 63 + modules/woocommerce/tags/product-content.php | 30 + modules/woocommerce/tags/product-gallery.php | 46 + modules/woocommerce/tags/product-image.php | 57 + modules/woocommerce/tags/product-price.php | 60 + modules/woocommerce/tags/product-rating.php | 60 + modules/woocommerce/tags/product-sale.php | 46 + .../tags/product-short-description.php | 30 + modules/woocommerce/tags/product-sku.php | 36 + modules/woocommerce/tags/product-stock.php | 52 + modules/woocommerce/tags/product-terms.php | 79 + modules/woocommerce/tags/product-title.php | 36 + .../tags/traits/tag-product-id.php | 28 + .../tags/woocommerce-add-to-cart.php | 93 + .../woocommerce/traits/product-id-trait.php | 27 + modules/woocommerce/traits/products-trait.php | 205 + .../wc-templates/cart/mini-cart.php | 101 + modules/woocommerce/widgets/add-to-cart.php | 312 + .../widgets/archive-description.php | 115 + .../widgets/archive-products-deprecated.php | 163 + .../woocommerce/widgets/archive-products.php | 188 + modules/woocommerce/widgets/base-widget.php | 153 + modules/woocommerce/widgets/breadcrumb.php | 120 + modules/woocommerce/widgets/cart.php | 2661 + modules/woocommerce/widgets/categories.php | 408 + .../woocommerce/widgets/category-image.php | 68 + modules/woocommerce/widgets/checkout.php | 4338 + modules/woocommerce/widgets/elements.php | 163 + modules/woocommerce/widgets/menu-cart.php | 2236 + modules/woocommerce/widgets/my-account.php | 2164 + modules/woocommerce/widgets/notices.php | 130 + .../widgets/product-add-to-cart.php | 826 + .../product-additional-information.php | 110 + .../woocommerce/widgets/product-content.php | 31 + .../woocommerce/widgets/product-data-tabs.php | 285 + .../woocommerce/widgets/product-images.php | 203 + modules/woocommerce/widgets/product-meta.php | 460 + modules/woocommerce/widgets/product-price.php | 183 + .../woocommerce/widgets/product-rating.php | 193 + .../woocommerce/widgets/product-related.php | 257 + .../widgets/product-short-description.php | 116 + modules/woocommerce/widgets/product-stock.php | 88 + modules/woocommerce/widgets/product-title.php | 97 + .../woocommerce/widgets/product-upsell.php | 235 + modules/woocommerce/widgets/products-base.php | 1120 + .../widgets/products-deprecated.php | 278 + modules/woocommerce/widgets/products.php | 387 + .../woocommerce/widgets/purchase-summary.php | 1719 + .../woocommerce/widgets/single-elements.php | 144 + modules/wp-cli/license-command.php | 68 + modules/wp-cli/module.php | 38 + modules/wp-cli/theme-builder.php | 36 + modules/wp-cli/update.php | 18 + plugin.php | 529 + readme.txt | 7 + run-on-linux.js | 44 + sample-data/acf-extra-post-fields.json | 71 + sample-data/acf-fields-products.json | 68 + sample-data/sample_products_with_acf_meta.xml | 4904 + .../simple-taxonomy-loop-template.json | 1 + 975 files changed, 419096 insertions(+) create mode 160000 Elementor Pro 3.21.2 create mode 100644 assets/css/admin-rtl.css create mode 100644 assets/css/admin-rtl.min.css create mode 100644 assets/css/admin.css create mode 100644 assets/css/admin.min.css create mode 100644 assets/css/app-rtl.css create mode 100644 assets/css/app-rtl.min.css create mode 100644 assets/css/app.css create mode 100644 assets/css/app.min.css create mode 100644 assets/css/editor-rtl.css create mode 100644 assets/css/editor-rtl.min.css create mode 100644 assets/css/editor.css create mode 100644 assets/css/editor.min.css create mode 100644 assets/css/frontend-lite-rtl.css create mode 100644 assets/css/frontend-lite-rtl.min.css create mode 100644 assets/css/frontend-lite.css create mode 100644 assets/css/frontend-lite.min.css create mode 100644 assets/css/frontend-msie.css create mode 100644 assets/css/frontend-msie.min.css create mode 100644 assets/css/frontend-rtl.css create mode 100644 assets/css/frontend-rtl.min.css create mode 100644 assets/css/frontend.css create mode 100644 assets/css/frontend.min.css create mode 100644 assets/css/loop-grid-cta.css create mode 100644 assets/css/loop-grid-cta.min.css create mode 100644 assets/css/modules/code-highlight.css create mode 100644 assets/css/modules/code-highlight.min.css create mode 100644 assets/css/modules/custom-code-rtl.css create mode 100644 assets/css/modules/custom-code-rtl.min.css create mode 100644 assets/css/modules/custom-code.css create mode 100644 assets/css/modules/custom-code.min.css create mode 100644 assets/css/modules/forms/submissions/admin-rtl.css create mode 100644 assets/css/modules/forms/submissions/admin-rtl.min.css create mode 100644 assets/css/modules/forms/submissions/admin.css create mode 100644 assets/css/modules/forms/submissions/admin.min.css create mode 100644 assets/css/modules/notes/frontend.css create mode 100644 assets/css/modules/notes/frontend.min.css create mode 100644 assets/css/preview-rtl.css create mode 100644 assets/css/preview-rtl.min.css create mode 100644 assets/css/preview.css create mode 100644 assets/css/preview.min.css create mode 100644 assets/css/templates/frontend-lite-rtl.css create mode 100644 assets/css/templates/frontend-lite-rtl.min.css create mode 100644 assets/css/templates/frontend-lite.css create mode 100644 assets/css/templates/frontend-lite.min.css create mode 100644 assets/css/templates/frontend-rtl.css create mode 100644 assets/css/templates/frontend-rtl.min.css create mode 100644 assets/css/templates/frontend.css create mode 100644 assets/css/templates/frontend.min.css create mode 100644 assets/css/templates/widget-call-to-action-rtl.min.css create mode 100644 assets/css/templates/widget-call-to-action.min.css create mode 100644 assets/css/templates/widget-flip-box-rtl.min.css create mode 100644 assets/css/templates/widget-flip-box.min.css create mode 100644 assets/css/templates/widget-mega-menu-rtl.min.css create mode 100644 assets/css/templates/widget-mega-menu.min.css create mode 100644 assets/css/templates/widget-nav-menu-rtl.min.css create mode 100644 assets/css/templates/widget-nav-menu.min.css create mode 100644 assets/css/templates/widget-slides-rtl.min.css create mode 100644 assets/css/templates/widget-slides.min.css create mode 100644 assets/css/templates/widget-video-playlist-rtl.min.css create mode 100644 assets/css/templates/widget-video-playlist.min.css create mode 100644 assets/css/widget-animated-headline-rtl.min.css create mode 100644 assets/css/widget-animated-headline.min.css create mode 100644 assets/css/widget-blockquote-rtl.min.css create mode 100644 assets/css/widget-blockquote.min.css create mode 100644 assets/css/widget-call-to-action-rtl.min.css create mode 100644 assets/css/widget-call-to-action.min.css create mode 100644 assets/css/widget-carousel-rtl.min.css create mode 100644 assets/css/widget-carousel.min.css create mode 100644 assets/css/widget-countdown-rtl.min.css create mode 100644 assets/css/widget-countdown.min.css create mode 100644 assets/css/widget-flip-box-rtl.min.css create mode 100644 assets/css/widget-flip-box.min.css create mode 100644 assets/css/widget-forms-rtl.min.css create mode 100644 assets/css/widget-forms.min.css create mode 100644 assets/css/widget-gallery-rtl.min.css create mode 100644 assets/css/widget-gallery.min.css create mode 100644 assets/css/widget-global-widget-rtl.min.css create mode 100644 assets/css/widget-global-widget.min.css create mode 100644 assets/css/widget-hotspot-rtl.min.css create mode 100644 assets/css/widget-hotspot.min.css create mode 100644 assets/css/widget-loop-builder-rtl.min.css create mode 100644 assets/css/widget-loop-builder.min.css create mode 100644 assets/css/widget-loop-filter-rtl.min.css create mode 100644 assets/css/widget-loop-filter.min.css create mode 100644 assets/css/widget-lottie-rtl.min.css create mode 100644 assets/css/widget-lottie.min.css create mode 100644 assets/css/widget-mega-menu-rtl.min.css create mode 100644 assets/css/widget-mega-menu.min.css create mode 100644 assets/css/widget-motion-fx-rtl.min.css create mode 100644 assets/css/widget-motion-fx.min.css create mode 100644 assets/css/widget-nav-menu-rtl.min.css create mode 100644 assets/css/widget-nav-menu.min.css create mode 100644 assets/css/widget-nested-carousel-rtl.min.css create mode 100644 assets/css/widget-nested-carousel.min.css create mode 100644 assets/css/widget-notes-rtl.min.css create mode 100644 assets/css/widget-notes.min.css create mode 100644 assets/css/widget-payments-rtl.min.css create mode 100644 assets/css/widget-payments.min.css create mode 100644 assets/css/widget-popup-rtl.min.css create mode 100644 assets/css/widget-popup.min.css create mode 100644 assets/css/widget-posts-rtl.min.css create mode 100644 assets/css/widget-posts.min.css create mode 100644 assets/css/widget-pricing-rtl.min.css create mode 100644 assets/css/widget-pricing.min.css create mode 100644 assets/css/widget-progress-tracker-rtl.min.css create mode 100644 assets/css/widget-progress-tracker.min.css create mode 100644 assets/css/widget-share-buttons-rtl.min.css create mode 100644 assets/css/widget-share-buttons.min.css create mode 100644 assets/css/widget-slides-rtl.min.css create mode 100644 assets/css/widget-slides.min.css create mode 100644 assets/css/widget-social-rtl.min.css create mode 100644 assets/css/widget-social.min.css create mode 100644 assets/css/widget-sticky-rtl.min.css create mode 100644 assets/css/widget-sticky.min.css create mode 100644 assets/css/widget-table-of-contents-rtl.min.css create mode 100644 assets/css/widget-table-of-contents.min.css create mode 100644 assets/css/widget-theme-builder-rtl.min.css create mode 100644 assets/css/widget-theme-builder.min.css create mode 100644 assets/css/widget-theme-elements-rtl.min.css create mode 100644 assets/css/widget-theme-elements.min.css create mode 100644 assets/css/widget-video-playlist-rtl.min.css create mode 100644 assets/css/widget-video-playlist.min.css create mode 100644 assets/css/widget-woocommerce-rtl.min.css create mode 100644 assets/css/widget-woocommerce.min.css create mode 100644 assets/css/woocommerce-notices.css create mode 100644 assets/css/woocommerce-notices.min.css create mode 100644 assets/data/responsive-widgets.json create mode 100644 assets/images/announcements/license-expired.png create mode 100644 assets/images/logo-placeholder.png create mode 100644 assets/js/1b816ba777b14157325b.bundle.min.js create mode 100644 assets/js/4abfbfd970d6f7680bc7.bundle.js create mode 100644 assets/js/60745ddf42fde6647dbc.bundle.min.js create mode 100644 assets/js/60745ddf42fde6647dbc.bundle.min.js.LICENSE.txt create mode 100644 assets/js/98217e0c00e1f53421ef.bundle.js create mode 100644 assets/js/admin.js create mode 100644 assets/js/admin.min.js create mode 100644 assets/js/ajax-pagination.a8dae0f5699fe9733e7d.bundle.min.js create mode 100644 assets/js/ajax-pagination.bc400e6cb24a14a2ea97.bundle.js create mode 100644 assets/js/animated-headline.3efc6517c2a055f6c242.bundle.min.js create mode 100644 assets/js/animated-headline.e4c2ed3934d0df18c40a.bundle.js create mode 100644 assets/js/app.js create mode 100644 assets/js/app.min.js create mode 100644 assets/js/archive-posts.0aae8c3bd7d196797b6c.bundle.js create mode 100644 assets/js/archive-posts.d30c917134774f65dd6d.bundle.min.js create mode 100644 assets/js/carousel.998a291abf70435fd698.bundle.js create mode 100644 assets/js/carousel.9b02b45d7826c1c48f33.bundle.min.js create mode 100644 assets/js/code-highlight.28a979661569ddbbf60d.bundle.min.js create mode 100644 assets/js/code-highlight.8b676d9a001f56fb77fa.bundle.js create mode 100644 assets/js/countdown.60cf02eaf22d71d83f3d.bundle.js create mode 100644 assets/js/countdown.be941c879efa861dbbfa.bundle.min.js create mode 100644 assets/js/custom-code.js create mode 100644 assets/js/custom-code.min.js create mode 100644 assets/js/display-conditions.js create mode 100644 assets/js/display-conditions.min.js create mode 100644 assets/js/editor.js create mode 100644 assets/js/editor.min.js create mode 100644 assets/js/elements-handlers.js create mode 100644 assets/js/elements-handlers.min.js create mode 100644 assets/js/form-submission-admin.js create mode 100644 assets/js/form-submission-admin.min.js create mode 100644 assets/js/form.10bf1a6475f0741920ff.bundle.min.js create mode 100644 assets/js/form.efd3434e4ecbe4dd5fc6.bundle.js create mode 100644 assets/js/frontend.js create mode 100644 assets/js/frontend.min.js create mode 100644 assets/js/gallery.805130d33e18cb04635f.bundle.js create mode 100644 assets/js/gallery.8ca9a354ce039d1ba641.bundle.min.js create mode 100644 assets/js/gutenberg-woocommerce-notice.js create mode 100644 assets/js/gutenberg-woocommerce-notice.min.js create mode 100644 assets/js/hotspot.6ab1751404c381bfe390.bundle.min.js create mode 100644 assets/js/hotspot.70886883c622dd8d5eb2.bundle.js create mode 100644 assets/js/jszip.vendor.99a5b769619f50a6cb60.bundle.min.js create mode 100644 assets/js/jszip.vendor.99a5b769619f50a6cb60.bundle.min.js.LICENSE.txt create mode 100644 assets/js/jszip.vendor.a3c65615c1de5560962d.bundle.js create mode 100644 assets/js/load-more.ad89e46f2f6bfd9c27e8.bundle.js create mode 100644 assets/js/load-more.bc9573b5d1f73abd80b9.bundle.min.js create mode 100644 assets/js/loop-carousel.4e8fd6593adbba21698e.bundle.min.js create mode 100644 assets/js/loop-carousel.827a11bd7f1b0343de42.bundle.js create mode 100644 assets/js/loop-filter-editor.21982d6e76a4fba12cd5.bundle.min.js create mode 100644 assets/js/loop-filter-editor.d1bae86a5ed21c0e9981.bundle.js create mode 100644 assets/js/loop.27d8ba43536f8b76ca41.bundle.js create mode 100644 assets/js/loop.4f538ab2476dd2d124e6.bundle.min.js create mode 100644 assets/js/lottie.565b778d23c04461c4ea.bundle.min.js create mode 100644 assets/js/lottie.a00fda0bbf10f9b99eae.bundle.js create mode 100644 assets/js/media-carousel.aca2224ef13e6f999011.bundle.min.js create mode 100644 assets/js/media-carousel.d8417210e0b731dd32b8.bundle.js create mode 100644 assets/js/mega-menu-editor.319a739ef1260a2b2da1.bundle.min.js create mode 100644 assets/js/mega-menu-editor.bbef3f7412481cbce555.bundle.js create mode 100644 assets/js/mega-menu-stretch-content.4648b25d00c1f94cec4e.bundle.js create mode 100644 assets/js/mega-menu-stretch-content.60ca9e1e97c52ac3bf8c.bundle.min.js create mode 100644 assets/js/mega-menu.5eea2dda8d43e4430297.bundle.js create mode 100644 assets/js/mega-menu.611dbb6e55a2c14924ad.bundle.min.js create mode 100644 assets/js/menu-title-keyboard-handler.255f6b16a2f292e5c260.bundle.js create mode 100644 assets/js/menu-title-keyboard-handler.80c53fcbf2fdb487c91d.bundle.min.js create mode 100644 assets/js/nav-menu.ad2c1632628f619ad9e9.bundle.js create mode 100644 assets/js/nav-menu.d43af66e5000fd109c04.bundle.min.js create mode 100644 assets/js/nested-carousel-editor.2fdc278ce6bc9f6ec2e0.bundle.js create mode 100644 assets/js/nested-carousel-editor.6337dab68af203be7c04.bundle.min.js create mode 100644 assets/js/nested-carousel.21c7f0c4423917225bce.bundle.min.js create mode 100644 assets/js/nested-carousel.a6b8a103e170cb2de9a4.bundle.js create mode 100644 assets/js/notes/93.min.js create mode 100644 assets/js/notes/93.min.js.LICENSE.txt create mode 100644 assets/js/notes/notes-app-initiator.js create mode 100644 assets/js/notes/notes-app-initiator.min.js create mode 100644 assets/js/notes/notes-app.js create mode 100644 assets/js/notes/notes-app.min.js create mode 100644 assets/js/notes/notes.js create mode 100644 assets/js/notes/notes.min.js create mode 100644 assets/js/notes/notes.min.js.LICENSE.txt create mode 100644 assets/js/notes/vendors-node_modules_radix-ui_react-alert-dialog_dist_index_module_js-node_modules_radix-ui_r-e4587e.js create mode 100644 assets/js/packages/editor-documents-extended/editor-documents-extended.asset.php create mode 100644 assets/js/packages/editor-documents-extended/editor-documents-extended.js create mode 100644 assets/js/packages/editor-documents-extended/editor-documents-extended.min.js create mode 100644 assets/js/packages/editor-documents-extended/editor-documents-extended.strings.js create mode 100644 assets/js/packages/editor-notes/editor-notes.asset.php create mode 100644 assets/js/packages/editor-notes/editor-notes.js create mode 100644 assets/js/packages/editor-notes/editor-notes.min.js create mode 100644 assets/js/packages/editor-notes/editor-notes.strings.js create mode 100644 assets/js/packages/editor-site-navigation-extended/editor-site-navigation-extended.asset.php create mode 100644 assets/js/packages/editor-site-navigation-extended/editor-site-navigation-extended.js create mode 100644 assets/js/packages/editor-site-navigation-extended/editor-site-navigation-extended.min.js create mode 100644 assets/js/packages/editor-site-navigation-extended/editor-site-navigation-extended.strings.js create mode 100644 assets/js/packages/ui/ui.asset.php create mode 100644 assets/js/packages/ui/ui.js create mode 100644 assets/js/packages/ui/ui.min.js create mode 100644 assets/js/packages/ui/ui.min.js.LICENSE.txt create mode 100644 assets/js/packages/ui/ui.strings.js create mode 100644 assets/js/page-transitions.js create mode 100644 assets/js/page-transitions.min.js create mode 100644 assets/js/paypal-button.3028ea98fc2e17fdfe8f.bundle.js create mode 100644 assets/js/paypal-button.3d0d5af7df85963df32c.bundle.min.js create mode 100644 assets/js/popup.085c1727e36940b18f29.bundle.min.js create mode 100644 assets/js/popup.1f90f6cfd0d44ef28772.bundle.js create mode 100644 assets/js/portfolio.9a52c1f0953359d74119.bundle.js create mode 100644 assets/js/portfolio.b5c5e89624dc6b81a11a.bundle.min.js create mode 100644 assets/js/posts.5d2d70b1d6918b6d8205.bundle.js create mode 100644 assets/js/posts.caaf3e27e57db8207afc.bundle.min.js create mode 100644 assets/js/preview.js create mode 100644 assets/js/preview.min.js create mode 100644 assets/js/product-add-to-cart.023d7d31fbf96c3dbdfc.bundle.min.js create mode 100644 assets/js/product-add-to-cart.e099bc90899376d00959.bundle.js create mode 100644 assets/js/progress-tracker.3ec316715116e9087057.bundle.js create mode 100644 assets/js/progress-tracker.53951a08af7543da98e6.bundle.min.js create mode 100644 assets/js/qunit-tests.js create mode 100644 assets/js/qunit-tests.min.js create mode 100644 assets/js/screenshot.js create mode 100644 assets/js/screenshot.min.js create mode 100644 assets/js/search-form.4beabae7f0e0a3129ef7.bundle.js create mode 100644 assets/js/search-form.a25a87283d08dad12f18.bundle.min.js create mode 100644 assets/js/share-buttons.08f4daf4a4285a8632b8.bundle.min.js create mode 100644 assets/js/share-buttons.58e0fcb000aa02df3f24.bundle.js create mode 100644 assets/js/slides.3b185c687f9167dfae0c.bundle.js create mode 100644 assets/js/slides.fb6b9afd278bb9c5e75b.bundle.min.js create mode 100644 assets/js/social.2d2e44e8608690943f29.bundle.min.js create mode 100644 assets/js/social.deeefd0e3641200f8239.bundle.js create mode 100644 assets/js/stripe-button.2acbca466dfeb9585680.bundle.min.js create mode 100644 assets/js/stripe-button.b00915f9aec396f7b070.bundle.js create mode 100644 assets/js/table-of-contents.82ad797536446d523057.bundle.min.js create mode 100644 assets/js/table-of-contents.e67f9eaf44032e14dc57.bundle.js create mode 100644 assets/js/taxonomy-filter.9d41aac2f76c01cfdb42.bundle.js create mode 100644 assets/js/taxonomy-filter.b42e9c10a9d0abc3454e.bundle.min.js create mode 100644 assets/js/video-playlist.74fca1f2470fa6474595.bundle.min.js create mode 100644 assets/js/video-playlist.964a12bbea2078517f07.bundle.js create mode 100644 assets/js/webpack-pro.runtime.js create mode 100644 assets/js/webpack-pro.runtime.min.js create mode 100644 assets/js/woocommerce-cart.73c6990b0b1a1ea18220.bundle.js create mode 100644 assets/js/woocommerce-cart.fc30c6cb753d4098eff5.bundle.min.js create mode 100644 assets/js/woocommerce-checkout-page.9b1242f2568f94bb8d5c.bundle.js create mode 100644 assets/js/woocommerce-checkout-page.b18af78282979b6f74e4.bundle.min.js create mode 100644 assets/js/woocommerce-menu-cart.010ec7298aee1fcdc2ea.bundle.js create mode 100644 assets/js/woocommerce-menu-cart.faa7b80e9ba9e5072070.bundle.min.js create mode 100644 assets/js/woocommerce-my-account.3ee10d01e625dad87f73.bundle.min.js create mode 100644 assets/js/woocommerce-my-account.6509f179e93231fa2b6a.bundle.js create mode 100644 assets/js/woocommerce-notices.aaa7a3d06f24f7ea6951.bundle.min.js create mode 100644 assets/js/woocommerce-notices.d8c0850de1984ac89f33.bundle.js create mode 100644 assets/js/woocommerce-purchase-summary.46445ab1120a8c28c05c.bundle.min.js create mode 100644 assets/js/woocommerce-purchase-summary.8d56a92f38ab4fc4575f.bundle.js create mode 100644 assets/lib/dom-to-image/js/dom-to-image.js create mode 100644 assets/lib/dom-to-image/js/dom-to-image.min.js create mode 100644 assets/lib/font-awesome-pro/brands.js create mode 100644 assets/lib/font-awesome-pro/duotone.js create mode 100644 assets/lib/font-awesome-pro/light.js create mode 100644 assets/lib/font-awesome-pro/regular.js create mode 100644 assets/lib/font-awesome-pro/solid.js create mode 100644 assets/lib/html2canvas/js/html2canvas.js create mode 100644 assets/lib/html2canvas/js/html2canvas.min.js create mode 100644 assets/lib/instant-page/instant-page.js create mode 100644 assets/lib/instant-page/instant-page.min.js create mode 100644 assets/lib/lottie/lottie.js create mode 100644 assets/lib/lottie/lottie.min.js create mode 100644 assets/lib/smartmenus/jquery.smartmenus.js create mode 100644 assets/lib/smartmenus/jquery.smartmenus.min.js create mode 100644 assets/lib/sticky/jquery.sticky.js create mode 100644 assets/lib/sticky/jquery.sticky.min.js create mode 100644 base/base-carousel-trait.php create mode 100644 base/base-widget-trait.php create mode 100644 base/base-widget.php create mode 100644 base/module-base.php create mode 100644 base/on-import-trait.php create mode 100644 changelog.txt create mode 100644 core/admin/admin.php create mode 100644 core/admin/canary-deployment.php create mode 100644 core/app/app.php create mode 100644 core/app/assets/js/hooks/use-feature-lock.js create mode 100644 core/app/assets/js/index.js create mode 100644 core/app/assets/js/ui/connect-button.js create mode 100644 core/app/assets/js/utils.js create mode 100644 core/app/assets/styles/app-imports.scss create mode 100644 core/app/modules/import-export/module.php create mode 100644 core/app/modules/import-export/runners/export/templates.php create mode 100644 core/app/modules/import-export/runners/import/templates.php create mode 100644 core/app/modules/import-export/runners/revert/templates.php create mode 100644 core/app/modules/kit-library/module.php create mode 100644 core/app/modules/onboarding/module.php create mode 100644 core/app/modules/site-editor/assets/js/atoms/indicator-bullet.js create mode 100644 core/app/modules/site-editor/assets/js/atoms/indicator-bullet.scss create mode 100644 core/app/modules/site-editor/assets/js/atoms/preview-iframe.js create mode 100644 core/app/modules/site-editor/assets/js/atoms/preview-iframe.scss create mode 100644 core/app/modules/site-editor/assets/js/context/base-context.js create mode 100644 core/app/modules/site-editor/assets/js/context/conditions.js create mode 100644 core/app/modules/site-editor/assets/js/context/models/condition.js create mode 100644 core/app/modules/site-editor/assets/js/context/services/conditions-config.js create mode 100644 core/app/modules/site-editor/assets/js/context/templates.js create mode 100644 core/app/modules/site-editor/assets/js/data/commands/conditions-config.js create mode 100644 core/app/modules/site-editor/assets/js/data/commands/index.js create mode 100644 core/app/modules/site-editor/assets/js/data/commands/templates-conditions-conflicts.js create mode 100644 core/app/modules/site-editor/assets/js/data/commands/templates-conditions.js create mode 100644 core/app/modules/site-editor/assets/js/data/commands/templates.js create mode 100644 core/app/modules/site-editor/assets/js/data/component.js create mode 100644 core/app/modules/site-editor/assets/js/editor.js create mode 100644 core/app/modules/site-editor/assets/js/hooks/use-templates-screenshot.js create mode 100644 core/app/modules/site-editor/assets/js/molecules/back-button.js create mode 100644 core/app/modules/site-editor/assets/js/molecules/back-button.scss create mode 100644 core/app/modules/site-editor/assets/js/molecules/site-template-body.js create mode 100644 core/app/modules/site-editor/assets/js/molecules/site-template-footer.js create mode 100644 core/app/modules/site-editor/assets/js/molecules/site-template-header.js create mode 100644 core/app/modules/site-editor/assets/js/molecules/site-template-thumbnail.js create mode 100644 core/app/modules/site-editor/assets/js/molecules/site-template.js create mode 100644 core/app/modules/site-editor/assets/js/molecules/site-template.scss create mode 100644 core/app/modules/site-editor/assets/js/organisms/site-templates.js create mode 100644 core/app/modules/site-editor/assets/js/pages/add-new.js create mode 100644 core/app/modules/site-editor/assets/js/pages/add-new.scss create mode 100644 core/app/modules/site-editor/assets/js/pages/conditions/condition-conflicts.js create mode 100644 core/app/modules/site-editor/assets/js/pages/conditions/condition-name.js create mode 100644 core/app/modules/site-editor/assets/js/pages/conditions/condition-sub-id.js create mode 100644 core/app/modules/site-editor/assets/js/pages/conditions/condition-sub.js create mode 100644 core/app/modules/site-editor/assets/js/pages/conditions/condition-type.js create mode 100644 core/app/modules/site-editor/assets/js/pages/conditions/conditions-api.scss create mode 100644 core/app/modules/site-editor/assets/js/pages/conditions/conditions-rows.js create mode 100644 core/app/modules/site-editor/assets/js/pages/conditions/conditions.js create mode 100644 core/app/modules/site-editor/assets/js/pages/conditions/conditions.scss create mode 100644 core/app/modules/site-editor/assets/js/pages/import.js create mode 100644 core/app/modules/site-editor/assets/js/pages/template-type.js create mode 100644 core/app/modules/site-editor/assets/js/pages/template-type.scss create mode 100644 core/app/modules/site-editor/assets/js/pages/templates.js create mode 100644 core/app/modules/site-editor/assets/js/part-actions/dialog-delete.js create mode 100644 core/app/modules/site-editor/assets/js/part-actions/dialog-rename.js create mode 100644 core/app/modules/site-editor/assets/js/part-actions/dialogs-and-buttons.js create mode 100644 core/app/modules/site-editor/assets/js/site-editor.js create mode 100644 core/app/modules/site-editor/assets/js/site-editor.scss create mode 100644 core/app/modules/site-editor/data/controller.php create mode 100644 core/app/modules/site-editor/data/endpoints/base-endpoint.php create mode 100644 core/app/modules/site-editor/data/endpoints/conditions-config.php create mode 100644 core/app/modules/site-editor/data/endpoints/template-types.php create mode 100644 core/app/modules/site-editor/data/endpoints/templates-conditions-conflicts.php create mode 100644 core/app/modules/site-editor/data/endpoints/templates-conditions.php create mode 100644 core/app/modules/site-editor/data/endpoints/templates.php create mode 100644 core/app/modules/site-editor/data/responses/lock-error-response.php create mode 100644 core/app/modules/site-editor/module.php create mode 100644 core/app/modules/site-editor/render-mode-template-preview.php create mode 100644 core/behaviors/feature-lock.php create mode 100644 core/behaviors/temp-lock-behavior.php create mode 100644 core/compatibility/compatibility.php create mode 100644 core/connect/apps/activate.php create mode 100644 core/connect/manager.php create mode 100644 core/database/base-database-updater.php create mode 100644 core/database/base-migration.php create mode 100644 core/database/join-clause.php create mode 100644 core/database/model-base.php create mode 100644 core/database/model-query-builder.php create mode 100644 core/database/query-builder.php create mode 100644 core/editor/editor.php create mode 100644 core/editor/notice-bar.php create mode 100644 core/editor/promotion.php create mode 100644 core/editor/template.php create mode 100644 core/integrations/actions/action-base.php create mode 100644 core/integrations/actions/email/email-address.php create mode 100644 core/integrations/actions/email/email-message.php create mode 100644 core/integrations/actions/email/email.php create mode 100644 core/integrations/exceptions/action-failed-exception.php create mode 100644 core/integrations/exceptions/action-validation-failed-exception.php create mode 100644 core/integrations/exceptions/exception-base.php create mode 100644 core/integrations/integrations-manager.php create mode 100644 core/isolation/wordpress-adapter-interface.php create mode 100644 core/isolation/wordpress-adapter.php create mode 100644 core/modules-manager.php create mode 100644 core/notifications/notification.php create mode 100644 core/notifications/notifications-manager.php create mode 100644 core/notifications/traits/notifiable.php create mode 100644 core/php-api.php create mode 100644 core/preview/preview.php create mode 100644 core/upgrade/manager.php create mode 100644 core/upgrade/upgrades.php create mode 100644 core/utils.php create mode 100644 core/utils/collection.php create mode 100644 core/utils/registrar.php create mode 100644 data/base/controller.php create mode 100644 data/http-status.php create mode 100644 elementor-pro.php create mode 100644 license.txt create mode 100644 license/admin.php create mode 100644 license/api.php create mode 100644 license/assets/js/admin.js create mode 100644 license/notices/trial-expired-notice.php create mode 100644 license/notices/trial-period-notice.php create mode 100644 license/updater.php create mode 100644 modules/admin-top-bar/module.php create mode 100644 modules/animated-headline/module.php create mode 100644 modules/animated-headline/widgets/animated-headline.php create mode 100644 modules/announcements/module.php create mode 100644 modules/announcements/triggers/is-license-expired.php create mode 100644 modules/assets-manager/asset-types/admin-menu-items/custom-fonts-menu-item.php create mode 100644 modules/assets-manager/asset-types/admin-menu-items/custom-fonts-promotion-menu-item.php create mode 100644 modules/assets-manager/asset-types/admin-menu-items/custom-icons-menu-item.php create mode 100644 modules/assets-manager/asset-types/admin-menu-items/custom-icons-promotion-menu-item.php create mode 100644 modules/assets-manager/asset-types/fonts-manager.php create mode 100644 modules/assets-manager/asset-types/fonts/custom-fonts.php create mode 100644 modules/assets-manager/asset-types/fonts/typekit-fonts.php create mode 100644 modules/assets-manager/asset-types/icons-manager.php create mode 100644 modules/assets-manager/asset-types/icons/custom-icons.php create mode 100644 modules/assets-manager/asset-types/icons/font-awesome-pro.php create mode 100644 modules/assets-manager/asset-types/icons/icon-sets/fontastic.php create mode 100644 modules/assets-manager/asset-types/icons/icon-sets/fontello.php create mode 100644 modules/assets-manager/asset-types/icons/icon-sets/icomoon.php create mode 100644 modules/assets-manager/asset-types/icons/icon-sets/icon-set-base.php create mode 100644 modules/assets-manager/asset-types/icons/templates.php create mode 100644 modules/assets-manager/classes/assets-base.php create mode 100644 modules/assets-manager/classes/font-base.php create mode 100644 modules/assets-manager/module.php create mode 100644 modules/blockquote/module.php create mode 100644 modules/blockquote/widgets/blockquote.php create mode 100644 modules/call-to-action/module.php create mode 100644 modules/call-to-action/widgets/call-to-action.php create mode 100644 modules/carousel/module.php create mode 100644 modules/carousel/widgets/base.php create mode 100644 modules/carousel/widgets/media-carousel.php create mode 100644 modules/carousel/widgets/reviews.php create mode 100644 modules/carousel/widgets/testimonial-carousel.php create mode 100644 modules/code-highlight/module.php create mode 100644 modules/code-highlight/widgets/code-highlight.php create mode 100644 modules/compatibility-tag/compatibility-tag-component.php create mode 100644 modules/compatibility-tag/module.php create mode 100644 modules/countdown/module.php create mode 100644 modules/countdown/widgets/countdown.php create mode 100644 modules/custom-attributes/module.php create mode 100644 modules/custom-code/admin-menu-items/custom-code-menu-item.php create mode 100644 modules/custom-code/admin-menu-items/custom-code-promotion-menu-item.php create mode 100644 modules/custom-code/custom-code-metabox.php create mode 100644 modules/custom-code/document.php create mode 100644 modules/custom-code/module.php create mode 100644 modules/custom-css/admin-menu-items/settings-custom-css-pro.php create mode 100644 modules/custom-css/module.php create mode 100644 modules/display-conditions/classes/and-condition.php create mode 100644 modules/display-conditions/classes/cache-notice.php create mode 100644 modules/display-conditions/classes/comparator-provider.php create mode 100644 modules/display-conditions/classes/comparators-checker.php create mode 100644 modules/display-conditions/classes/conditions-manager.php create mode 100644 modules/display-conditions/classes/dynamic-tags/custom-fields-data-provider.php create mode 100644 modules/display-conditions/classes/dynamic-tags/data-provider.php create mode 100644 modules/display-conditions/classes/dynamic-tags/dynamic-tags-data-provider.php create mode 100644 modules/display-conditions/classes/experiments.php create mode 100644 modules/display-conditions/classes/or-condition.php create mode 100644 modules/display-conditions/conditions/archive-of-author-condition.php create mode 100644 modules/display-conditions/conditions/archive-of-category-condition.php create mode 100644 modules/display-conditions/conditions/archive-of-tag-condition.php create mode 100644 modules/display-conditions/conditions/base/archive-condition-base.php create mode 100644 modules/display-conditions/conditions/base/condition-base.php create mode 100644 modules/display-conditions/conditions/base/date-condition-base.php create mode 100644 modules/display-conditions/conditions/base/title-condition-base.php create mode 100644 modules/display-conditions/conditions/current-date-condition.php create mode 100644 modules/display-conditions/conditions/date-of-modification-condition.php create mode 100644 modules/display-conditions/conditions/date-of-publish-condition.php create mode 100644 modules/display-conditions/conditions/day-of-the-week-condition.php create mode 100644 modules/display-conditions/conditions/dynamic-tags-condition.php create mode 100644 modules/display-conditions/conditions/featured-image-condition.php create mode 100644 modules/display-conditions/conditions/from-url-condition.php create mode 100644 modules/display-conditions/conditions/in-categories-condition.php create mode 100644 modules/display-conditions/conditions/in-tags-condition.php create mode 100644 modules/display-conditions/conditions/login-status-condition.php create mode 100644 modules/display-conditions/conditions/page-author-condition.php create mode 100644 modules/display-conditions/conditions/page-parent-condition.php create mode 100644 modules/display-conditions/conditions/page-title-condition.php create mode 100644 modules/display-conditions/conditions/post-author-condition.php create mode 100644 modules/display-conditions/conditions/post-number-of-comments-condition.php create mode 100644 modules/display-conditions/conditions/post-title-condition.php create mode 100644 modules/display-conditions/conditions/time-of-the-day-condition.php create mode 100644 modules/display-conditions/conditions/user-registration-date-condition.php create mode 100644 modules/display-conditions/conditions/user-role-condition.php create mode 100644 modules/display-conditions/module.php create mode 100644 modules/dynamic-tags/acf/dynamic-value-provider.php create mode 100644 modules/dynamic-tags/acf/module.php create mode 100644 modules/dynamic-tags/acf/tags/acf-color.php create mode 100644 modules/dynamic-tags/acf/tags/acf-date-time.php create mode 100644 modules/dynamic-tags/acf/tags/acf-file.php create mode 100644 modules/dynamic-tags/acf/tags/acf-gallery.php create mode 100644 modules/dynamic-tags/acf/tags/acf-image.php create mode 100644 modules/dynamic-tags/acf/tags/acf-number.php create mode 100644 modules/dynamic-tags/acf/tags/acf-text.php create mode 100644 modules/dynamic-tags/acf/tags/acf-url.php create mode 100644 modules/dynamic-tags/components/author-meta-filter.php create mode 100644 modules/dynamic-tags/module.php create mode 100644 modules/dynamic-tags/pods/dynamic-value-provider.php create mode 100644 modules/dynamic-tags/pods/module.php create mode 100644 modules/dynamic-tags/pods/tags/pods-base.php create mode 100644 modules/dynamic-tags/pods/tags/pods-date-time.php create mode 100644 modules/dynamic-tags/pods/tags/pods-date.php create mode 100644 modules/dynamic-tags/pods/tags/pods-gallery.php create mode 100644 modules/dynamic-tags/pods/tags/pods-image.php create mode 100644 modules/dynamic-tags/pods/tags/pods-numeric.php create mode 100644 modules/dynamic-tags/pods/tags/pods-text.php create mode 100644 modules/dynamic-tags/pods/tags/pods-url.php create mode 100644 modules/dynamic-tags/tags/archive-description.php create mode 100644 modules/dynamic-tags/tags/archive-meta.php create mode 100644 modules/dynamic-tags/tags/archive-title.php create mode 100644 modules/dynamic-tags/tags/archive-url.php create mode 100644 modules/dynamic-tags/tags/author-info.php create mode 100644 modules/dynamic-tags/tags/author-meta.php create mode 100644 modules/dynamic-tags/tags/author-name.php create mode 100644 modules/dynamic-tags/tags/author-profile-picture.php create mode 100644 modules/dynamic-tags/tags/author-url.php create mode 100644 modules/dynamic-tags/tags/base/author-tag.php create mode 100644 modules/dynamic-tags/tags/base/data-tag.php create mode 100644 modules/dynamic-tags/tags/base/tag-trait.php create mode 100644 modules/dynamic-tags/tags/base/tag.php create mode 100644 modules/dynamic-tags/tags/comments-number.php create mode 100644 modules/dynamic-tags/tags/comments-url.php create mode 100644 modules/dynamic-tags/tags/contact-url.php create mode 100644 modules/dynamic-tags/tags/current-date-time.php create mode 100644 modules/dynamic-tags/tags/featured-image-data.php create mode 100644 modules/dynamic-tags/tags/internal-url.php create mode 100644 modules/dynamic-tags/tags/lightbox.php create mode 100644 modules/dynamic-tags/tags/page-title.php create mode 100644 modules/dynamic-tags/tags/post-custom-field.php create mode 100644 modules/dynamic-tags/tags/post-date.php create mode 100644 modules/dynamic-tags/tags/post-excerpt.php create mode 100644 modules/dynamic-tags/tags/post-featured-image.php create mode 100644 modules/dynamic-tags/tags/post-gallery.php create mode 100644 modules/dynamic-tags/tags/post-id.php create mode 100644 modules/dynamic-tags/tags/post-terms.php create mode 100644 modules/dynamic-tags/tags/post-time.php create mode 100644 modules/dynamic-tags/tags/post-title.php create mode 100644 modules/dynamic-tags/tags/post-url.php create mode 100644 modules/dynamic-tags/tags/request-parameter.php create mode 100644 modules/dynamic-tags/tags/shortcode.php create mode 100644 modules/dynamic-tags/tags/site-logo.php create mode 100644 modules/dynamic-tags/tags/site-tagline.php create mode 100644 modules/dynamic-tags/tags/site-title.php create mode 100644 modules/dynamic-tags/tags/site-url.php create mode 100644 modules/dynamic-tags/tags/user-info.php create mode 100644 modules/dynamic-tags/tags/user-profile-picture.php create mode 100644 modules/dynamic-tags/toolset/module.php create mode 100644 modules/dynamic-tags/toolset/tags/toolset-base.php create mode 100644 modules/dynamic-tags/toolset/tags/toolset-date.php create mode 100644 modules/dynamic-tags/toolset/tags/toolset-gallery.php create mode 100644 modules/dynamic-tags/toolset/tags/toolset-image.php create mode 100644 modules/dynamic-tags/toolset/tags/toolset-text.php create mode 100644 modules/dynamic-tags/toolset/tags/toolset-url.php create mode 100644 modules/element-manager/module.php create mode 100644 modules/element-manager/options.php create mode 100644 modules/flip-box/module.php create mode 100644 modules/flip-box/widgets/flip-box.php create mode 100644 modules/forms/actions/activecampaign.php create mode 100644 modules/forms/actions/activity-log.php create mode 100644 modules/forms/actions/cf7db.php create mode 100644 modules/forms/actions/convertkit.php create mode 100644 modules/forms/actions/discord.php create mode 100644 modules/forms/actions/drip.php create mode 100644 modules/forms/actions/email.php create mode 100644 modules/forms/actions/email2.php create mode 100644 modules/forms/actions/getresponse.php create mode 100644 modules/forms/actions/mailchimp.php create mode 100644 modules/forms/actions/mailerlite.php create mode 100644 modules/forms/actions/mailpoet.php create mode 100644 modules/forms/actions/mailpoet3.php create mode 100644 modules/forms/actions/redirect.php create mode 100644 modules/forms/actions/slack.php create mode 100644 modules/forms/actions/webhook.php create mode 100644 modules/forms/classes/action-base.php create mode 100644 modules/forms/classes/activecampaign-handler.php create mode 100644 modules/forms/classes/ajax-handler.php create mode 100644 modules/forms/classes/akismet.php create mode 100644 modules/forms/classes/convertkit-handler.php create mode 100644 modules/forms/classes/drip-handler.php create mode 100644 modules/forms/classes/form-base.php create mode 100644 modules/forms/classes/form-record.php create mode 100644 modules/forms/classes/getresponse-handler.php create mode 100644 modules/forms/classes/honeypot-handler.php create mode 100644 modules/forms/classes/integration-base.php create mode 100644 modules/forms/classes/mailchimp-handler.php create mode 100644 modules/forms/classes/mailerlite-handler.php create mode 100644 modules/forms/classes/recaptcha-handler.php create mode 100644 modules/forms/classes/recaptcha-v3-handler.php create mode 100644 modules/forms/classes/rest-client.php create mode 100644 modules/forms/controls/fields-map.php create mode 100644 modules/forms/controls/fields-repeater.php create mode 100644 modules/forms/data/controller.php create mode 100644 modules/forms/fields/acceptance.php create mode 100644 modules/forms/fields/date.php create mode 100644 modules/forms/fields/field-base.php create mode 100644 modules/forms/fields/number.php create mode 100644 modules/forms/fields/step.php create mode 100644 modules/forms/fields/tel.php create mode 100644 modules/forms/fields/time.php create mode 100644 modules/forms/fields/upload.php create mode 100644 modules/forms/module.php create mode 100644 modules/forms/registrars/form-actions-registrar.php create mode 100644 modules/forms/registrars/form-fields-registrar.php create mode 100644 modules/forms/submissions/actions/save-to-database.php create mode 100644 modules/forms/submissions/admin-menu-items/submissions-menu-item.php create mode 100644 modules/forms/submissions/admin-menu-items/submissions-promotion-menu-item.php create mode 100644 modules/forms/submissions/component.php create mode 100644 modules/forms/submissions/data/controller.php create mode 100644 modules/forms/submissions/data/endpoints/export.php create mode 100644 modules/forms/submissions/data/endpoints/forms-index.php create mode 100644 modules/forms/submissions/data/endpoints/index.php create mode 100644 modules/forms/submissions/data/endpoints/referer.php create mode 100644 modules/forms/submissions/data/endpoints/restore.php create mode 100644 modules/forms/submissions/data/forms-controller.php create mode 100644 modules/forms/submissions/data/responses/query-failed-response.php create mode 100644 modules/forms/submissions/database/entities/form-snapshot.php create mode 100644 modules/forms/submissions/database/migration.php create mode 100644 modules/forms/submissions/database/migrations/base-migration.php create mode 100644 modules/forms/submissions/database/migrations/fix-indexes.php create mode 100644 modules/forms/submissions/database/migrations/initial.php create mode 100644 modules/forms/submissions/database/migrations/referer-extra.php create mode 100644 modules/forms/submissions/database/query.php create mode 100644 modules/forms/submissions/database/repositories/form-snapshot-repository.php create mode 100644 modules/forms/submissions/export/csv-export.php create mode 100644 modules/forms/submissions/personal-data.php create mode 100644 modules/forms/widgets/form.php create mode 100644 modules/forms/widgets/login.php create mode 100644 modules/gallery/module.php create mode 100644 modules/gallery/widgets/gallery.php create mode 100644 modules/global-widget/data/controller.php create mode 100644 modules/global-widget/documents/widget.php create mode 100644 modules/global-widget/module.php create mode 100644 modules/global-widget/views/panel-template.php create mode 100644 modules/global-widget/widgets/global-widget.php create mode 100644 modules/hotspot/module.php create mode 100644 modules/hotspot/widgets/hotspot.php create mode 100644 modules/library/classes/shortcode.php create mode 100644 modules/library/module.php create mode 100644 modules/library/widgets/template.php create mode 100644 modules/library/wp-widgets/elementor-library.php create mode 100644 modules/loop-builder/assets/images/loop-item.svg create mode 100644 modules/loop-builder/documents/loop.php create mode 100644 modules/loop-builder/files/css/loop-css-trait.php create mode 100644 modules/loop-builder/files/css/loop-dynamic-css.php create mode 100644 modules/loop-builder/files/css/loop-preview.php create mode 100644 modules/loop-builder/files/css/loop.php create mode 100644 modules/loop-builder/module.php create mode 100644 modules/loop-builder/providers/taxonomy-loop-provider.php create mode 100644 modules/loop-builder/skins/skin-loop-base.php create mode 100644 modules/loop-builder/skins/skin-loop-post-taxonomy.php create mode 100644 modules/loop-builder/skins/skin-loop-post.php create mode 100644 modules/loop-builder/skins/skin-loop-taxonomy-base.php create mode 100644 modules/loop-builder/traits/alternate-templates-trait.php create mode 100644 modules/loop-builder/views/cta-template.php create mode 100644 modules/loop-builder/widgets/base.php create mode 100644 modules/loop-builder/widgets/loop-carousel.php create mode 100644 modules/loop-builder/widgets/loop-grid.php create mode 100644 modules/loop-filter/data/controller.php create mode 100644 modules/loop-filter/data/endpoints/base.php create mode 100644 modules/loop-filter/data/endpoints/get-post-type-taxonomies.php create mode 100644 modules/loop-filter/data/endpoints/refresh-loop.php create mode 100644 modules/loop-filter/data/interfaces/endpoint.php create mode 100644 modules/loop-filter/module.php create mode 100644 modules/loop-filter/query/data/query-constants.php create mode 100644 modules/loop-filter/query/interfaces/query-interface.php create mode 100644 modules/loop-filter/query/query-types/hierarchy-and-query.php create mode 100644 modules/loop-filter/query/query-types/hierarchy-or-query.php create mode 100644 modules/loop-filter/query/query-types/single-terms-query.php create mode 100644 modules/loop-filter/query/taxonomy-manager.php create mode 100644 modules/loop-filter/query/taxonomy-query-builder.php create mode 100644 modules/loop-filter/traits/hierarchical-taxonomy-trait.php create mode 100644 modules/loop-filter/traits/taxonomy-filter-trait.php create mode 100644 modules/loop-filter/widgets/taxonomy-filter.php create mode 100644 modules/lottie/assets/animations/default.json create mode 100644 modules/lottie/module.php create mode 100644 modules/lottie/widgets/lottie.php create mode 100644 modules/mega-menu/module.php create mode 100644 modules/mega-menu/traits/url-helper-trait.php create mode 100644 modules/mega-menu/widgets/mega-menu.php create mode 100644 modules/motion-fx/controls-group.php create mode 100644 modules/motion-fx/module.php create mode 100644 modules/nav-menu/module.php create mode 100644 modules/nav-menu/widgets/nav-menu.php create mode 100644 modules/nested-carousel/module.php create mode 100644 modules/nested-carousel/widgets/nested-carousel.php create mode 100644 modules/notes/admin-bar.php create mode 100644 modules/notes/admin-page.php create mode 100644 modules/notes/assets/images/elementor-logo-orange.png create mode 100644 modules/notes/data/controller.php create mode 100644 modules/notes/data/endpoints/read-status-endpoint.php create mode 100644 modules/notes/data/endpoints/summary-endpoint.php create mode 100644 modules/notes/data/endpoints/users-endpoint.php create mode 100644 modules/notes/database/migrations/add-author-display-name.php create mode 100644 modules/notes/database/migrations/add-capabilities.php create mode 100644 modules/notes/database/migrations/add-note-position.php create mode 100644 modules/notes/database/migrations/add-route-post-id.php create mode 100644 modules/notes/database/migrations/initial.php create mode 100644 modules/notes/database/models/document.php create mode 100644 modules/notes/database/models/note-summary.php create mode 100644 modules/notes/database/models/note.php create mode 100644 modules/notes/database/models/user.php create mode 100644 modules/notes/database/notes-database-updater.php create mode 100644 modules/notes/database/query/note-query-builder.php create mode 100644 modules/notes/database/query/user-query-builder.php create mode 100644 modules/notes/database/transformers/user-transformer.php create mode 100644 modules/notes/document-events.php create mode 100644 modules/notes/module.php create mode 100644 modules/notes/notifications/base-notes-notification.php create mode 100644 modules/notes/notifications/user-mentioned-notification.php create mode 100644 modules/notes/notifications/user-replied-notification.php create mode 100644 modules/notes/notifications/user-resolved-notification.php create mode 100644 modules/notes/notifications/views/email.php create mode 100644 modules/notes/usage.php create mode 100644 modules/notes/user/capabilities.php create mode 100644 modules/notes/user/delete-user.php create mode 100644 modules/notes/user/personal-data.php create mode 100644 modules/notes/user/preferences.php create mode 100644 modules/notes/utils.php create mode 100644 modules/page-transitions/module.php create mode 100644 modules/payments/classes/payment-button.php create mode 100644 modules/payments/classes/stripe-handler.php create mode 100644 modules/payments/module.php create mode 100644 modules/payments/widgets/paypal-button.php create mode 100644 modules/payments/widgets/stripe-button.php create mode 100644 modules/popup/admin-menu-items/popups-menu-item.php create mode 100644 modules/popup/admin-menu-items/popups-promotion-menu-item.php create mode 100644 modules/popup/assets/images/timing-tab.svg create mode 100644 modules/popup/assets/images/timing/browsers.svg create mode 100644 modules/popup/assets/images/timing/devices.svg create mode 100644 modules/popup/assets/images/timing/logged_in.svg create mode 100644 modules/popup/assets/images/timing/page_views.svg create mode 100644 modules/popup/assets/images/timing/schedule.svg create mode 100644 modules/popup/assets/images/timing/sessions.svg create mode 100644 modules/popup/assets/images/timing/sources.svg create mode 100644 modules/popup/assets/images/timing/times.svg create mode 100644 modules/popup/assets/images/timing/url.svg create mode 100644 modules/popup/assets/images/triggers-tab.svg create mode 100644 modules/popup/assets/images/triggers/click.svg create mode 100644 modules/popup/assets/images/triggers/exit_intent.svg create mode 100644 modules/popup/assets/images/triggers/inactivity.svg create mode 100644 modules/popup/assets/images/triggers/page_load.svg create mode 100644 modules/popup/assets/images/triggers/scrolling.svg create mode 100644 modules/popup/assets/images/triggers/scrolling_to.svg create mode 100644 modules/popup/display-settings/base.php create mode 100644 modules/popup/display-settings/timing.php create mode 100644 modules/popup/display-settings/triggers.php create mode 100644 modules/popup/document.php create mode 100644 modules/popup/form-action.php create mode 100644 modules/popup/module.php create mode 100644 modules/popup/tag.php create mode 100644 modules/posts/data/controller.php create mode 100644 modules/posts/module.php create mode 100644 modules/posts/skins/skin-base.php create mode 100644 modules/posts/skins/skin-cards.php create mode 100644 modules/posts/skins/skin-classic.php create mode 100644 modules/posts/skins/skin-content-base.php create mode 100644 modules/posts/skins/skin-full-content.php create mode 100644 modules/posts/traits/button-widget-trait.php create mode 100644 modules/posts/traits/pagination-trait.php create mode 100644 modules/posts/widgets/portfolio.php create mode 100644 modules/posts/widgets/posts-base.php create mode 100644 modules/posts/widgets/posts.php create mode 100644 modules/pricing/module.php create mode 100644 modules/pricing/widgets/price-list.php create mode 100644 modules/pricing/widgets/price-table.php create mode 100644 modules/progress-tracker/module.php create mode 100644 modules/progress-tracker/widgets/progress-tracker.php create mode 100644 modules/query-control/classes/elementor-post-query.php create mode 100644 modules/query-control/classes/elementor-related-query.php create mode 100644 modules/query-control/controls/group-control-posts.php create mode 100644 modules/query-control/controls/group-control-query.php create mode 100644 modules/query-control/controls/group-control-related.php create mode 100644 modules/query-control/controls/group-control-taxonomy.php create mode 100644 modules/query-control/controls/query.php create mode 100644 modules/query-control/controls/template-query.php create mode 100644 modules/query-control/module.php create mode 100644 modules/role-manager/module.php create mode 100644 modules/screenshots/module.php create mode 100644 modules/screenshots/render-mode-screenshot.php create mode 100644 modules/screenshots/screenshot.php create mode 100644 modules/scroll-snap/module.php create mode 100644 modules/share-buttons/module.php create mode 100644 modules/share-buttons/widgets/share-buttons.php create mode 100644 modules/slides/module.php create mode 100644 modules/slides/widgets/slides.php create mode 100644 modules/social/classes/facebook-sdk-manager.php create mode 100644 modules/social/module.php create mode 100644 modules/social/widgets/facebook-button.php create mode 100644 modules/social/widgets/facebook-comments.php create mode 100644 modules/social/widgets/facebook-embed.php create mode 100644 modules/social/widgets/facebook-page.php create mode 100644 modules/sticky/module.php create mode 100644 modules/table-of-contents/module.php create mode 100644 modules/table-of-contents/widgets/table-of-contents.php create mode 100644 modules/theme-builder/admin-menu-items/theme-builder-menu-item.php create mode 100644 modules/theme-builder/api.php create mode 100644 modules/theme-builder/assets/images/conditions-tab.svg create mode 100644 modules/theme-builder/classes/conditions-cache.php create mode 100644 modules/theme-builder/classes/conditions-manager.php create mode 100644 modules/theme-builder/classes/conditions-repeater.php create mode 100644 modules/theme-builder/classes/control-media-preview.php create mode 100644 modules/theme-builder/classes/locations-manager.php create mode 100644 modules/theme-builder/classes/preview-manager.php create mode 100644 modules/theme-builder/classes/template-conditions.php create mode 100644 modules/theme-builder/classes/templates-types-manager.php create mode 100644 modules/theme-builder/classes/theme-support.php create mode 100644 modules/theme-builder/conditions/any-child-of-term.php create mode 100644 modules/theme-builder/conditions/any-child-of.php create mode 100644 modules/theme-builder/conditions/archive.php create mode 100644 modules/theme-builder/conditions/author.php create mode 100644 modules/theme-builder/conditions/by-author.php create mode 100644 modules/theme-builder/conditions/child-of-term.php create mode 100644 modules/theme-builder/conditions/child-of.php create mode 100644 modules/theme-builder/conditions/condition-base.php create mode 100644 modules/theme-builder/conditions/date.php create mode 100644 modules/theme-builder/conditions/front-page.php create mode 100644 modules/theme-builder/conditions/general.php create mode 100644 modules/theme-builder/conditions/in-sub-term.php create mode 100644 modules/theme-builder/conditions/in-taxonomy.php create mode 100644 modules/theme-builder/conditions/not-found404.php create mode 100644 modules/theme-builder/conditions/post-type-archive.php create mode 100644 modules/theme-builder/conditions/post-type-by-author.php create mode 100644 modules/theme-builder/conditions/post.php create mode 100644 modules/theme-builder/conditions/search.php create mode 100644 modules/theme-builder/conditions/singular.php create mode 100644 modules/theme-builder/conditions/taxonomy.php create mode 100644 modules/theme-builder/documents/archive-single-base.php create mode 100644 modules/theme-builder/documents/archive.php create mode 100644 modules/theme-builder/documents/error-404.php create mode 100644 modules/theme-builder/documents/footer.php create mode 100644 modules/theme-builder/documents/header-footer-base.php create mode 100644 modules/theme-builder/documents/header.php create mode 100644 modules/theme-builder/documents/search-results.php create mode 100644 modules/theme-builder/documents/section.php create mode 100644 modules/theme-builder/documents/single-base.php create mode 100644 modules/theme-builder/documents/single-page.php create mode 100644 modules/theme-builder/documents/single-post.php create mode 100644 modules/theme-builder/documents/single.php create mode 100644 modules/theme-builder/documents/theme-document.php create mode 100644 modules/theme-builder/documents/theme-page-document.php create mode 100644 modules/theme-builder/documents/theme-section-document.php create mode 100644 modules/theme-builder/module.php create mode 100644 modules/theme-builder/skins/post-comments-skin-classic.php create mode 100644 modules/theme-builder/skins/posts-archive-skin-base.php create mode 100644 modules/theme-builder/skins/posts-archive-skin-cards.php create mode 100644 modules/theme-builder/skins/posts-archive-skin-classic.php create mode 100644 modules/theme-builder/skins/posts-archive-skin-full-content.php create mode 100644 modules/theme-builder/theme-support/generate-press-theme-support.php create mode 100644 modules/theme-builder/theme-support/safe-mode-theme-support.php create mode 100644 modules/theme-builder/views/comments-template.php create mode 100644 modules/theme-builder/views/panel-template.php create mode 100644 modules/theme-builder/views/theme-support-footer.php create mode 100644 modules/theme-builder/views/theme-support-header.php create mode 100644 modules/theme-builder/widgets/archive-posts.php create mode 100644 modules/theme-builder/widgets/archive-title.php create mode 100644 modules/theme-builder/widgets/page-title.php create mode 100644 modules/theme-builder/widgets/post-content.php create mode 100644 modules/theme-builder/widgets/post-excerpt.php create mode 100644 modules/theme-builder/widgets/post-featured-image.php create mode 100644 modules/theme-builder/widgets/post-title.php create mode 100644 modules/theme-builder/widgets/site-logo.php create mode 100644 modules/theme-builder/widgets/site-title.php create mode 100644 modules/theme-builder/widgets/title-widget-base.php create mode 100644 modules/theme-elements/module.php create mode 100644 modules/theme-elements/widgets/author-box.php create mode 100644 modules/theme-elements/widgets/base.php create mode 100644 modules/theme-elements/widgets/breadcrumbs.php create mode 100644 modules/theme-elements/widgets/post-comments.php create mode 100644 modules/theme-elements/widgets/post-info.php create mode 100644 modules/theme-elements/widgets/post-navigation.php create mode 100644 modules/theme-elements/widgets/search-form.php create mode 100644 modules/theme-elements/widgets/sitemap.php create mode 100644 modules/tiers/admin-menu-items/base-promotion-item.php create mode 100644 modules/tiers/admin-menu-items/base-promotion-template.php create mode 100644 modules/tiers/module.php create mode 100644 modules/usage/features-reporter.php create mode 100644 modules/usage/integrations-reporter.php create mode 100644 modules/usage/module.php create mode 100644 modules/video-playlist/module.php create mode 100644 modules/video-playlist/widgets/video-playlist.php create mode 100644 modules/woocommerce/classes/base-products-renderer.php create mode 100644 modules/woocommerce/classes/current-query-renderer.php create mode 100644 modules/woocommerce/classes/products-renderer.php create mode 100644 modules/woocommerce/conditions/product-archive.php create mode 100644 modules/woocommerce/conditions/product-search.php create mode 100644 modules/woocommerce/conditions/shop-page.php create mode 100644 modules/woocommerce/conditions/woocommerce.php create mode 100644 modules/woocommerce/data/controller.php create mode 100644 modules/woocommerce/data/endpoints/base-endpoint.php create mode 100644 modules/woocommerce/data/endpoints/get-notice-dismissed.php create mode 100644 modules/woocommerce/data/endpoints/set-notice-dismissed.php create mode 100644 modules/woocommerce/documents/product-archive.php create mode 100644 modules/woocommerce/documents/product-post.php create mode 100644 modules/woocommerce/documents/product.php create mode 100644 modules/woocommerce/module.php create mode 100644 modules/woocommerce/settings/settings-woocommerce.php create mode 100644 modules/woocommerce/skins/skin-classic.php create mode 100644 modules/woocommerce/skins/skin-loop-product-taxonomy.php create mode 100644 modules/woocommerce/skins/skin-loop-product.php create mode 100644 modules/woocommerce/tags/base-data-tag.php create mode 100644 modules/woocommerce/tags/base-tag.php create mode 100644 modules/woocommerce/tags/category-image.php create mode 100644 modules/woocommerce/tags/product-content.php create mode 100644 modules/woocommerce/tags/product-gallery.php create mode 100644 modules/woocommerce/tags/product-image.php create mode 100644 modules/woocommerce/tags/product-price.php create mode 100644 modules/woocommerce/tags/product-rating.php create mode 100644 modules/woocommerce/tags/product-sale.php create mode 100644 modules/woocommerce/tags/product-short-description.php create mode 100644 modules/woocommerce/tags/product-sku.php create mode 100644 modules/woocommerce/tags/product-stock.php create mode 100644 modules/woocommerce/tags/product-terms.php create mode 100644 modules/woocommerce/tags/product-title.php create mode 100644 modules/woocommerce/tags/traits/tag-product-id.php create mode 100644 modules/woocommerce/tags/woocommerce-add-to-cart.php create mode 100644 modules/woocommerce/traits/product-id-trait.php create mode 100644 modules/woocommerce/traits/products-trait.php create mode 100644 modules/woocommerce/wc-templates/cart/mini-cart.php create mode 100644 modules/woocommerce/widgets/add-to-cart.php create mode 100644 modules/woocommerce/widgets/archive-description.php create mode 100644 modules/woocommerce/widgets/archive-products-deprecated.php create mode 100644 modules/woocommerce/widgets/archive-products.php create mode 100644 modules/woocommerce/widgets/base-widget.php create mode 100644 modules/woocommerce/widgets/breadcrumb.php create mode 100644 modules/woocommerce/widgets/cart.php create mode 100644 modules/woocommerce/widgets/categories.php create mode 100644 modules/woocommerce/widgets/category-image.php create mode 100644 modules/woocommerce/widgets/checkout.php create mode 100644 modules/woocommerce/widgets/elements.php create mode 100644 modules/woocommerce/widgets/menu-cart.php create mode 100644 modules/woocommerce/widgets/my-account.php create mode 100644 modules/woocommerce/widgets/notices.php create mode 100644 modules/woocommerce/widgets/product-add-to-cart.php create mode 100644 modules/woocommerce/widgets/product-additional-information.php create mode 100644 modules/woocommerce/widgets/product-content.php create mode 100644 modules/woocommerce/widgets/product-data-tabs.php create mode 100644 modules/woocommerce/widgets/product-images.php create mode 100644 modules/woocommerce/widgets/product-meta.php create mode 100644 modules/woocommerce/widgets/product-price.php create mode 100644 modules/woocommerce/widgets/product-rating.php create mode 100644 modules/woocommerce/widgets/product-related.php create mode 100644 modules/woocommerce/widgets/product-short-description.php create mode 100644 modules/woocommerce/widgets/product-stock.php create mode 100644 modules/woocommerce/widgets/product-title.php create mode 100644 modules/woocommerce/widgets/product-upsell.php create mode 100644 modules/woocommerce/widgets/products-base.php create mode 100644 modules/woocommerce/widgets/products-deprecated.php create mode 100644 modules/woocommerce/widgets/products.php create mode 100644 modules/woocommerce/widgets/purchase-summary.php create mode 100644 modules/woocommerce/widgets/single-elements.php create mode 100644 modules/wp-cli/license-command.php create mode 100644 modules/wp-cli/module.php create mode 100644 modules/wp-cli/theme-builder.php create mode 100644 modules/wp-cli/update.php create mode 100644 plugin.php create mode 100644 readme.txt create mode 100644 run-on-linux.js create mode 100644 sample-data/acf-extra-post-fields.json create mode 100644 sample-data/acf-fields-products.json create mode 100644 sample-data/sample_products_with_acf_meta.xml create mode 100644 sample-data/simple-taxonomy-loop-template.json diff --git a/Elementor Pro 3.21.2 b/Elementor Pro 3.21.2 new file mode 160000 index 0000000..92179c6 --- /dev/null +++ b/Elementor Pro 3.21.2 @@ -0,0 +1 @@ +Subproject commit 92179c680c9ef542a6307e9d1a44f3ada48f3de1 diff --git a/assets/css/admin-rtl.css b/assets/css/admin-rtl.css new file mode 100644 index 0000000..7bd73a7 --- /dev/null +++ b/assets/css/admin-rtl.css @@ -0,0 +1,610 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.wrap.elementor-admin-page-license form.elementor-license-box { + max-width: 600px; + background: white; + margin: 20px 0; + padding: 20px 20px; +} +.wrap.elementor-admin-page-license form.elementor-license-box h3 { + display: flex; + justify-content: space-between; + align-items: center; + margin: 0; + padding: 0; + padding-block-end: 20px; + border-block-end: 1px solid #eee; +} +.wrap.elementor-admin-page-license form.elementor-license-box h3 span { + flex-grow: 1; + padding-inline-start: 5px; +} +.wrap.elementor-admin-page-license form.elementor-license-box h3 small { + font-size: 13px; + font-weight: normal; +} +.wrap.elementor-admin-page-license form.elementor-license-box label { + display: block; + font-size: 1.3em; + font-weight: 600; + margin: 1em 0; +} +.wrap.elementor-admin-page-license form.elementor-license-box .button { + height: 30px; + margin-inline-start: 15px; + margin-block-end: 0; +} +.wrap.elementor-admin-page-license form.elementor-license-box p.description { + margin: 10px 0; +} +.wrap.elementor-admin-page-license form.elementor-license-box .e-row-stretch { + display: flex; + align-items: center; + justify-content: space-between; +} +.wrap.elementor-admin-page-license form.elementor-license-box .e-row-divider-bottom { + padding-block-end: 15px; + border-block-end: 1px solid #eeeeee; +} +.wrap.elementor-admin-page-license .elementor-box-action { + display: flex; + justify-content: flex-end; + align-items: flex-end; + margin-block-start: 30px; +} +.wrap.elementor-admin-page-license .elementor-box-action .elementor-manually-link { + color: #72777c; + margin-inline-end: 15px; +} +.wrap.elementor-admin-page-license .elementor-box-action .elementor-manually-link:hover { + color: inherit; +} + +#adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link] { + font-weight: 600; + background-color: #93003c; + color: #ffffff; + margin: 3px 10px 0; + display: block; + text-align: center; + border-radius: 3px; + transition: all 0.3s; +} +#adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link]:hover, #adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link]:focus { + background-color: #c60051; + box-shadow: none; +} + +.fixed .column-elementor_library_type, +.fixed .column-instances { + width: 10%; +} +.fixed .elementor-shortcode-input { + min-width: 235px; +} +@media (min-width: 768px) and (max-width: 1440px) { + .fixed .column-shortcode { + width: 25%; + } + .fixed .elementor-shortcode-input { + min-width: 100%; + } +} + +#available-widgets [class*=elementor-template] .widget-title:before { + content: "\e801"; + font-family: eicon; + font-size: 17px; +} + +#elementor-widget-template-empty-templates { + margin-block-start: 15px; + text-align: center; +} + +.elementor-widget-template-empty-templates-title { + padding: 25px 0 30px; +} + +.elementor-widget-template-empty-templates-icon { + font-size: 96px; +} + +.elementor-widget-template-empty-templates-footer { + color: var(--e-a-color-txt-muted); + font-size: 13px; + font-style: italic; + margin-block-end: 15px; +} + +.elementor-button-spinner.error:before { + content: "\f335"; + color: #ff0000; +} + +span.font-variations-count { + display: inline-block; + vertical-align: top; + margin: 1px 0 0 5px; + padding: 0 5px; + min-width: 7px; + height: 17px; + border-radius: 11px; + background-color: #d4dffb; + color: #4278b2; + font-size: 9px; + line-height: 17px; + text-align: center; + z-index: 26; +} + +.post-type-elementor_font div#elementor-font-custommetabox { + background: none; + border: 0; +} +.post-type-elementor_font div#elementor-font-custommetabox button.handlediv { + display: none; +} +.post-type-elementor_font div#elementor-font-custommetabox #poststuff .inside { + margin: 0; + padding: 0; +} +.post-type-elementor_font div#elementor-font-custommetabox h2.hndle { + padding: 0; +} +.post-type-elementor_font div#elementor-font-custommetabox .handle-actions { + display: none; +} +.post-type-elementor_font #tagsdiv-elementor_font_type, +.post-type-elementor_font #minor-publishing-actions, +.post-type-elementor_font #misc-publishing-actions { + display: none; +} + +.elementor-metabox-content .repeater-block { + background: #ffffff; + color: #3f444b; + padding: 20px; + margin-block-end: 2px; +} +.elementor-metabox-content .repeater-block span.elementor-repeater-tool-btn.close-repeater-row { + display: none; +} +.elementor-metabox-content .repeater-block.block-visible { + padding-block-end: 0; + margin-block-end: 0; +} +.elementor-metabox-content .repeater-block.block-visible span.elementor-repeater-tool-btn.toggle-repeater-row { + display: none; +} +.elementor-metabox-content .repeater-block.block-visible span.elementor-repeater-tool-btn.close-repeater-row { + display: inline-block; +} +.elementor-metabox-content .repeater-block:not(.block-visible) .close-repeater-row { + display: none; +} +.elementor-metabox-content .repeater-block .repeater-title { + cursor: pointer; +} +.elementor-metabox-content .row-font-label { + padding: 0; + margin: 0; + display: flex; + text-transform: capitalize; +} +.elementor-metabox-content .row-font-label li { + box-sizing: border-box; + flex-grow: 1; + width: 100%; + margin: 0; +} +.elementor-metabox-content .row-font-label li span.label { + font-weight: 500; + padding-inline-end: 10px; +} +.elementor-metabox-content .row-font-label li.row-font-weight, .elementor-metabox-content .row-font-label li.row-font-style { + max-width: 180px; +} +.elementor-metabox-content .row-font-label li.row-font-actions { + max-width: 200px; + text-align: end; +} +.elementor-metabox-content .repeater-content { + margin: 0; +} +.elementor-metabox-content .repeater-content .repeater-content-top { + display: flex; + margin-block-end: 20px; + line-height: 28px; +} +.elementor-metabox-content .repeater-content .repeater-content-top > div { + box-sizing: border-box; + flex-grow: 1; +} +.elementor-metabox-content .repeater-content .repeater-content-top p { + margin: 0; + display: inline-block; +} +.elementor-metabox-content .repeater-content .repeater-content-top p label { + font-weight: 500; + padding-inline-end: 10px; +} +.elementor-metabox-content .repeater-content .repeater-content-top .elementor-field-select { + max-width: 180px; +} +.elementor-metabox-content .repeater-content .repeater-content-top .elementor-field-toolbar { + max-width: 200px; + text-align: end; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom { + background-color: #F9FAFA; + padding: 20px 40px; + margin: 0 -20px; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field { + display: flex; + align-items: center; + background-color: #ffffff; + padding: 10px 20px; + margin-block-end: 10px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.05); +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field:last-child { + margin-block-end: 0; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field p, +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field input { + box-sizing: border-box; + flex-grow: 1; + width: 100%; + margin: 0; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field p.elementor-field-label { + font-weight: 500; + max-width: 120px; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input { + padding: 5px 8px; + margin: 0 15px; + border-radius: 3px; + font-size: 12px; + width: 100%; + background: none; + box-shadow: none; + color: #0C0D0E; + border: 1px solid; + outline: none; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input:not(:focus) { + border-color: #D5D8DC; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input:focus { + border-color: #9DA5AE; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-btn, +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn { + max-width: 100px; + font-size: 11px; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn { + background-color: #F1F2F3; + color: #9DA5AE; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn:hover { + background-color: #F59E0B; + color: white; +} +.elementor-metabox-content .elementor-button { + background-color: #9DA5AE; + color: #fff; + line-height: 1; + text-transform: uppercase; + height: auto; + padding: 10px 20px; + outline: none; + border: none; + transition-property: background, color, box-shadow, opacity; + transition-duration: 0.3s; +} +.elementor-metabox-content .elementor-button:hover, .elementor-metabox-content .elementor-button:focus, .elementor-metabox-content .elementor-button:visited { + color: #fff; +} +.elementor-metabox-content .elementor-button:focus, .elementor-metabox-content .elementor-button:visited { + background-color: #9DA5AE; +} +.elementor-metabox-content .elementor-button:hover { + background-color: #3f444b; + box-shadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 2px rgba(0, 0, 0, 0.2); + border: none; +} +.elementor-metabox-content .elementor-button:active { + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.19), 0 3px 3px rgba(0, 0, 0, 0.1); +} +.elementor-metabox-content .elementor-button:not([disabled]) { + cursor: pointer; +} +.elementor-metabox-content .elementor-button.elementor-size-xs { + font-size: 11px; + padding: 10px 20px; + border-radius: 2px; +} +.elementor-metabox-content .elementor-button.elementor-size-sm { + font-size: 13px; + padding: 12px 24px; + border-radius: 3px; +} +.elementor-metabox-content .elementor-button.elementor-size-md { + font-size: 14px; + padding: 15px 30px; + border-radius: 4px; +} +.elementor-metabox-content .elementor-button.elementor-size-lg { + font-size: 15px; + padding: 20px 40px; + border-radius: 5px; +} +.elementor-metabox-content .elementor-button.elementor-size-xl { + font-size: 18px; + padding: 25px 50px; + border-radius: 6px; +} +.elementor-metabox-content .elementor-button .elementor-align-icon-right { + float: right; + margin-left: 5px; +} +.elementor-metabox-content .elementor-button .elementor-align-icon-left { + float: left; + margin-right: 5px; +} +.elementor-metabox-content input.button.add-repeater-row { + margin-block-start: 18px; + border: none; + box-shadow: none; +} +.elementor-metabox-content .elementor-repeater-tool-btn { + color: #9DA5AE; + cursor: pointer; + padding: 0 20px; + font-size: 12px; + transition: all 0.3s; +} +.elementor-metabox-content .elementor-repeater-tool-btn i { + padding-inline-end: 5px; +} +.elementor-metabox-content .elementor-repeater-tool-btn:hover { + color: #3f444b; +} +.elementor-metabox-content .elementor-repeater-tool-btn.remove-repeater-row:hover { + color: #F59E0B; +} +.elementor-metabox-content .row-font-preview, +.elementor-metabox-content .inline-preview { + font-size: 16px; + text-transform: capitalize; +} + +.column-font_preview { + width: 65%; +} + +.widefat td.column-font_preview { + font-size: 16px; +} + +.post-type-elementor_icons .elementor-metabox-content .elementor-button:not([disabled]) { + margin-block-start: 10px; +} +.post-type-elementor_icons div#postbox-container-1 { + display: none; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox { + display: none; + border: 1px solid #F1F2F3; + border-radius: 1px; + background-color: #fff; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .inside { + margin-block-start: 10px; + margin-block-end: 20px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-metabox-content { + background-color: #fff; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-custom-icons-metabox { + padding-block-start: 4px; + padding-block-end: 10px; + padding-inline-start: 10px; + padding-inline-end: 10px; +} +@media (max-width: 1025px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-custom-icons-metabox { + padding: 0; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox h4 { + color: #1f2124; + font-size: 22px; + font-weight: 500; + letter-spacing: 0.7px; + line-height: 28px; + margin: 0 0 4px 0; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox h5 { + color: #9DA5AE; + font-size: 16px; + font-weight: 500; + letter-spacing: 0.5px; + line-height: 21px; + margin: 0; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor--dropzone--upload__icon i { + font-size: 64px; + color: #0A875A; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .box__uploading, +.post-type-elementor_icons div#elementor-custom-icons-metabox .box__success, +.post-type-elementor_icons div#elementor-custom-icons-metabox .box__error, +.post-type-elementor_icons div#elementor-custom-icons-metabox .box__file { + display: none; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .is-dragover { + background-color: grey; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .box__input { + padding: 180px 0; + display: flex; + flex-direction: column; + align-items: center; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-field-dropzone { + outline: 2px dashed #D5D8DC; + outline-offset: -3px; + background-color: #fff; + display: none; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons { + background-color: #F9FAFA; + border: 1px solid #F1F2F3; + border-radius: 1px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-metabox-content { + background-color: #F9FAFA; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-metabox-content .elementor-custom-icons-metabox { + padding-block-start: 4px; + padding-block-end: 0; + padding-inline-start: 10px; + padding-inline-end: 10px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header { + height: 50px; + color: #3f444b; + background-color: #fff; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.06); + padding: 0 35px; + display: flex; + align-items: center; + justify-content: flex-start; +} +@media (max-width: 1025px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header { + padding: 0 6px; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div { + padding-inline-end: 10px; + padding-inline-start: 10px; +} +@media (max-width: 1025px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div { + line-height: 1; + } + .post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div.remove { + font-size: 10px; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div:nth-of-type(2) { + border: 1px solid #9DA5AE; + border-block-start: 0; + border-block-end: 0; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta { + color: #1f2124; + font-size: 14px; + line-height: 1; +} +@media (max-width: 1025px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta { + font-size: 10px; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-value { + font-weight: bold; +} +@media (max-width: 1025px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-value { + font-size: 10px; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove { + margin-inline-start: auto; + color: #1f2124; + opacity: 0.6; + cursor: pointer; + transition: all 0.3s; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove i { + color: #3f444b; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove:hover { + opacity: 1; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-footer { + color: #BABFC5; + font-family: Roboto, Arial, Helvetica, sans-serif; + border-block-start: 1px solid #F1F2F3; + font-size: 11px; + font-weight: 500; + line-height: 1; + text-align: end; + padding-block-start: 10px; + padding-block-end: 10px; + padding-inline-end: 35px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox ul { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(105px, 1fr)); + grid-gap: 20px; + padding-block-start: 15px; + padding-block-end: 0; + padding-inline-start: 35px; + padding-inline-end: 35px; + overflow-y: auto; + max-height: 575px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox ul li { + position: relative; + height: 0; + padding-block-end: 100%; + background-color: #fff; + box-shadow: 0 1px 12px rgba(0, 0, 0, 0.05); + border-radius: 3px; + overflow: hidden; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding: 1px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon-name { + color: #BABFC5; + font-size: 11px; + padding: 18px 20px 0; + white-space: nowrap; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; +} +@media (max-width: 479px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon-name { + display: none; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox ul li i { + font-size: 32px; +} +.post-type-elementor_icons #tagsdiv-elementor_icon_type, +.post-type-elementor_icons #minor-publishing-actions, +.post-type-elementor_icons #misc-publishing-actions { + display: none; +} + +.column-icons_prefix { + width: 65%; +} +/*# sourceMappingURL=admin-rtl.css.map */ \ No newline at end of file diff --git a/assets/css/admin-rtl.min.css b/assets/css/admin-rtl.min.css new file mode 100644 index 0000000..7561be6 --- /dev/null +++ b/assets/css/admin-rtl.min.css @@ -0,0 +1,2 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.wrap.elementor-admin-page-license form.elementor-license-box{max-width:600px;background:#fff;margin:20px 0;padding:20px}.wrap.elementor-admin-page-license form.elementor-license-box h3{display:flex;justify-content:space-between;align-items:center;margin:0;padding:0;padding-block-end:20px;border-block-end:1px solid #eee}.wrap.elementor-admin-page-license form.elementor-license-box h3 span{flex-grow:1;padding-inline-start:5px}.wrap.elementor-admin-page-license form.elementor-license-box h3 small{font-size:13px;font-weight:400}.wrap.elementor-admin-page-license form.elementor-license-box label{display:block;font-size:1.3em;font-weight:600;margin:1em 0}.wrap.elementor-admin-page-license form.elementor-license-box .button{height:30px;margin-inline-start:15px;margin-block-end:0}.wrap.elementor-admin-page-license form.elementor-license-box p.description{margin:10px 0}.wrap.elementor-admin-page-license form.elementor-license-box .e-row-stretch{display:flex;align-items:center;justify-content:space-between}.wrap.elementor-admin-page-license form.elementor-license-box .e-row-divider-bottom{padding-block-end:15px;border-block-end:1px solid #eee}.wrap.elementor-admin-page-license .elementor-box-action{display:flex;justify-content:flex-end;align-items:flex-end;margin-block-start:30px}.wrap.elementor-admin-page-license .elementor-box-action .elementor-manually-link{color:#72777c;margin-inline-end:15px}.wrap.elementor-admin-page-license .elementor-box-action .elementor-manually-link:hover{color:inherit}#adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link]{font-weight:600;background-color:#93003c;color:#fff;margin:3px 10px 0;display:block;text-align:center;border-radius:3px;transition:all .3s}#adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link]:focus,#adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link]:hover{background-color:#c60051;box-shadow:none}.fixed .column-elementor_library_type,.fixed .column-instances{width:10%}.fixed .elementor-shortcode-input{min-width:235px}@media (min-width:768px) and (max-width:1440px){.fixed .column-shortcode{width:25%}.fixed .elementor-shortcode-input{min-width:100%}}#available-widgets [class*=elementor-template] .widget-title:before{content:"\e801";font-family:eicon;font-size:17px}#elementor-widget-template-empty-templates{margin-block-start:15px;text-align:center}.elementor-widget-template-empty-templates-title{padding:25px 0 30px}.elementor-widget-template-empty-templates-icon{font-size:96px}.elementor-widget-template-empty-templates-footer{color:var(--e-a-color-txt-muted);font-size:13px;font-style:italic;margin-block-end:15px}.elementor-button-spinner.error:before{content:"\f335";color:red}span.font-variations-count{display:inline-block;vertical-align:top;margin:1px 0 0 5px;padding:0 5px;min-width:7px;height:17px;border-radius:11px;background-color:#d4dffb;color:#4278b2;font-size:9px;line-height:17px;text-align:center;z-index:26}.post-type-elementor_font div#elementor-font-custommetabox{background:none;border:0}.post-type-elementor_font div#elementor-font-custommetabox button.handlediv{display:none}.post-type-elementor_font div#elementor-font-custommetabox #poststuff .inside{margin:0;padding:0}.post-type-elementor_font div#elementor-font-custommetabox h2.hndle{padding:0}.post-type-elementor_font #minor-publishing-actions,.post-type-elementor_font #misc-publishing-actions,.post-type-elementor_font #tagsdiv-elementor_font_type,.post-type-elementor_font div#elementor-font-custommetabox .handle-actions{display:none}.elementor-metabox-content .repeater-block{background:#fff;color:#3f444b;padding:20px;margin-block-end:2px}.elementor-metabox-content .repeater-block span.elementor-repeater-tool-btn.close-repeater-row{display:none}.elementor-metabox-content .repeater-block.block-visible{padding-block-end:0;margin-block-end:0}.elementor-metabox-content .repeater-block.block-visible span.elementor-repeater-tool-btn.toggle-repeater-row{display:none}.elementor-metabox-content .repeater-block.block-visible span.elementor-repeater-tool-btn.close-repeater-row{display:inline-block}.elementor-metabox-content .repeater-block:not(.block-visible) .close-repeater-row{display:none}.elementor-metabox-content .repeater-block .repeater-title{cursor:pointer}.elementor-metabox-content .row-font-label{padding:0;margin:0;display:flex;text-transform:capitalize}.elementor-metabox-content .row-font-label li{box-sizing:border-box;flex-grow:1;width:100%;margin:0}.elementor-metabox-content .row-font-label li span.label{font-weight:500;padding-inline-end:10px}.elementor-metabox-content .row-font-label li.row-font-style,.elementor-metabox-content .row-font-label li.row-font-weight{max-width:180px}.elementor-metabox-content .row-font-label li.row-font-actions{max-width:200px;text-align:end}.elementor-metabox-content .repeater-content{margin:0}.elementor-metabox-content .repeater-content .repeater-content-top{display:flex;margin-block-end:20px;line-height:28px}.elementor-metabox-content .repeater-content .repeater-content-top>div{box-sizing:border-box;flex-grow:1}.elementor-metabox-content .repeater-content .repeater-content-top p{margin:0;display:inline-block}.elementor-metabox-content .repeater-content .repeater-content-top p label{font-weight:500;padding-inline-end:10px}.elementor-metabox-content .repeater-content .repeater-content-top .elementor-field-select{max-width:180px}.elementor-metabox-content .repeater-content .repeater-content-top .elementor-field-toolbar{max-width:200px;text-align:end}.elementor-metabox-content .repeater-content .repeater-content-bottom{background-color:#f9fafa;padding:20px 40px;margin:0 -20px}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field{display:flex;align-items:center;background-color:#fff;padding:10px 20px;margin-block-end:10px;box-shadow:0 3px 5px rgba(0,0,0,.05)}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field:last-child{margin-block-end:0}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field input,.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field p{box-sizing:border-box;flex-grow:1;width:100%;margin:0}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field p.elementor-field-label{font-weight:500;max-width:120px}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input{padding:5px 8px;margin:0 15px;border-radius:3px;font-size:12px;width:100%;background:none;box-shadow:none;color:#0c0d0e;border:1px solid;outline:none}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input:not(:focus){border-color:#d5d8dc}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input:focus{border-color:#9da5ae}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-btn,.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn{max-width:100px;font-size:11px}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn{background-color:#f1f2f3;color:#9da5ae}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn:hover{background-color:#f59e0b;color:#fff}.elementor-metabox-content .elementor-button{background-color:#9da5ae;color:#fff;line-height:1;text-transform:uppercase;height:auto;padding:10px 20px;outline:none;border:none;transition-property:background,color,box-shadow,opacity;transition-duration:.3s}.elementor-metabox-content .elementor-button:focus,.elementor-metabox-content .elementor-button:hover,.elementor-metabox-content .elementor-button:visited{color:#fff}.elementor-metabox-content .elementor-button:focus,.elementor-metabox-content .elementor-button:visited{background-color:#9da5ae}.elementor-metabox-content .elementor-button:hover{background-color:#3f444b;box-shadow:0 0 2px rgba(0,0,0,.12),0 2px 2px rgba(0,0,0,.2);border:none}.elementor-metabox-content .elementor-button:active{box-shadow:0 5px 10px rgba(0,0,0,.19),0 3px 3px rgba(0,0,0,.1)}.elementor-metabox-content .elementor-button:not([disabled]){cursor:pointer}.elementor-metabox-content .elementor-button.elementor-size-xs{font-size:11px;padding:10px 20px;border-radius:2px}.elementor-metabox-content .elementor-button.elementor-size-sm{font-size:13px;padding:12px 24px;border-radius:3px}.elementor-metabox-content .elementor-button.elementor-size-md{font-size:14px;padding:15px 30px;border-radius:4px}.elementor-metabox-content .elementor-button.elementor-size-lg{font-size:15px;padding:20px 40px;border-radius:5px}.elementor-metabox-content .elementor-button.elementor-size-xl{font-size:18px;padding:25px 50px;border-radius:6px}.elementor-metabox-content .elementor-button .elementor-align-icon-right{float:right;margin-left:5px}.elementor-metabox-content .elementor-button .elementor-align-icon-left{float:left;margin-right:5px}.elementor-metabox-content input.button.add-repeater-row{margin-block-start:18px;border:none;box-shadow:none}.elementor-metabox-content .elementor-repeater-tool-btn{color:#9da5ae;cursor:pointer;padding:0 20px;font-size:12px;transition:all .3s}.elementor-metabox-content .elementor-repeater-tool-btn i{padding-inline-end:5px}.elementor-metabox-content .elementor-repeater-tool-btn:hover{color:#3f444b}.elementor-metabox-content .elementor-repeater-tool-btn.remove-repeater-row:hover{color:#f59e0b}.elementor-metabox-content .inline-preview,.elementor-metabox-content .row-font-preview{font-size:16px;text-transform:capitalize}.column-font_preview{width:65%}.widefat td.column-font_preview{font-size:16px}.post-type-elementor_icons .elementor-metabox-content .elementor-button:not([disabled]){margin-block-start:10px}.post-type-elementor_icons div#postbox-container-1{display:none}.post-type-elementor_icons div#elementor-custom-icons-metabox{display:none;border:1px solid #f1f2f3;border-radius:1px;background-color:#fff}.post-type-elementor_icons div#elementor-custom-icons-metabox .inside{margin-block-start:10px;margin-block-end:20px}.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-metabox-content{background-color:#fff}.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-custom-icons-metabox{padding-block-start:4px;padding-block-end:10px;padding-inline-start:10px;padding-inline-end:10px}@media (max-width:1025px){.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-custom-icons-metabox{padding:0}}.post-type-elementor_icons div#elementor-custom-icons-metabox h4{color:#1f2124;font-size:22px;font-weight:500;letter-spacing:.7px;line-height:28px;margin:0 0 4px}.post-type-elementor_icons div#elementor-custom-icons-metabox h5{color:#9da5ae;font-size:16px;font-weight:500;letter-spacing:.5px;line-height:21px;margin:0}.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor--dropzone--upload__icon i{font-size:64px;color:#0a875a}.post-type-elementor_icons div#elementor-custom-icons-metabox .box__error,.post-type-elementor_icons div#elementor-custom-icons-metabox .box__file,.post-type-elementor_icons div#elementor-custom-icons-metabox .box__success,.post-type-elementor_icons div#elementor-custom-icons-metabox .box__uploading{display:none}.post-type-elementor_icons div#elementor-custom-icons-metabox .is-dragover{background-color:grey}.post-type-elementor_icons div#elementor-custom-icons-metabox .box__input{padding:180px 0;display:flex;flex-direction:column;align-items:center}.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-field-dropzone{outline:2px dashed #d5d8dc;outline-offset:-3px;background-color:#fff;display:none}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons{background-color:#f9fafa;border:1px solid #f1f2f3;border-radius:1px}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-metabox-content{background-color:#f9fafa}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-metabox-content .elementor-custom-icons-metabox{padding-block-start:4px;padding-block-end:0;padding-inline-start:10px;padding-inline-end:10px}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header{height:50px;color:#3f444b;background-color:#fff;box-shadow:0 2px 6px 0 rgba(0,0,0,.06);padding:0 35px;display:flex;align-items:center;justify-content:flex-start}@media (max-width:1025px){.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header{padding:0 6px}}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div{padding-inline-end:10px;padding-inline-start:10px}@media (max-width:1025px){.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div{line-height:1}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div.remove{font-size:10px}}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div:nth-of-type(2){border:1px solid #9da5ae;border-block-start:0;border-block-end:0}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta{color:#1f2124;font-size:14px;line-height:1}@media (max-width:1025px){.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta{font-size:10px}}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-value{font-weight:700}@media (max-width:1025px){.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-value{font-size:10px}}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove{margin-inline-start:auto;color:#1f2124;opacity:.6;cursor:pointer;transition:all .3s}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove i{color:#3f444b}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove:hover{opacity:1}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-footer{color:#babfc5;font-family:Roboto,Arial,Helvetica,sans-serif;border-block-start:1px solid #f1f2f3;font-size:11px;font-weight:500;line-height:1;text-align:end;padding-block-start:10px;padding-block-end:10px;padding-inline-end:35px}.post-type-elementor_icons div#elementor-custom-icons-metabox ul{display:grid;grid-template-columns:repeat(auto-fill,minmax(105px,1fr));grid-gap:20px;padding-block-start:15px;padding-block-end:0;padding-inline-start:35px;padding-inline-end:35px;overflow-y:auto;max-height:575px}.post-type-elementor_icons div#elementor-custom-icons-metabox ul li{position:relative;height:0;padding-block-end:100%;background-color:#fff;box-shadow:0 1px 12px rgba(0,0,0,.05);border-radius:3px;overflow:hidden}.post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon{display:flex;flex-direction:column;align-items:center;width:100%;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);padding:1px}.post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon-name{color:#babfc5;font-size:11px;padding:18px 20px 0;white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}@media (max-width:479px){.post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon-name{display:none}}.post-type-elementor_icons div#elementor-custom-icons-metabox ul li i{font-size:32px}.post-type-elementor_icons #minor-publishing-actions,.post-type-elementor_icons #misc-publishing-actions,.post-type-elementor_icons #tagsdiv-elementor_icon_type{display:none}.column-icons_prefix{width:65%} \ No newline at end of file diff --git a/assets/css/admin.css b/assets/css/admin.css new file mode 100644 index 0000000..84392bb --- /dev/null +++ b/assets/css/admin.css @@ -0,0 +1,610 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.wrap.elementor-admin-page-license form.elementor-license-box { + max-width: 600px; + background: white; + margin: 20px 0; + padding: 20px 20px; +} +.wrap.elementor-admin-page-license form.elementor-license-box h3 { + display: flex; + justify-content: space-between; + align-items: center; + margin: 0; + padding: 0; + padding-block-end: 20px; + border-block-end: 1px solid #eee; +} +.wrap.elementor-admin-page-license form.elementor-license-box h3 span { + flex-grow: 1; + padding-inline-start: 5px; +} +.wrap.elementor-admin-page-license form.elementor-license-box h3 small { + font-size: 13px; + font-weight: normal; +} +.wrap.elementor-admin-page-license form.elementor-license-box label { + display: block; + font-size: 1.3em; + font-weight: 600; + margin: 1em 0; +} +.wrap.elementor-admin-page-license form.elementor-license-box .button { + height: 30px; + margin-inline-start: 15px; + margin-block-end: 0; +} +.wrap.elementor-admin-page-license form.elementor-license-box p.description { + margin: 10px 0; +} +.wrap.elementor-admin-page-license form.elementor-license-box .e-row-stretch { + display: flex; + align-items: center; + justify-content: space-between; +} +.wrap.elementor-admin-page-license form.elementor-license-box .e-row-divider-bottom { + padding-block-end: 15px; + border-block-end: 1px solid #eeeeee; +} +.wrap.elementor-admin-page-license .elementor-box-action { + display: flex; + justify-content: flex-end; + align-items: flex-end; + margin-block-start: 30px; +} +.wrap.elementor-admin-page-license .elementor-box-action .elementor-manually-link { + color: #72777c; + margin-inline-end: 15px; +} +.wrap.elementor-admin-page-license .elementor-box-action .elementor-manually-link:hover { + color: inherit; +} + +#adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link] { + font-weight: 600; + background-color: #93003c; + color: #ffffff; + margin: 3px 10px 0; + display: block; + text-align: center; + border-radius: 3px; + transition: all 0.3s; +} +#adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link]:hover, #adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link]:focus { + background-color: #c60051; + box-shadow: none; +} + +.fixed .column-elementor_library_type, +.fixed .column-instances { + width: 10%; +} +.fixed .elementor-shortcode-input { + min-width: 235px; +} +@media (min-width: 768px) and (max-width: 1440px) { + .fixed .column-shortcode { + width: 25%; + } + .fixed .elementor-shortcode-input { + min-width: 100%; + } +} + +#available-widgets [class*=elementor-template] .widget-title:before { + content: "\e801"; + font-family: eicon; + font-size: 17px; +} + +#elementor-widget-template-empty-templates { + margin-block-start: 15px; + text-align: center; +} + +.elementor-widget-template-empty-templates-title { + padding: 25px 0 30px; +} + +.elementor-widget-template-empty-templates-icon { + font-size: 96px; +} + +.elementor-widget-template-empty-templates-footer { + color: var(--e-a-color-txt-muted); + font-size: 13px; + font-style: italic; + margin-block-end: 15px; +} + +.elementor-button-spinner.error:before { + content: "\f335"; + color: #ff0000; +} + +span.font-variations-count { + display: inline-block; + vertical-align: top; + margin: 1px 0 0 5px; + padding: 0 5px; + min-width: 7px; + height: 17px; + border-radius: 11px; + background-color: #d4dffb; + color: #4278b2; + font-size: 9px; + line-height: 17px; + text-align: center; + z-index: 26; +} + +.post-type-elementor_font div#elementor-font-custommetabox { + background: none; + border: 0; +} +.post-type-elementor_font div#elementor-font-custommetabox button.handlediv { + display: none; +} +.post-type-elementor_font div#elementor-font-custommetabox #poststuff .inside { + margin: 0; + padding: 0; +} +.post-type-elementor_font div#elementor-font-custommetabox h2.hndle { + padding: 0; +} +.post-type-elementor_font div#elementor-font-custommetabox .handle-actions { + display: none; +} +.post-type-elementor_font #tagsdiv-elementor_font_type, +.post-type-elementor_font #minor-publishing-actions, +.post-type-elementor_font #misc-publishing-actions { + display: none; +} + +.elementor-metabox-content .repeater-block { + background: #ffffff; + color: #3f444b; + padding: 20px; + margin-block-end: 2px; +} +.elementor-metabox-content .repeater-block span.elementor-repeater-tool-btn.close-repeater-row { + display: none; +} +.elementor-metabox-content .repeater-block.block-visible { + padding-block-end: 0; + margin-block-end: 0; +} +.elementor-metabox-content .repeater-block.block-visible span.elementor-repeater-tool-btn.toggle-repeater-row { + display: none; +} +.elementor-metabox-content .repeater-block.block-visible span.elementor-repeater-tool-btn.close-repeater-row { + display: inline-block; +} +.elementor-metabox-content .repeater-block:not(.block-visible) .close-repeater-row { + display: none; +} +.elementor-metabox-content .repeater-block .repeater-title { + cursor: pointer; +} +.elementor-metabox-content .row-font-label { + padding: 0; + margin: 0; + display: flex; + text-transform: capitalize; +} +.elementor-metabox-content .row-font-label li { + box-sizing: border-box; + flex-grow: 1; + width: 100%; + margin: 0; +} +.elementor-metabox-content .row-font-label li span.label { + font-weight: 500; + padding-inline-end: 10px; +} +.elementor-metabox-content .row-font-label li.row-font-weight, .elementor-metabox-content .row-font-label li.row-font-style { + max-width: 180px; +} +.elementor-metabox-content .row-font-label li.row-font-actions { + max-width: 200px; + text-align: end; +} +.elementor-metabox-content .repeater-content { + margin: 0; +} +.elementor-metabox-content .repeater-content .repeater-content-top { + display: flex; + margin-block-end: 20px; + line-height: 28px; +} +.elementor-metabox-content .repeater-content .repeater-content-top > div { + box-sizing: border-box; + flex-grow: 1; +} +.elementor-metabox-content .repeater-content .repeater-content-top p { + margin: 0; + display: inline-block; +} +.elementor-metabox-content .repeater-content .repeater-content-top p label { + font-weight: 500; + padding-inline-end: 10px; +} +.elementor-metabox-content .repeater-content .repeater-content-top .elementor-field-select { + max-width: 180px; +} +.elementor-metabox-content .repeater-content .repeater-content-top .elementor-field-toolbar { + max-width: 200px; + text-align: end; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom { + background-color: #F9FAFA; + padding: 20px 40px; + margin: 0 -20px; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field { + display: flex; + align-items: center; + background-color: #ffffff; + padding: 10px 20px; + margin-block-end: 10px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.05); +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field:last-child { + margin-block-end: 0; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field p, +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field input { + box-sizing: border-box; + flex-grow: 1; + width: 100%; + margin: 0; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field p.elementor-field-label { + font-weight: 500; + max-width: 120px; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input { + padding: 5px 8px; + margin: 0 15px; + border-radius: 3px; + font-size: 12px; + width: 100%; + background: none; + box-shadow: none; + color: #0C0D0E; + border: 1px solid; + outline: none; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input:not(:focus) { + border-color: #D5D8DC; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input:focus { + border-color: #9DA5AE; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-btn, +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn { + max-width: 100px; + font-size: 11px; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn { + background-color: #F1F2F3; + color: #9DA5AE; +} +.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn:hover { + background-color: #F59E0B; + color: white; +} +.elementor-metabox-content .elementor-button { + background-color: #9DA5AE; + color: #fff; + line-height: 1; + text-transform: uppercase; + height: auto; + padding: 10px 20px; + outline: none; + border: none; + transition-property: background, color, box-shadow, opacity; + transition-duration: 0.3s; +} +.elementor-metabox-content .elementor-button:hover, .elementor-metabox-content .elementor-button:focus, .elementor-metabox-content .elementor-button:visited { + color: #fff; +} +.elementor-metabox-content .elementor-button:focus, .elementor-metabox-content .elementor-button:visited { + background-color: #9DA5AE; +} +.elementor-metabox-content .elementor-button:hover { + background-color: #3f444b; + box-shadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 2px rgba(0, 0, 0, 0.2); + border: none; +} +.elementor-metabox-content .elementor-button:active { + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.19), 0 3px 3px rgba(0, 0, 0, 0.1); +} +.elementor-metabox-content .elementor-button:not([disabled]) { + cursor: pointer; +} +.elementor-metabox-content .elementor-button.elementor-size-xs { + font-size: 11px; + padding: 10px 20px; + border-radius: 2px; +} +.elementor-metabox-content .elementor-button.elementor-size-sm { + font-size: 13px; + padding: 12px 24px; + border-radius: 3px; +} +.elementor-metabox-content .elementor-button.elementor-size-md { + font-size: 14px; + padding: 15px 30px; + border-radius: 4px; +} +.elementor-metabox-content .elementor-button.elementor-size-lg { + font-size: 15px; + padding: 20px 40px; + border-radius: 5px; +} +.elementor-metabox-content .elementor-button.elementor-size-xl { + font-size: 18px; + padding: 25px 50px; + border-radius: 6px; +} +.elementor-metabox-content .elementor-button .elementor-align-icon-right { + float: right; + margin-left: 5px; +} +.elementor-metabox-content .elementor-button .elementor-align-icon-left { + float: left; + margin-right: 5px; +} +.elementor-metabox-content input.button.add-repeater-row { + margin-block-start: 18px; + border: none; + box-shadow: none; +} +.elementor-metabox-content .elementor-repeater-tool-btn { + color: #9DA5AE; + cursor: pointer; + padding: 0 20px; + font-size: 12px; + transition: all 0.3s; +} +.elementor-metabox-content .elementor-repeater-tool-btn i { + padding-inline-end: 5px; +} +.elementor-metabox-content .elementor-repeater-tool-btn:hover { + color: #3f444b; +} +.elementor-metabox-content .elementor-repeater-tool-btn.remove-repeater-row:hover { + color: #F59E0B; +} +.elementor-metabox-content .row-font-preview, +.elementor-metabox-content .inline-preview { + font-size: 16px; + text-transform: capitalize; +} + +.column-font_preview { + width: 65%; +} + +.widefat td.column-font_preview { + font-size: 16px; +} + +.post-type-elementor_icons .elementor-metabox-content .elementor-button:not([disabled]) { + margin-block-start: 10px; +} +.post-type-elementor_icons div#postbox-container-1 { + display: none; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox { + display: none; + border: 1px solid #F1F2F3; + border-radius: 1px; + background-color: #fff; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .inside { + margin-block-start: 10px; + margin-block-end: 20px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-metabox-content { + background-color: #fff; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-custom-icons-metabox { + padding-block-start: 4px; + padding-block-end: 10px; + padding-inline-start: 10px; + padding-inline-end: 10px; +} +@media (max-width: 1025px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-custom-icons-metabox { + padding: 0; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox h4 { + color: #1f2124; + font-size: 22px; + font-weight: 500; + letter-spacing: 0.7px; + line-height: 28px; + margin: 0 0 4px 0; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox h5 { + color: #9DA5AE; + font-size: 16px; + font-weight: 500; + letter-spacing: 0.5px; + line-height: 21px; + margin: 0; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor--dropzone--upload__icon i { + font-size: 64px; + color: #0A875A; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .box__uploading, +.post-type-elementor_icons div#elementor-custom-icons-metabox .box__success, +.post-type-elementor_icons div#elementor-custom-icons-metabox .box__error, +.post-type-elementor_icons div#elementor-custom-icons-metabox .box__file { + display: none; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .is-dragover { + background-color: grey; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .box__input { + padding: 180px 0; + display: flex; + flex-direction: column; + align-items: center; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-field-dropzone { + outline: 2px dashed #D5D8DC; + outline-offset: -3px; + background-color: #fff; + display: none; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons { + background-color: #F9FAFA; + border: 1px solid #F1F2F3; + border-radius: 1px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-metabox-content { + background-color: #F9FAFA; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-metabox-content .elementor-custom-icons-metabox { + padding-block-start: 4px; + padding-block-end: 0; + padding-inline-start: 10px; + padding-inline-end: 10px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header { + height: 50px; + color: #3f444b; + background-color: #fff; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.06); + padding: 0 35px; + display: flex; + align-items: center; + justify-content: flex-start; +} +@media (max-width: 1025px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header { + padding: 0 6px; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div { + padding-inline-end: 10px; + padding-inline-start: 10px; +} +@media (max-width: 1025px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div { + line-height: 1; + } + .post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div.remove { + font-size: 10px; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div:nth-of-type(2) { + border: 1px solid #9DA5AE; + border-block-start: 0; + border-block-end: 0; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta { + color: #1f2124; + font-size: 14px; + line-height: 1; +} +@media (max-width: 1025px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta { + font-size: 10px; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-value { + font-weight: bold; +} +@media (max-width: 1025px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-value { + font-size: 10px; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove { + margin-inline-start: auto; + color: #1f2124; + opacity: 0.6; + cursor: pointer; + transition: all 0.3s; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove i { + color: #3f444b; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove:hover { + opacity: 1; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-footer { + color: #BABFC5; + font-family: Roboto, Arial, Helvetica, sans-serif; + border-block-start: 1px solid #F1F2F3; + font-size: 11px; + font-weight: 500; + line-height: 1; + text-align: end; + padding-block-start: 10px; + padding-block-end: 10px; + padding-inline-end: 35px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox ul { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(105px, 1fr)); + grid-gap: 20px; + padding-block-start: 15px; + padding-block-end: 0; + padding-inline-start: 35px; + padding-inline-end: 35px; + overflow-y: auto; + max-height: 575px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox ul li { + position: relative; + height: 0; + padding-block-end: 100%; + background-color: #fff; + box-shadow: 0 1px 12px rgba(0, 0, 0, 0.05); + border-radius: 3px; + overflow: hidden; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding: 1px; +} +.post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon-name { + color: #BABFC5; + font-size: 11px; + padding: 18px 20px 0; + white-space: nowrap; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; +} +@media (max-width: 479px) { + .post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon-name { + display: none; + } +} +.post-type-elementor_icons div#elementor-custom-icons-metabox ul li i { + font-size: 32px; +} +.post-type-elementor_icons #tagsdiv-elementor_icon_type, +.post-type-elementor_icons #minor-publishing-actions, +.post-type-elementor_icons #misc-publishing-actions { + display: none; +} + +.column-icons_prefix { + width: 65%; +} +/*# sourceMappingURL=admin.css.map */ \ No newline at end of file diff --git a/assets/css/admin.min.css b/assets/css/admin.min.css new file mode 100644 index 0000000..7561be6 --- /dev/null +++ b/assets/css/admin.min.css @@ -0,0 +1,2 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.wrap.elementor-admin-page-license form.elementor-license-box{max-width:600px;background:#fff;margin:20px 0;padding:20px}.wrap.elementor-admin-page-license form.elementor-license-box h3{display:flex;justify-content:space-between;align-items:center;margin:0;padding:0;padding-block-end:20px;border-block-end:1px solid #eee}.wrap.elementor-admin-page-license form.elementor-license-box h3 span{flex-grow:1;padding-inline-start:5px}.wrap.elementor-admin-page-license form.elementor-license-box h3 small{font-size:13px;font-weight:400}.wrap.elementor-admin-page-license form.elementor-license-box label{display:block;font-size:1.3em;font-weight:600;margin:1em 0}.wrap.elementor-admin-page-license form.elementor-license-box .button{height:30px;margin-inline-start:15px;margin-block-end:0}.wrap.elementor-admin-page-license form.elementor-license-box p.description{margin:10px 0}.wrap.elementor-admin-page-license form.elementor-license-box .e-row-stretch{display:flex;align-items:center;justify-content:space-between}.wrap.elementor-admin-page-license form.elementor-license-box .e-row-divider-bottom{padding-block-end:15px;border-block-end:1px solid #eee}.wrap.elementor-admin-page-license .elementor-box-action{display:flex;justify-content:flex-end;align-items:flex-end;margin-block-start:30px}.wrap.elementor-admin-page-license .elementor-box-action .elementor-manually-link{color:#72777c;margin-inline-end:15px}.wrap.elementor-admin-page-license .elementor-box-action .elementor-manually-link:hover{color:inherit}#adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link]{font-weight:600;background-color:#93003c;color:#fff;margin:3px 10px 0;display:block;text-align:center;border-radius:3px;transition:all .3s}#adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link]:focus,#adminmenu #toplevel_page_elementor a[href=elementor_pro_upgrade_license_menu_link]:hover{background-color:#c60051;box-shadow:none}.fixed .column-elementor_library_type,.fixed .column-instances{width:10%}.fixed .elementor-shortcode-input{min-width:235px}@media (min-width:768px) and (max-width:1440px){.fixed .column-shortcode{width:25%}.fixed .elementor-shortcode-input{min-width:100%}}#available-widgets [class*=elementor-template] .widget-title:before{content:"\e801";font-family:eicon;font-size:17px}#elementor-widget-template-empty-templates{margin-block-start:15px;text-align:center}.elementor-widget-template-empty-templates-title{padding:25px 0 30px}.elementor-widget-template-empty-templates-icon{font-size:96px}.elementor-widget-template-empty-templates-footer{color:var(--e-a-color-txt-muted);font-size:13px;font-style:italic;margin-block-end:15px}.elementor-button-spinner.error:before{content:"\f335";color:red}span.font-variations-count{display:inline-block;vertical-align:top;margin:1px 0 0 5px;padding:0 5px;min-width:7px;height:17px;border-radius:11px;background-color:#d4dffb;color:#4278b2;font-size:9px;line-height:17px;text-align:center;z-index:26}.post-type-elementor_font div#elementor-font-custommetabox{background:none;border:0}.post-type-elementor_font div#elementor-font-custommetabox button.handlediv{display:none}.post-type-elementor_font div#elementor-font-custommetabox #poststuff .inside{margin:0;padding:0}.post-type-elementor_font div#elementor-font-custommetabox h2.hndle{padding:0}.post-type-elementor_font #minor-publishing-actions,.post-type-elementor_font #misc-publishing-actions,.post-type-elementor_font #tagsdiv-elementor_font_type,.post-type-elementor_font div#elementor-font-custommetabox .handle-actions{display:none}.elementor-metabox-content .repeater-block{background:#fff;color:#3f444b;padding:20px;margin-block-end:2px}.elementor-metabox-content .repeater-block span.elementor-repeater-tool-btn.close-repeater-row{display:none}.elementor-metabox-content .repeater-block.block-visible{padding-block-end:0;margin-block-end:0}.elementor-metabox-content .repeater-block.block-visible span.elementor-repeater-tool-btn.toggle-repeater-row{display:none}.elementor-metabox-content .repeater-block.block-visible span.elementor-repeater-tool-btn.close-repeater-row{display:inline-block}.elementor-metabox-content .repeater-block:not(.block-visible) .close-repeater-row{display:none}.elementor-metabox-content .repeater-block .repeater-title{cursor:pointer}.elementor-metabox-content .row-font-label{padding:0;margin:0;display:flex;text-transform:capitalize}.elementor-metabox-content .row-font-label li{box-sizing:border-box;flex-grow:1;width:100%;margin:0}.elementor-metabox-content .row-font-label li span.label{font-weight:500;padding-inline-end:10px}.elementor-metabox-content .row-font-label li.row-font-style,.elementor-metabox-content .row-font-label li.row-font-weight{max-width:180px}.elementor-metabox-content .row-font-label li.row-font-actions{max-width:200px;text-align:end}.elementor-metabox-content .repeater-content{margin:0}.elementor-metabox-content .repeater-content .repeater-content-top{display:flex;margin-block-end:20px;line-height:28px}.elementor-metabox-content .repeater-content .repeater-content-top>div{box-sizing:border-box;flex-grow:1}.elementor-metabox-content .repeater-content .repeater-content-top p{margin:0;display:inline-block}.elementor-metabox-content .repeater-content .repeater-content-top p label{font-weight:500;padding-inline-end:10px}.elementor-metabox-content .repeater-content .repeater-content-top .elementor-field-select{max-width:180px}.elementor-metabox-content .repeater-content .repeater-content-top .elementor-field-toolbar{max-width:200px;text-align:end}.elementor-metabox-content .repeater-content .repeater-content-bottom{background-color:#f9fafa;padding:20px 40px;margin:0 -20px}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field{display:flex;align-items:center;background-color:#fff;padding:10px 20px;margin-block-end:10px;box-shadow:0 3px 5px rgba(0,0,0,.05)}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field:last-child{margin-block-end:0}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field input,.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field p{box-sizing:border-box;flex-grow:1;width:100%;margin:0}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field p.elementor-field-label{font-weight:500;max-width:120px}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input{padding:5px 8px;margin:0 15px;border-radius:3px;font-size:12px;width:100%;background:none;box-shadow:none;color:#0c0d0e;border:1px solid;outline:none}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input:not(:focus){border-color:#d5d8dc}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-field-input:focus{border-color:#9da5ae}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-btn,.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn{max-width:100px;font-size:11px}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn{background-color:#f1f2f3;color:#9da5ae}.elementor-metabox-content .repeater-content .repeater-content-bottom .elementor-field .elementor-upload-clear-btn:hover{background-color:#f59e0b;color:#fff}.elementor-metabox-content .elementor-button{background-color:#9da5ae;color:#fff;line-height:1;text-transform:uppercase;height:auto;padding:10px 20px;outline:none;border:none;transition-property:background,color,box-shadow,opacity;transition-duration:.3s}.elementor-metabox-content .elementor-button:focus,.elementor-metabox-content .elementor-button:hover,.elementor-metabox-content .elementor-button:visited{color:#fff}.elementor-metabox-content .elementor-button:focus,.elementor-metabox-content .elementor-button:visited{background-color:#9da5ae}.elementor-metabox-content .elementor-button:hover{background-color:#3f444b;box-shadow:0 0 2px rgba(0,0,0,.12),0 2px 2px rgba(0,0,0,.2);border:none}.elementor-metabox-content .elementor-button:active{box-shadow:0 5px 10px rgba(0,0,0,.19),0 3px 3px rgba(0,0,0,.1)}.elementor-metabox-content .elementor-button:not([disabled]){cursor:pointer}.elementor-metabox-content .elementor-button.elementor-size-xs{font-size:11px;padding:10px 20px;border-radius:2px}.elementor-metabox-content .elementor-button.elementor-size-sm{font-size:13px;padding:12px 24px;border-radius:3px}.elementor-metabox-content .elementor-button.elementor-size-md{font-size:14px;padding:15px 30px;border-radius:4px}.elementor-metabox-content .elementor-button.elementor-size-lg{font-size:15px;padding:20px 40px;border-radius:5px}.elementor-metabox-content .elementor-button.elementor-size-xl{font-size:18px;padding:25px 50px;border-radius:6px}.elementor-metabox-content .elementor-button .elementor-align-icon-right{float:right;margin-left:5px}.elementor-metabox-content .elementor-button .elementor-align-icon-left{float:left;margin-right:5px}.elementor-metabox-content input.button.add-repeater-row{margin-block-start:18px;border:none;box-shadow:none}.elementor-metabox-content .elementor-repeater-tool-btn{color:#9da5ae;cursor:pointer;padding:0 20px;font-size:12px;transition:all .3s}.elementor-metabox-content .elementor-repeater-tool-btn i{padding-inline-end:5px}.elementor-metabox-content .elementor-repeater-tool-btn:hover{color:#3f444b}.elementor-metabox-content .elementor-repeater-tool-btn.remove-repeater-row:hover{color:#f59e0b}.elementor-metabox-content .inline-preview,.elementor-metabox-content .row-font-preview{font-size:16px;text-transform:capitalize}.column-font_preview{width:65%}.widefat td.column-font_preview{font-size:16px}.post-type-elementor_icons .elementor-metabox-content .elementor-button:not([disabled]){margin-block-start:10px}.post-type-elementor_icons div#postbox-container-1{display:none}.post-type-elementor_icons div#elementor-custom-icons-metabox{display:none;border:1px solid #f1f2f3;border-radius:1px;background-color:#fff}.post-type-elementor_icons div#elementor-custom-icons-metabox .inside{margin-block-start:10px;margin-block-end:20px}.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-metabox-content{background-color:#fff}.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-custom-icons-metabox{padding-block-start:4px;padding-block-end:10px;padding-inline-start:10px;padding-inline-end:10px}@media (max-width:1025px){.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-custom-icons-metabox{padding:0}}.post-type-elementor_icons div#elementor-custom-icons-metabox h4{color:#1f2124;font-size:22px;font-weight:500;letter-spacing:.7px;line-height:28px;margin:0 0 4px}.post-type-elementor_icons div#elementor-custom-icons-metabox h5{color:#9da5ae;font-size:16px;font-weight:500;letter-spacing:.5px;line-height:21px;margin:0}.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor--dropzone--upload__icon i{font-size:64px;color:#0a875a}.post-type-elementor_icons div#elementor-custom-icons-metabox .box__error,.post-type-elementor_icons div#elementor-custom-icons-metabox .box__file,.post-type-elementor_icons div#elementor-custom-icons-metabox .box__success,.post-type-elementor_icons div#elementor-custom-icons-metabox .box__uploading{display:none}.post-type-elementor_icons div#elementor-custom-icons-metabox .is-dragover{background-color:grey}.post-type-elementor_icons div#elementor-custom-icons-metabox .box__input{padding:180px 0;display:flex;flex-direction:column;align-items:center}.post-type-elementor_icons div#elementor-custom-icons-metabox .elementor-field-dropzone{outline:2px dashed #d5d8dc;outline-offset:-3px;background-color:#fff;display:none}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons{background-color:#f9fafa;border:1px solid #f1f2f3;border-radius:1px}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-metabox-content{background-color:#f9fafa}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-metabox-content .elementor-custom-icons-metabox{padding-block-start:4px;padding-block-end:0;padding-inline-start:10px;padding-inline-end:10px}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header{height:50px;color:#3f444b;background-color:#fff;box-shadow:0 2px 6px 0 rgba(0,0,0,.06);padding:0 35px;display:flex;align-items:center;justify-content:flex-start}@media (max-width:1025px){.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header{padding:0 6px}}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div{padding-inline-end:10px;padding-inline-start:10px}@media (max-width:1025px){.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div{line-height:1}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div.remove{font-size:10px}}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header div:nth-of-type(2){border:1px solid #9da5ae;border-block-start:0;border-block-end:0}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta{color:#1f2124;font-size:14px;line-height:1}@media (max-width:1025px){.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta{font-size:10px}}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-value{font-weight:700}@media (max-width:1025px){.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-value{font-size:10px}}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove{margin-inline-start:auto;color:#1f2124;opacity:.6;cursor:pointer;transition:all .3s}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove i{color:#3f444b}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-header-meta-remove:hover{opacity:1}.post-type-elementor_icons div#elementor-custom-icons-metabox.elementor--has-icons .elementor-icon-set-footer{color:#babfc5;font-family:Roboto,Arial,Helvetica,sans-serif;border-block-start:1px solid #f1f2f3;font-size:11px;font-weight:500;line-height:1;text-align:end;padding-block-start:10px;padding-block-end:10px;padding-inline-end:35px}.post-type-elementor_icons div#elementor-custom-icons-metabox ul{display:grid;grid-template-columns:repeat(auto-fill,minmax(105px,1fr));grid-gap:20px;padding-block-start:15px;padding-block-end:0;padding-inline-start:35px;padding-inline-end:35px;overflow-y:auto;max-height:575px}.post-type-elementor_icons div#elementor-custom-icons-metabox ul li{position:relative;height:0;padding-block-end:100%;background-color:#fff;box-shadow:0 1px 12px rgba(0,0,0,.05);border-radius:3px;overflow:hidden}.post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon{display:flex;flex-direction:column;align-items:center;width:100%;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);padding:1px}.post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon-name{color:#babfc5;font-size:11px;padding:18px 20px 0;white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}@media (max-width:479px){.post-type-elementor_icons div#elementor-custom-icons-metabox ul li div.icon-name{display:none}}.post-type-elementor_icons div#elementor-custom-icons-metabox ul li i{font-size:32px}.post-type-elementor_icons #minor-publishing-actions,.post-type-elementor_icons #misc-publishing-actions,.post-type-elementor_icons #tagsdiv-elementor_icon_type{display:none}.column-icons_prefix{width:65%} \ No newline at end of file diff --git a/assets/css/app-rtl.css b/assets/css/app-rtl.css new file mode 100644 index 0000000..01c7193 --- /dev/null +++ b/assets/css/app-rtl.css @@ -0,0 +1,359 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +:root { + --color-box-shadow-color: rgba(0, 0, 0, 0.05); +} + +.eps-theme-dark { + --color-box-shadow-color: rgba(0, 0, 0, 0.1); +} + +:root { + --eps-meta-icon-color: #818A96; +} + +.eps-theme-dark { + --eps-meta-icon-color: #BABFC5; +} + +.e-site-template__meta-data { + margin-inline-start: 0.625rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.6875rem; +} +.e-site-template__meta-data:last-of-type { + margin-inline-end: auto; +} +.e-site-template__meta-data:first-of-type { + margin-inline-start: 1rem; +} +.e-site-template__meta-data .eps-icon { + margin-inline-end: 0.3125rem; + color: var(--eps-meta-icon-color); + font-size: 0.8125rem; +} +.e-site-template__placeholder .eps-card__image { + filter: var(--placeholder-filter, none); +} +.e-site-template__overlay-preview { + padding-block-start: calc(var(--card-image-aspect-ratio, 95.6%) - 1.875rem); + position: relative; +} +.e-site-template__overlay-preview-button { + font-weight: bold; + display: flex; + flex-wrap: wrap; + height: 1.875rem; + width: 100%; + background-color: var(--card-background-color-hover); + justify-content: center; + align-items: center; + padding-block-start: 0.625rem; + line-height: 1.25rem; + --button-background-color: var(--card-headline-color); +} +.e-site-template__overlay-preview-button::before { + content: ""; + position: absolute; + display: block; + width: 100%; + top: 0; + left: 0; + padding-block-start: calc(var(--card-image-aspect-ratio, 95.6%) - 1.875rem); +} +.e-site-template__overlay-preview-button > :not(:first-child) { + margin-inline-start: 0.3125rem; +} +.e-site-template__edit-btn { + margin-inline-end: 1.25rem; +} +.e-site-template__edit-btn .eps-icon { + margin-inline-end: 0.3125rem; +} +.e-site-template__instances .eps-icon { + margin-inline-end: 0.3125rem; + color: var(--eps-meta-icon-color); + font-size: 0.8125rem; +} +.e-site-template__instances-list { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.e-site-template__edit-conditions { + margin-inline-start: 1rem; + text-decoration: underline; + font-style: italic; +} +.e-site-template--extended .eps-card__figure { + overflow: auto; +} +.e-site-template--extended .eps-card__headline { + flex-grow: 0; +} +.e-site-template--wide { + --card-image-aspect-ratio: 12.35%; +} + +.eps-add-new__overlay { + display: flex; + align-items: center; + justify-content: center; + opacity: 1; + --card-image-overlay-background-color: transparent; +} + +.e-site-editor__templates .page-header { + margin-block-end: 0.625rem; +} +.e-site-editor__templates .page-header > a { + align-self: baseline; +} +.e-site-editor__templates .eps-separator { + margin-block-end: 2.75rem; +} + +:root { + --e-site-editor-conditions-row-controls-background: #ffffff; + --e-site-editor-input-wrapper-border-color: #D5D8DC; + --e-site-editor-input-wrapper-select-color: #3f444b; + --e-site-editor-conditions-row-controls-border: 1px solid #D5D8DC; + --e-site-editor-add-button-background-color: #69727D; + --e-site-editor-add-button-color-hover-background-color: #515962; + --e-site-editor-input-wrapper-condition-include-background-color: + #69727D; + --e-site-editor-input-wrapper-condition-exclude-background-color: + #818A96; + --e-site-editor-input-select2-search-field-color: #515962 ; +} + +.eps-theme-dark { + --select2-selection-background-color: tints(600); + --e-site-editor-conditions-row-controls-background: #515962; + --e-site-editor-input-wrapper-border-color: #3f444b; + --e-site-editor-input-wrapper-select-color: #BABFC5; + --e-site-editor-conditions-row-controls-border: 1px solid #3f444b; + --e-site-editor-add-button-background-color: #69727D; + --e-site-editor-add-button-color-hover-background-color: #515962; + --e-site-editor-input-wrapper-condition-include-background-color: + #515962; + --e-site-editor-input-wrapper-condition-exclude-background-color: + #515962; + --e-site-editor-input-select2-search-field-color: #ffffff ; +} + +.e-site-editor-conditions__header { + text-align: center; +} +.e-site-editor-conditions__header-image { + display: block; + margin: 0 auto 2.75rem; + width: 4.375rem; +} +.e-site-editor-conditions__rows { + margin: 2.75rem auto; + max-width: 43.75rem; +} +.e-site-editor-conditions__row { + display: flex; + flex-grow: 1; + margin-block-start: 0.75rem; +} +.e-site-editor-conditions__remove-condition { + color: #818A96; + font-size: 1.125rem; + display: flex; + align-items: center; + justify-content: center; +} +.e-site-editor-conditions__row-controls { + overflow: hidden; + margin-inline-end: 0.625rem; + background-color: var(--e-site-editor-conditions-row-controls-background); + display: flex; + width: 100%; + border: var(--e-site-editor-conditions-row-controls-border); + border-radius: 0.1875rem; +} +.e-site-editor-conditions__row-controls--error { + border: 1px solid #DC2626; +} +.e-site-editor-conditions__conflict { + text-align: center; + margin-block-start: 0.3125rem; + color: #DC2626; +} +.e-site-editor-conditions__row-controls-inner { + width: 100%; + display: flex; +} +.e-site-editor-conditions__row-controls-inner div { + flex: 1; +} +.e-site-editor-conditions__add-button-container { + text-align: center; +} +.e-site-editor-conditions__add-button { + margin-block-start: 2.75rem; + background-color: var(--e-site-editor-add-button-background-color); + color: #ffffff; + text-transform: uppercase; +} +.e-site-editor-conditions__add-button:hover { + background-color: var(--e-site-editor-add-button-color-hover-background-color); + color: #ffffff; +} +.e-site-editor-conditions__footer { + display: flex; + justify-content: flex-end; + position: absolute; + bottom: 0; + right: 0; + left: 0; + padding: 0.5rem; + border-block-start: 1px solid var(--hr-color); +} +.e-site-editor-conditions__input-wrapper { + position: relative; + padding-inline-start: 1px solid; + border-color: var(--e-site-editor-input-wrapper-border-color); +} +.e-site-editor-conditions__input-wrapper:first-child { + border: none; +} +.e-site-editor-conditions__input-wrapper select { + -moz-appearance: none; + appearance: none; + -webkit-appearance: none; + font-size: 0.75rem; + height: 2.5rem; + border-width: 0; + padding: 0 0.625rem; + width: 100%; + position: relative; + color: var(--e-site-editor-input-wrapper-select-color); + outline: none; + background: transparent; +} +.e-site-editor-conditions__input-wrapper:after { + font-family: eicons; + content: "\e8ad"; + font-size: 0.75rem; + pointer-events: none; + position: absolute; + top: 50%; + transform: translateY(-50%); + left: 0.625rem; +} +.e-site-editor-conditions__input-wrapper .select2-container--default .select2-selection--single { + border: none; + line-height: 2.5rem; +} +.e-site-editor-conditions__input-wrapper .select2-container--default .select2-selection--single .select2-selection__rendered { + line-height: 2.5rem; + font-size: 0.75rem; +} +.e-site-editor-conditions__input-wrapper .select2-selection { + outline: none; + background: transparent; + height: 2.5rem; +} +.e-site-editor-conditions__input-wrapper .select2-selection__arrow { + display: none; +} +.e-site-editor-conditions__input-wrapper--condition-type { + position: relative; +} +.e-site-editor-conditions__input-wrapper--condition-type:before { + font-family: eicons; + position: absolute; + top: 50%; + transform: translateY(-50%); + right: 0.75rem; + font-size: 0.9375rem; + pointer-events: none; + z-index: 1000; +} +.e-site-editor-conditions__input-wrapper--condition-type select { + text-transform: uppercase; + padding-inline-start: 2.125rem; + width: 7.5rem; + font-size: 0.75rem; + border-inline-end: 1px solid; + border-color: var(--e-site-editor-input-wrapper-border-color); +} +.e-site-editor-conditions__input-wrapper--condition-type[data-elementor-condition-type=include]:before { + content: "\e8cc"; +} +.e-site-editor-conditions__input-wrapper--condition-type[data-elementor-condition-type=exclude]:before { + content: "\e8cd"; +} + +.select2-search__field { + background-color: transparent; + color: var(--e-site-editor-input-select2-search-field-color); +} + +.eps-back-button { + font-size: 14px; + margin-block-end: 1.5rem; +} +.eps-back-button .eps-icon { + transform: rotate(180deg); +} + +:root { + --indicator-bullet-border-color: #ffffff; +} + +.eps-theme-dark { + --indicator-bullet-border-color: #69727D; +} + +.eps-indicator-bullet { + display: block; + flex-shrink: 0; + width: 0.75rem; + height: 0.75rem; + box-shadow: 0 2px 3px 1px var(--color-box-shadow-color); + background-color: #9DA5AE; + border: 2px solid var(--indicator-bullet-border-color); + border-radius: 100%; + margin-inline-end: 0.625rem; +} +.eps-indicator-bullet--active { + background-color: #0A875A; +} + +.site-editor__preview-iframe { + height: 50vh; + position: relative; +} +.site-editor__preview-iframe__iframe { + top: 0; + left: 0; + position: absolute; + border: none; + transform-origin: 0 0; + height: 100%; +} +.site-editor__preview-iframe--header, .site-editor__preview-iframe--footer { + height: 15vh; +} + +.e-site-editor__content_container { + flex-direction: column; + min-height: 100%; + flex-wrap: nowrap; +} + +.e-site-editor__content_container_main { + flex: 1; +} + +.e-site-editor__content_container_secondary { + margin: 0 auto; + padding-block-start: 2.75rem; +} +/*# sourceMappingURL=app-rtl.css.map */ \ No newline at end of file diff --git a/assets/css/app-rtl.min.css b/assets/css/app-rtl.min.css new file mode 100644 index 0000000..1df44c9 --- /dev/null +++ b/assets/css/app-rtl.min.css @@ -0,0 +1,2 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +:root{--color-box-shadow-color:rgba(0,0,0,0.05)}.eps-theme-dark{--color-box-shadow-color:rgba(0,0,0,0.1)}:root{--eps-meta-icon-color:#818a96}.eps-theme-dark{--eps-meta-icon-color:#babfc5}.e-site-template__meta-data{margin-inline-start:.625rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.6875rem}.e-site-template__meta-data:last-of-type{margin-inline-end:auto}.e-site-template__meta-data:first-of-type{margin-inline-start:1rem}.e-site-template__meta-data .eps-icon{margin-inline-end:.3125rem;color:var(--eps-meta-icon-color);font-size:.8125rem}.e-site-template__placeholder .eps-card__image{filter:var(--placeholder-filter,none)}.e-site-template__overlay-preview{padding-block-start:calc(var(--card-image-aspect-ratio, 95.6%) - 1.875rem);position:relative}.e-site-template__overlay-preview-button{font-weight:700;display:flex;flex-wrap:wrap;height:1.875rem;width:100%;background-color:var(--card-background-color-hover);justify-content:center;align-items:center;padding-block-start:.625rem;line-height:1.25rem;--button-background-color:var(--card-headline-color)}.e-site-template__overlay-preview-button:before{content:"";position:absolute;display:block;width:100%;top:0;left:0;padding-block-start:calc(var(--card-image-aspect-ratio, 95.6%) - 1.875rem)}.e-site-template__overlay-preview-button>:not(:first-child){margin-inline-start:.3125rem}.e-site-template__edit-btn{margin-inline-end:1.25rem}.e-site-template__edit-btn .eps-icon{margin-inline-end:.3125rem}.e-site-template__instances .eps-icon{margin-inline-end:.3125rem;color:var(--eps-meta-icon-color);font-size:.8125rem}.e-site-template__instances-list{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.e-site-template__edit-conditions{margin-inline-start:1rem;text-decoration:underline;font-style:italic}.e-site-template--extended .eps-card__figure{overflow:auto}.e-site-template--extended .eps-card__headline{flex-grow:0}.e-site-template--wide{--card-image-aspect-ratio:12.35%}.eps-add-new__overlay{display:flex;align-items:center;justify-content:center;opacity:1;--card-image-overlay-background-color:transparent}.e-site-editor__templates .page-header{margin-block-end:.625rem}.e-site-editor__templates .page-header>a{align-self:baseline}.e-site-editor__templates .eps-separator{margin-block-end:2.75rem}:root{--e-site-editor-conditions-row-controls-background:#fff;--e-site-editor-input-wrapper-border-color:#d5d8dc;--e-site-editor-input-wrapper-select-color:#3f444b;--e-site-editor-conditions-row-controls-border:1px solid #d5d8dc;--e-site-editor-add-button-background-color:#69727d;--e-site-editor-add-button-color-hover-background-color:#515962;--e-site-editor-input-wrapper-condition-include-background-color:#69727d;--e-site-editor-input-wrapper-condition-exclude-background-color:#818a96;--e-site-editor-input-select2-search-field-color:#515962 }.eps-theme-dark{--select2-selection-background-color:tints(600);--e-site-editor-conditions-row-controls-background:#515962;--e-site-editor-input-wrapper-border-color:#3f444b;--e-site-editor-input-wrapper-select-color:#babfc5;--e-site-editor-conditions-row-controls-border:1px solid #3f444b;--e-site-editor-add-button-background-color:#69727d;--e-site-editor-add-button-color-hover-background-color:#515962;--e-site-editor-input-wrapper-condition-include-background-color:#515962;--e-site-editor-input-wrapper-condition-exclude-background-color:#515962;--e-site-editor-input-select2-search-field-color:#fff }.e-site-editor-conditions__header{text-align:center}.e-site-editor-conditions__header-image{display:block;margin:0 auto 2.75rem;width:4.375rem}.e-site-editor-conditions__rows{margin:2.75rem auto;max-width:43.75rem}.e-site-editor-conditions__row{display:flex;flex-grow:1;margin-block-start:.75rem}.e-site-editor-conditions__remove-condition{color:#818a96;font-size:1.125rem;display:flex;align-items:center;justify-content:center}.e-site-editor-conditions__row-controls{overflow:hidden;margin-inline-end:.625rem;background-color:var(--e-site-editor-conditions-row-controls-background);display:flex;width:100%;border:var(--e-site-editor-conditions-row-controls-border);border-radius:.1875rem}.e-site-editor-conditions__row-controls--error{border:1px solid #dc2626}.e-site-editor-conditions__conflict{text-align:center;margin-block-start:.3125rem;color:#dc2626}.e-site-editor-conditions__row-controls-inner{width:100%;display:flex}.e-site-editor-conditions__row-controls-inner div{flex:1}.e-site-editor-conditions__add-button-container{text-align:center}.e-site-editor-conditions__add-button{margin-block-start:2.75rem;background-color:var(--e-site-editor-add-button-background-color);color:#fff;text-transform:uppercase}.e-site-editor-conditions__add-button:hover{background-color:var(--e-site-editor-add-button-color-hover-background-color);color:#fff}.e-site-editor-conditions__footer{display:flex;justify-content:flex-end;position:absolute;bottom:0;right:0;left:0;padding:.5rem;border-block-start:1px solid var(--hr-color)}.e-site-editor-conditions__input-wrapper{position:relative;padding-inline-start:1px solid;border-color:var(--e-site-editor-input-wrapper-border-color)}.e-site-editor-conditions__input-wrapper:first-child{border:none}.e-site-editor-conditions__input-wrapper select{-moz-appearance:none;appearance:none;-webkit-appearance:none;font-size:.75rem;height:2.5rem;border-width:0;padding:0 .625rem;width:100%;position:relative;color:var(--e-site-editor-input-wrapper-select-color);outline:none;background:transparent}.e-site-editor-conditions__input-wrapper:after{font-family:eicons;content:"\e8ad";font-size:.75rem;pointer-events:none;position:absolute;top:50%;transform:translateY(-50%);left:.625rem}.e-site-editor-conditions__input-wrapper .select2-container--default .select2-selection--single{border:none;line-height:2.5rem}.e-site-editor-conditions__input-wrapper .select2-container--default .select2-selection--single .select2-selection__rendered{line-height:2.5rem;font-size:.75rem}.e-site-editor-conditions__input-wrapper .select2-selection{outline:none;background:transparent;height:2.5rem}.e-site-editor-conditions__input-wrapper .select2-selection__arrow{display:none}.e-site-editor-conditions__input-wrapper--condition-type{position:relative}.e-site-editor-conditions__input-wrapper--condition-type:before{font-family:eicons;position:absolute;top:50%;transform:translateY(-50%);right:.75rem;font-size:.9375rem;pointer-events:none;z-index:1000}.e-site-editor-conditions__input-wrapper--condition-type select{text-transform:uppercase;padding-inline-start:2.125rem;width:7.5rem;font-size:.75rem;border-inline-end:1px solid;border-color:var(--e-site-editor-input-wrapper-border-color)}.e-site-editor-conditions__input-wrapper--condition-type[data-elementor-condition-type=include]:before{content:"\e8cc"}.e-site-editor-conditions__input-wrapper--condition-type[data-elementor-condition-type=exclude]:before{content:"\e8cd"}.select2-search__field{background-color:transparent;color:var(--e-site-editor-input-select2-search-field-color)}.eps-back-button{font-size:14px;margin-block-end:1.5rem}.eps-back-button .eps-icon{transform:rotate(180deg)}:root{--indicator-bullet-border-color:#fff}.eps-theme-dark{--indicator-bullet-border-color:#69727d}.eps-indicator-bullet{display:block;flex-shrink:0;width:.75rem;height:.75rem;box-shadow:0 2px 3px 1px var(--color-box-shadow-color);background-color:#9da5ae;border:2px solid var(--indicator-bullet-border-color);border-radius:100%;margin-inline-end:.625rem}.eps-indicator-bullet--active{background-color:#0a875a}.site-editor__preview-iframe{height:50vh;position:relative}.site-editor__preview-iframe__iframe{top:0;left:0;position:absolute;border:none;transform-origin:0 0;height:100%}.site-editor__preview-iframe--footer,.site-editor__preview-iframe--header{height:15vh}.e-site-editor__content_container{flex-direction:column;min-height:100%;flex-wrap:nowrap}.e-site-editor__content_container_main{flex:1}.e-site-editor__content_container_secondary{margin:0 auto;padding-block-start:2.75rem} \ No newline at end of file diff --git a/assets/css/app.css b/assets/css/app.css new file mode 100644 index 0000000..821f387 --- /dev/null +++ b/assets/css/app.css @@ -0,0 +1,359 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +:root { + --color-box-shadow-color: rgba(0, 0, 0, 0.05); +} + +.eps-theme-dark { + --color-box-shadow-color: rgba(0, 0, 0, 0.1); +} + +:root { + --eps-meta-icon-color: #818A96; +} + +.eps-theme-dark { + --eps-meta-icon-color: #BABFC5; +} + +.e-site-template__meta-data { + margin-inline-start: 0.625rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.6875rem; +} +.e-site-template__meta-data:last-of-type { + margin-inline-end: auto; +} +.e-site-template__meta-data:first-of-type { + margin-inline-start: 1rem; +} +.e-site-template__meta-data .eps-icon { + margin-inline-end: 0.3125rem; + color: var(--eps-meta-icon-color); + font-size: 0.8125rem; +} +.e-site-template__placeholder .eps-card__image { + filter: var(--placeholder-filter, none); +} +.e-site-template__overlay-preview { + padding-block-start: calc(var(--card-image-aspect-ratio, 95.6%) - 1.875rem); + position: relative; +} +.e-site-template__overlay-preview-button { + font-weight: bold; + display: flex; + flex-wrap: wrap; + height: 1.875rem; + width: 100%; + background-color: var(--card-background-color-hover); + justify-content: center; + align-items: center; + padding-block-start: 0.625rem; + line-height: 1.25rem; + --button-background-color: var(--card-headline-color); +} +.e-site-template__overlay-preview-button::before { + content: ""; + position: absolute; + display: block; + width: 100%; + top: 0; + left: 0; + padding-block-start: calc(var(--card-image-aspect-ratio, 95.6%) - 1.875rem); +} +.e-site-template__overlay-preview-button > :not(:first-child) { + margin-inline-start: 0.3125rem; +} +.e-site-template__edit-btn { + margin-inline-end: 1.25rem; +} +.e-site-template__edit-btn .eps-icon { + margin-inline-end: 0.3125rem; +} +.e-site-template__instances .eps-icon { + margin-inline-end: 0.3125rem; + color: var(--eps-meta-icon-color); + font-size: 0.8125rem; +} +.e-site-template__instances-list { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.e-site-template__edit-conditions { + margin-inline-start: 1rem; + text-decoration: underline; + font-style: italic; +} +.e-site-template--extended .eps-card__figure { + overflow: auto; +} +.e-site-template--extended .eps-card__headline { + flex-grow: 0; +} +.e-site-template--wide { + --card-image-aspect-ratio: 12.35%; +} + +.eps-add-new__overlay { + display: flex; + align-items: center; + justify-content: center; + opacity: 1; + --card-image-overlay-background-color: transparent; +} + +.e-site-editor__templates .page-header { + margin-block-end: 0.625rem; +} +.e-site-editor__templates .page-header > a { + align-self: baseline; +} +.e-site-editor__templates .eps-separator { + margin-block-end: 2.75rem; +} + +:root { + --e-site-editor-conditions-row-controls-background: #ffffff; + --e-site-editor-input-wrapper-border-color: #D5D8DC; + --e-site-editor-input-wrapper-select-color: #3f444b; + --e-site-editor-conditions-row-controls-border: 1px solid #D5D8DC; + --e-site-editor-add-button-background-color: #69727D; + --e-site-editor-add-button-color-hover-background-color: #515962; + --e-site-editor-input-wrapper-condition-include-background-color: + #69727D; + --e-site-editor-input-wrapper-condition-exclude-background-color: + #818A96; + --e-site-editor-input-select2-search-field-color: #515962 ; +} + +.eps-theme-dark { + --select2-selection-background-color: tints(600); + --e-site-editor-conditions-row-controls-background: #515962; + --e-site-editor-input-wrapper-border-color: #3f444b; + --e-site-editor-input-wrapper-select-color: #BABFC5; + --e-site-editor-conditions-row-controls-border: 1px solid #3f444b; + --e-site-editor-add-button-background-color: #69727D; + --e-site-editor-add-button-color-hover-background-color: #515962; + --e-site-editor-input-wrapper-condition-include-background-color: + #515962; + --e-site-editor-input-wrapper-condition-exclude-background-color: + #515962; + --e-site-editor-input-select2-search-field-color: #ffffff ; +} + +.e-site-editor-conditions__header { + text-align: center; +} +.e-site-editor-conditions__header-image { + display: block; + margin: 0 auto 2.75rem; + width: 4.375rem; +} +.e-site-editor-conditions__rows { + margin: 2.75rem auto; + max-width: 43.75rem; +} +.e-site-editor-conditions__row { + display: flex; + flex-grow: 1; + margin-block-start: 0.75rem; +} +.e-site-editor-conditions__remove-condition { + color: #818A96; + font-size: 1.125rem; + display: flex; + align-items: center; + justify-content: center; +} +.e-site-editor-conditions__row-controls { + overflow: hidden; + margin-inline-end: 0.625rem; + background-color: var(--e-site-editor-conditions-row-controls-background); + display: flex; + width: 100%; + border: var(--e-site-editor-conditions-row-controls-border); + border-radius: 0.1875rem; +} +.e-site-editor-conditions__row-controls--error { + border: 1px solid #DC2626; +} +.e-site-editor-conditions__conflict { + text-align: center; + margin-block-start: 0.3125rem; + color: #DC2626; +} +.e-site-editor-conditions__row-controls-inner { + width: 100%; + display: flex; +} +.e-site-editor-conditions__row-controls-inner div { + flex: 1; +} +.e-site-editor-conditions__add-button-container { + text-align: center; +} +.e-site-editor-conditions__add-button { + margin-block-start: 2.75rem; + background-color: var(--e-site-editor-add-button-background-color); + color: #ffffff; + text-transform: uppercase; +} +.e-site-editor-conditions__add-button:hover { + background-color: var(--e-site-editor-add-button-color-hover-background-color); + color: #ffffff; +} +.e-site-editor-conditions__footer { + display: flex; + justify-content: flex-end; + position: absolute; + bottom: 0; + right: 0; + left: 0; + padding: 0.5rem; + border-block-start: 1px solid var(--hr-color); +} +.e-site-editor-conditions__input-wrapper { + position: relative; + padding-inline-start: 1px solid; + border-color: var(--e-site-editor-input-wrapper-border-color); +} +.e-site-editor-conditions__input-wrapper:first-child { + border: none; +} +.e-site-editor-conditions__input-wrapper select { + -moz-appearance: none; + appearance: none; + -webkit-appearance: none; + font-size: 0.75rem; + height: 2.5rem; + border-width: 0; + padding: 0 0.625rem; + width: 100%; + position: relative; + color: var(--e-site-editor-input-wrapper-select-color); + outline: none; + background: transparent; +} +.e-site-editor-conditions__input-wrapper:after { + font-family: eicons; + content: "\e8ad"; + font-size: 0.75rem; + pointer-events: none; + position: absolute; + top: 50%; + transform: translateY(-50%); + right: 0.625rem; +} +.e-site-editor-conditions__input-wrapper .select2-container--default .select2-selection--single { + border: none; + line-height: 2.5rem; +} +.e-site-editor-conditions__input-wrapper .select2-container--default .select2-selection--single .select2-selection__rendered { + line-height: 2.5rem; + font-size: 0.75rem; +} +.e-site-editor-conditions__input-wrapper .select2-selection { + outline: none; + background: transparent; + height: 2.5rem; +} +.e-site-editor-conditions__input-wrapper .select2-selection__arrow { + display: none; +} +.e-site-editor-conditions__input-wrapper--condition-type { + position: relative; +} +.e-site-editor-conditions__input-wrapper--condition-type:before { + font-family: eicons; + position: absolute; + top: 50%; + transform: translateY(-50%); + left: 0.75rem; + font-size: 0.9375rem; + pointer-events: none; + z-index: 1000; +} +.e-site-editor-conditions__input-wrapper--condition-type select { + text-transform: uppercase; + padding-inline-start: 2.125rem; + width: 7.5rem; + font-size: 0.75rem; + border-inline-end: 1px solid; + border-color: var(--e-site-editor-input-wrapper-border-color); +} +.e-site-editor-conditions__input-wrapper--condition-type[data-elementor-condition-type=include]:before { + content: "\e8cc"; +} +.e-site-editor-conditions__input-wrapper--condition-type[data-elementor-condition-type=exclude]:before { + content: "\e8cd"; +} + +.select2-search__field { + background-color: transparent; + color: var(--e-site-editor-input-select2-search-field-color); +} + +.eps-back-button { + font-size: 14px; + margin-block-end: 1.5rem; +} +.eps-back-button .eps-icon { + transform: rotate(0deg); +} + +:root { + --indicator-bullet-border-color: #ffffff; +} + +.eps-theme-dark { + --indicator-bullet-border-color: #69727D; +} + +.eps-indicator-bullet { + display: block; + flex-shrink: 0; + width: 0.75rem; + height: 0.75rem; + box-shadow: 0 2px 3px 1px var(--color-box-shadow-color); + background-color: #9DA5AE; + border: 2px solid var(--indicator-bullet-border-color); + border-radius: 100%; + margin-inline-end: 0.625rem; +} +.eps-indicator-bullet--active { + background-color: #0A875A; +} + +.site-editor__preview-iframe { + height: 50vh; + position: relative; +} +.site-editor__preview-iframe__iframe { + top: 0; + left: 0; + position: absolute; + border: none; + transform-origin: 0 0; + height: 100%; +} +.site-editor__preview-iframe--header, .site-editor__preview-iframe--footer { + height: 15vh; +} + +.e-site-editor__content_container { + flex-direction: column; + min-height: 100%; + flex-wrap: nowrap; +} + +.e-site-editor__content_container_main { + flex: 1; +} + +.e-site-editor__content_container_secondary { + margin: 0 auto; + padding-block-start: 2.75rem; +} +/*# sourceMappingURL=app.css.map */ \ No newline at end of file diff --git a/assets/css/app.min.css b/assets/css/app.min.css new file mode 100644 index 0000000..bc52788 --- /dev/null +++ b/assets/css/app.min.css @@ -0,0 +1,2 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +:root{--color-box-shadow-color:rgba(0,0,0,0.05)}.eps-theme-dark{--color-box-shadow-color:rgba(0,0,0,0.1)}:root{--eps-meta-icon-color:#818a96}.eps-theme-dark{--eps-meta-icon-color:#babfc5}.e-site-template__meta-data{margin-inline-start:.625rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.6875rem}.e-site-template__meta-data:last-of-type{margin-inline-end:auto}.e-site-template__meta-data:first-of-type{margin-inline-start:1rem}.e-site-template__meta-data .eps-icon{margin-inline-end:.3125rem;color:var(--eps-meta-icon-color);font-size:.8125rem}.e-site-template__placeholder .eps-card__image{filter:var(--placeholder-filter,none)}.e-site-template__overlay-preview{padding-block-start:calc(var(--card-image-aspect-ratio, 95.6%) - 1.875rem);position:relative}.e-site-template__overlay-preview-button{font-weight:700;display:flex;flex-wrap:wrap;height:1.875rem;width:100%;background-color:var(--card-background-color-hover);justify-content:center;align-items:center;padding-block-start:.625rem;line-height:1.25rem;--button-background-color:var(--card-headline-color)}.e-site-template__overlay-preview-button:before{content:"";position:absolute;display:block;width:100%;top:0;left:0;padding-block-start:calc(var(--card-image-aspect-ratio, 95.6%) - 1.875rem)}.e-site-template__overlay-preview-button>:not(:first-child){margin-inline-start:.3125rem}.e-site-template__edit-btn{margin-inline-end:1.25rem}.e-site-template__edit-btn .eps-icon{margin-inline-end:.3125rem}.e-site-template__instances .eps-icon{margin-inline-end:.3125rem;color:var(--eps-meta-icon-color);font-size:.8125rem}.e-site-template__instances-list{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.e-site-template__edit-conditions{margin-inline-start:1rem;text-decoration:underline;font-style:italic}.e-site-template--extended .eps-card__figure{overflow:auto}.e-site-template--extended .eps-card__headline{flex-grow:0}.e-site-template--wide{--card-image-aspect-ratio:12.35%}.eps-add-new__overlay{display:flex;align-items:center;justify-content:center;opacity:1;--card-image-overlay-background-color:transparent}.e-site-editor__templates .page-header{margin-block-end:.625rem}.e-site-editor__templates .page-header>a{align-self:baseline}.e-site-editor__templates .eps-separator{margin-block-end:2.75rem}:root{--e-site-editor-conditions-row-controls-background:#fff;--e-site-editor-input-wrapper-border-color:#d5d8dc;--e-site-editor-input-wrapper-select-color:#3f444b;--e-site-editor-conditions-row-controls-border:1px solid #d5d8dc;--e-site-editor-add-button-background-color:#69727d;--e-site-editor-add-button-color-hover-background-color:#515962;--e-site-editor-input-wrapper-condition-include-background-color:#69727d;--e-site-editor-input-wrapper-condition-exclude-background-color:#818a96;--e-site-editor-input-select2-search-field-color:#515962 }.eps-theme-dark{--select2-selection-background-color:tints(600);--e-site-editor-conditions-row-controls-background:#515962;--e-site-editor-input-wrapper-border-color:#3f444b;--e-site-editor-input-wrapper-select-color:#babfc5;--e-site-editor-conditions-row-controls-border:1px solid #3f444b;--e-site-editor-add-button-background-color:#69727d;--e-site-editor-add-button-color-hover-background-color:#515962;--e-site-editor-input-wrapper-condition-include-background-color:#515962;--e-site-editor-input-wrapper-condition-exclude-background-color:#515962;--e-site-editor-input-select2-search-field-color:#fff }.e-site-editor-conditions__header{text-align:center}.e-site-editor-conditions__header-image{display:block;margin:0 auto 2.75rem;width:4.375rem}.e-site-editor-conditions__rows{margin:2.75rem auto;max-width:43.75rem}.e-site-editor-conditions__row{display:flex;flex-grow:1;margin-block-start:.75rem}.e-site-editor-conditions__remove-condition{color:#818a96;font-size:1.125rem;display:flex;align-items:center;justify-content:center}.e-site-editor-conditions__row-controls{overflow:hidden;margin-inline-end:.625rem;background-color:var(--e-site-editor-conditions-row-controls-background);display:flex;width:100%;border:var(--e-site-editor-conditions-row-controls-border);border-radius:.1875rem}.e-site-editor-conditions__row-controls--error{border:1px solid #dc2626}.e-site-editor-conditions__conflict{text-align:center;margin-block-start:.3125rem;color:#dc2626}.e-site-editor-conditions__row-controls-inner{width:100%;display:flex}.e-site-editor-conditions__row-controls-inner div{flex:1}.e-site-editor-conditions__add-button-container{text-align:center}.e-site-editor-conditions__add-button{margin-block-start:2.75rem;background-color:var(--e-site-editor-add-button-background-color);color:#fff;text-transform:uppercase}.e-site-editor-conditions__add-button:hover{background-color:var(--e-site-editor-add-button-color-hover-background-color);color:#fff}.e-site-editor-conditions__footer{display:flex;justify-content:flex-end;position:absolute;bottom:0;right:0;left:0;padding:.5rem;border-block-start:1px solid var(--hr-color)}.e-site-editor-conditions__input-wrapper{position:relative;padding-inline-start:1px solid;border-color:var(--e-site-editor-input-wrapper-border-color)}.e-site-editor-conditions__input-wrapper:first-child{border:none}.e-site-editor-conditions__input-wrapper select{-moz-appearance:none;appearance:none;-webkit-appearance:none;font-size:.75rem;height:2.5rem;border-width:0;padding:0 .625rem;width:100%;position:relative;color:var(--e-site-editor-input-wrapper-select-color);outline:none;background:transparent}.e-site-editor-conditions__input-wrapper:after{font-family:eicons;content:"\e8ad";font-size:.75rem;pointer-events:none;position:absolute;top:50%;transform:translateY(-50%);right:.625rem}.e-site-editor-conditions__input-wrapper .select2-container--default .select2-selection--single{border:none;line-height:2.5rem}.e-site-editor-conditions__input-wrapper .select2-container--default .select2-selection--single .select2-selection__rendered{line-height:2.5rem;font-size:.75rem}.e-site-editor-conditions__input-wrapper .select2-selection{outline:none;background:transparent;height:2.5rem}.e-site-editor-conditions__input-wrapper .select2-selection__arrow{display:none}.e-site-editor-conditions__input-wrapper--condition-type{position:relative}.e-site-editor-conditions__input-wrapper--condition-type:before{font-family:eicons;position:absolute;top:50%;transform:translateY(-50%);left:.75rem;font-size:.9375rem;pointer-events:none;z-index:1000}.e-site-editor-conditions__input-wrapper--condition-type select{text-transform:uppercase;padding-inline-start:2.125rem;width:7.5rem;font-size:.75rem;border-inline-end:1px solid;border-color:var(--e-site-editor-input-wrapper-border-color)}.e-site-editor-conditions__input-wrapper--condition-type[data-elementor-condition-type=include]:before{content:"\e8cc"}.e-site-editor-conditions__input-wrapper--condition-type[data-elementor-condition-type=exclude]:before{content:"\e8cd"}.select2-search__field{background-color:transparent;color:var(--e-site-editor-input-select2-search-field-color)}.eps-back-button{font-size:14px;margin-block-end:1.5rem}.eps-back-button .eps-icon{transform:rotate(0deg)}:root{--indicator-bullet-border-color:#fff}.eps-theme-dark{--indicator-bullet-border-color:#69727d}.eps-indicator-bullet{display:block;flex-shrink:0;width:.75rem;height:.75rem;box-shadow:0 2px 3px 1px var(--color-box-shadow-color);background-color:#9da5ae;border:2px solid var(--indicator-bullet-border-color);border-radius:100%;margin-inline-end:.625rem}.eps-indicator-bullet--active{background-color:#0a875a}.site-editor__preview-iframe{height:50vh;position:relative}.site-editor__preview-iframe__iframe{top:0;left:0;position:absolute;border:none;transform-origin:0 0;height:100%}.site-editor__preview-iframe--footer,.site-editor__preview-iframe--header{height:15vh}.e-site-editor__content_container{flex-direction:column;min-height:100%;flex-wrap:nowrap}.e-site-editor__content_container_main{flex:1}.e-site-editor__content_container_secondary{margin:0 auto;padding-block-start:2.75rem} \ No newline at end of file diff --git a/assets/css/editor-rtl.css b/assets/css/editor-rtl.css new file mode 100644 index 0000000..bc76907 --- /dev/null +++ b/assets/css/editor-rtl.css @@ -0,0 +1,591 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.elementor-panel .elementor-control .e-control-error { + color: #F59E0B; +} +.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-content { + flex-flow: row; + align-items: center; +} +.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-title { + width: 45%; +} +.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-raw-html { + width: 55%; +} +.elementor-panel .elementor-control .elementor-button.elementor-button-default.elementor-button-center { + display: block; + margin: 0 auto; +} + +#elementor-element--promotion__dialog .dialog-header .eicon-pro-icon { + visibility: hidden; +} + +.elementor-context-menu-list__item { + position: relative; +} +.elementor-context-menu-list__item--disabled .elementor-context-menu-list__item__title { + opacity: 0.5; +} +.elementor-context-menu-list__item__shortcut .eicon-advanced { + font-size: 16px; + color: var(--e-a-color-txt-muted); +} +.elementor-context-menu-list__item__shortcut--link-fullwidth { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + z-index: 2; + cursor: pointer; +} + +#elementor-widget-template-empty-templates { + margin-block-start: 15px; + text-align: center; +} + +.elementor-widget-template-empty-templates-title { + padding: 25px 0 30px; +} + +.elementor-widget-template-empty-templates-icon { + font-size: 96px; +} + +.elementor-widget-template-empty-templates-footer { + color: var(--e-a-color-txt-muted); + font-size: 13px; + font-style: italic; + margin-block-end: 15px; +} + +#elementor-panel-global-widget { + height: 100%; +} +#elementor-panel-global-widget > * { + background-color: var(--e-a-bg-default); +} + +#elementor-global-widget-locked-header { + border-block-end: var(--e-a-border); +} +#elementor-global-widget-locked-header.elementor-nerd-box { + padding: 40px 25px; +} +#elementor-global-widget-locked-header.elementor-nerd-box .elementor-nerd-box-icon { + margin-block-start: 20px; +} + +#elementor-global-widget-locked-tools { + margin-block-start: 15px; + padding: 0 20px; +} + +.elementor-global-widget-locked-tool { + display: flex; + padding: 20px 0; + align-items: center; +} +.elementor-global-widget-locked-tool .elementor-button { + min-width: 70px; +} + +.elementor-global-widget-locked-tool-description { + flex-grow: 1; +} + +#elementor-global-widget-locked-unlink { + border-block-start: var(--e-a-border); +} + +#elementor-global-templates .elementor-element { + position: relative; +} +#elementor-global-templates .elementor-element:before { + position: absolute; + font-family: eicons; + content: "\e91f"; + top: 5px; + left: 5px; + font-size: 10px; +} +#elementor-global-templates .elementor-element:hover:before { + color: var(--e-a-color-global); +} + +#elementor-global-widget-loading { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + align-items: center; + justify-content: center; +} +#elementor-global-widget-loading:not(.elementor-hidden) { + display: flex; +} +#elementor-global-widget-loading i { + font-size: 50px; +} + +.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields { + margin: 10px 0; +} +.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields .elementor-control { + padding: 0; +} +.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields:last-child { + margin-block-end: 0; +} + +.elementor-repeater-row--form-step .elementor-repeater-row-tools:hover { + background-color: #BABFC5; +} +.elementor-repeater-row--form-step .elementor-repeater-row-tools div:not(.elementor-repeater-row-handle-sortable) { + background-color: #F1F2F3; +} +.elementor-repeater-row--form-step .elementor-repeater-row-tools div:not(.elementor-repeater-row-handle-sortable):hover { + background-color: #ffffff; +} + +.elementor-facebook-widget.fb_iframe_widget { + width: 100% !important; +} +.elementor-facebook-widget.fb_iframe_widget span { + width: 100% !important; +} +.elementor-facebook-widget.fb_iframe_widget iframe { + position: relative; + width: 100% !important; +} +.elementor-facebook-widget.fb-like { + height: 1px; +} + +.elementor-widget-facebook-comments iframe { + width: 100% !important; +} + +#elementor-publish { + height: 100%; + display: flex; +} +#elementor-publish__modal .dialog-message { + padding: 0; +} +#elementor-publish__modal .dialog-buttons-wrapper { + display: flex; +} +#elementor-publish__tabs { + padding-block-start: 50px; +} +@media (max-width: 1439px) { + #elementor-publish__tabs { + width: 28%; + } +} +#elementor-publish__screen { + overflow: auto; + padding: 50px; +} + +.elementor-publish__tab { + display: flex; + align-items: center; + position: relative; + height: 110px; + padding: 20px 15px; + cursor: pointer; +} +.elementor-publish__tab:hover { + background-color: var(--e-a-bg-hover); +} +.elementor-publish__tab.elementor-active { + background-color: var(--e-a-bg-active); + color: var(--e-a-color-txt-accent); +} +.elementor-publish__tab.elementor-active:after { + content: ""; + position: absolute; + top: 0; + right: 0; + height: 100%; + width: 3px; + background-color: var(--e-a-border-color-accent); +} +.elementor-publish__tab__image { + width: 50px; + flex-shrink: 0; +} +.elementor-publish__tab__image img { + width: 100%; +} +.elementor-publish__tab__content { + text-align: start; + padding-inline-start: 15px; +} +.elementor-publish__tab__title { + font-size: 18px; + font-weight: bold; +} + +#elementor-theme-builder-conditions { + margin: 40px 0 60px; +} +#elementor-theme-builder-conditions-view { + overflow: hidden; +} +#elementor-theme-builder-conditions .elementor-control { + background-color: transparent; + padding: 0; +} +#elementor-theme-builder-conditions .elementor-control:before { + content: none; +} +#elementor-theme-builder-conditions .elementor-control select { + border-width: 0; + height: 40px; + padding: 0 14px; +} +#elementor-theme-builder-conditions .elementor-control-type-query { + width: 100px; +} +#elementor-theme-builder-conditions .elementor-control-type-select .elementor-control-input-wrapper:after { + left: 10px; +} +#elementor-theme-builder-conditions .elementor-control-type { + width: 120px; +} +#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=include] .elementor-control-input-wrapper:before { + content: "\e8cc"; +} +#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=exclude] .elementor-control-input-wrapper:before { + content: "\e8cd"; +} +#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=exclude] select, #elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=include] select { + padding-inline-start: 33px; +} +#elementor-theme-builder-conditions .elementor-control-type .elementor-control-input-wrapper:before { + font-family: eicons; + position: absolute; + top: 50%; + transform: translateY(-50%); + right: 13px; + font-size: 15px; +} +#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls { + display: flex; + flex-grow: 1; + margin-inline-end: 10px; + width: 70%; + border: var(--e-a-border-bold); + overflow: hidden; + border-radius: var(--e-a-border-radius); +} +#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls .elementor-control:not(:first-child) { + flex-grow: 1; + border-inline-start: var(--e-a-border-bold); + margin-inline-start: 1px; +} +#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls .elementor-control:not(:first-child) select { + border-radius: 0; +} +#elementor-theme-builder-conditions .elementor-repeater-fields-wrapper { + max-width: 700px; + width: 100%; + margin: auto; +} +#elementor-theme-builder-conditions .elementor-repeater-fields { + display: flex; + align-items: center; + justify-content: center; + margin-block-start: 10px; +} +#elementor-theme-builder-conditions .elementor-control-field { + display: block; +} +#elementor-theme-builder-conditions .elementor-control-title, +#elementor-theme-builder-conditions .elementor-control-spinner { + display: none; +} +#elementor-theme-builder-conditions .elementor-control-input-wrapper { + width: 100%; + max-width: initial; +} +#elementor-theme-builder-conditions .select2-selection { + height: 40px; + border: none; + border-radius: 0; +} +#elementor-theme-builder-conditions .select2-selection__rendered { + line-height: 40px; + padding-inline-start: 15px; + text-align: start; +} +#elementor-theme-builder-conditions .select2-selection__arrow { + height: 30px; +} +#elementor-theme-builder-conditions .select2-selection__arrow b { + border-width: 4px 4px 0 4px; + margin-block-start: 2px; + margin-inline-start: -7px; +} +#elementor-theme-builder-conditions .elementor-repeater-tool-remove { + font-size: 18px; + cursor: pointer; + color: var(--e-a-color-txt); +} +#elementor-theme-builder-conditions .elementor-button-wrapper { + margin-block-start: 50px; +} +#elementor-theme-builder-conditions .elementor-repeater-add { + padding: 12px 26px; +} + +.elementor-error .elementor-theme-builder-conditions-repeater-row-controls { + border: 1px solid var(--e-a-color-warning); +} + +.elementor-conditions-conflict-message { + margin-block-start: 10px; + font-size: 11px; + color: var(--e-a-color-warning); + text-align: start; + padding-inline-start: 90px; +} +.elementor-conditions-conflict-message a { + color: var(--e-a-color-warning); +} + +.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu { + display: flex; +} +.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item { + width: 100%; +} +.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item i { + margin-inline-end: 5px; +} +.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item > * { + display: inline-block; + line-height: 40px; +} + +.elementor-conditions-select2-dropdown { + border: none; + border-radius: 0; +} +.elementor-conditions-select2-dropdown .select2-results__message { + display: none; +} +.elementor-conditions-select2-dropdown .select2-search--dropdown .select2-search__field { + border-width: 0 0 1px; + border-radius: 0; +} + +#elementor-toast.e-theme-builder-save-toaster .dialog-buttons-wrapper { + flex-direction: column; +} +#elementor-toast.e-theme-builder-save-toaster .dialog-buttons-message { + text-align: center; +} +#elementor-toast.e-theme-builder-save-toaster .dialog-open_site_editor { + margin-block-end: 10px; +} + +.elementor-control.elementor-control-sitemap_items.elementor-control-type-repeater .elementor-repeater-row-tools .elementor-repeater-row-item-title span { + text-transform: capitalize; +} + +.elementor-editor-popup .elementor-tab-control-settings a:before { + content: "\e922"; +} + +#elementor-publish .elementor-popup__display-settings .elementor-control-type-slider .elementor-control-input-wrapper { + width: 150px; +} +#elementor-publish .elementor-popup__display-settings .elementor-control-input-wrapper { + width: 80px; + max-width: 100%; +} + +.elementor-popup__display-settings { + text-align: start; +} +.elementor-popup__display-settings_controls_group { + display: flex; + align-items: center; + height: 60px; + border: var(--e-a-border); + margin-block-end: 10px; + padding-inline-end: 20px; + border-radius: var(--e-a-border-radius); +} +.elementor-popup__display-settings_controls_group:hover { + background-color: var(--e-a-bg-hover); +} +.elementor-popup__display-settings_controls_group.elementor-active { + background-color: var(--e-a-bg-active); + color: var(--e-a-color-txt-accent); + border-color: var(--e-a-border-color-accent); +} +.elementor-popup__display-settings_controls_group:not(.elementor-active) .elementor-control:nth-child(2) h3 { + color: #9DA5AE; +} +.elementor-popup__display-settings_controls_group__icon { + width: 60px; + height: 60px; + border-inline-end: var(--e-a-border); + display: inline-flex; + align-items: center; + justify-content: center; +} +.elementor-popup__display-settings_controls_group__icon img { + width: 35px; +} +.elementor-popup__display-settings_controls_group .elementor-control { + padding: 0; + margin: 0; +} +.elementor-popup__display-settings_controls_group .elementor-control:nth-child(2) { + width: 230px; +} +.elementor-popup__display-settings_controls_group .elementor-control:nth-child(2) h3 { + font-size: 16px; + font-weight: 400; +} +.elementor-popup__display-settings_controls_group .elementor-control:not(:nth-child(2)) .elementor-control-title:not(:empty) { + width: 75px; +} +.elementor-popup__display-settings_controls_group .elementor-control:not(:last-child) { + margin-inline-start: 25px; +} +.elementor-popup__display-settings_controls_group .elementor-control-type-slider .elementor-control-input-wrapper { + display: flex; +} +.elementor-popup__display-settings_controls_group .elementor-control-type-slider .elementor-slider-input { + width: 45%; +} +.elementor-popup__display-settings_controls_group .elementor-control-type-switcher .elementor-control-field { + justify-content: flex-end; +} +.elementor-popup__display-settings .elementor-control-type-section { + display: none; +} +.elementor-popup__display-settings__group-toggle { + flex-grow: 1; +} + +#elementor-popup-timing__controls .select2-selection { + min-height: 27px; +} +#elementor-popup-timing__controls .select2-selection__rendered { + line-height: 1; +} +#elementor-popup-timing__controls .select2-selection__choice { + font-size: 10px; +} + +#elementor-popup__timing-controls-group--url .elementor-control-url_url .elementor-control-input-wrapper { + width: 200px; +} +#elementor-popup__timing-controls-group--sources .elementor-control-sources_sources .elementor-control-input-wrapper { + width: 300px; +} +#elementor-popup__timing-controls-group--devices .elementor-control-devices_devices .elementor-control-input-wrapper { + width: 330px; +} +#elementor-popup__timing-controls-group--logged_in .elementor-control-logged_in_roles .elementor-control-input-wrapper { + width: 195px; +} +#elementor-popup__timing-controls-group--browsers .elementor-control-browsers_browsers .elementor-control-input-wrapper { + width: 100px; +} +#elementor-popup__timing-controls-group--browsers .elementor-control-browsers_browsers_options .elementor-control-input-wrapper { + width: 200px; +} + +#elementor-popup__timing-controls-group--times .elementor-control-field { + display: unset; +} + +#elementor-popup__timing-controls-group--schedule .elementor-control-title { + width: unset; +} +#elementor-popup__timing-controls-group--schedule .elementor-control-input-wrapper { + margin-block-start: 5px; +} +#elementor-popup__timing-controls-group--schedule div[class*=elementor-control-schedule_] .elementor-control-content .elementor-control-field { + display: block; +} +#elementor-popup__timing-controls-group--schedule div[class*=elementor-control-schedule_] .elementor-control-content .elementor-control-input-wrapper { + width: 108px; +} +#elementor-popup__timing-controls-group--schedule .elementor-control-schedule_timezone { + margin-inline-start: -17px; +} + +.elementor-control-type-raw_html .elementor-descriptor-subtle a { + color: inherit; + border-block-end-color: inherit; + font-weight: 500; +} +.elementor-control-type-raw_html .elementor-descriptor-subtle a:hover { + color: #0C0D0E; +} + +.e-page-transition-preview::before { + content: ""; + height: 1em; + width: 0.8em; + margin-inline-end: 4px; + background-image: url("data:image/svg+xml,%3Csvg width='10' height='13' viewBox='0 0 10 13' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8.40554 6.20969C8.6115 6.34841 8.6115 6.65153 8.40554 6.79026L1.19553 11.6467C0.963052 11.8033 0.65 11.6368 0.65 11.3565L0.650001 1.64349C0.650001 1.36319 0.963054 1.19661 1.19553 1.3532L8.40554 6.20969Z' stroke='white' stroke-width='1.3'/%3E%3C/svg%3E%0A"); + background-repeat: no-repeat; + background-size: contain; + display: inline-block; + vertical-align: top; + transform: rotate(180deg); +} + +.elementor-template-query-control-actions { + display: flex; + align-items: center; + justify-content: center; + margin-block-start: 15px; + gap: 15px; +} + +.e-control-display-conditions__wrapper, +.e-control-display-conditions-promo__wrapper { + display: flex; + justify-content: space-between; +} +.e-control-display-conditions__desc, +.e-control-display-conditions-promo__desc { + align-self: center; +} +.e-control-display-conditions__desc .eicon-lock, +.e-control-display-conditions-promo__desc .eicon-lock { + margin-inline-start: 15px; +} +.e-control-display-conditions__desc .eicon-lock:hover, +.e-control-display-conditions-promo__desc .eicon-lock:hover { + color: var(--e-a-color-accent); +} +.e-control-display-conditions.eicon-flow, +.e-control-display-conditions-promo.eicon-flow { + align-self: flex-end; + cursor: pointer; + border: var(--e-a-border-bold); + border-radius: var(--e-a-border-radius); + padding: 5px; +} +.e-control-display-conditions.eicon-flow.filled, +.e-control-display-conditions-promo.eicon-flow.filled { + background-color: var(--e-a-bg-active); + color: #E73CF6; +} +/*# sourceMappingURL=editor-rtl.css.map */ \ No newline at end of file diff --git a/assets/css/editor-rtl.min.css b/assets/css/editor-rtl.min.css new file mode 100644 index 0000000..12ba106 --- /dev/null +++ b/assets/css/editor-rtl.min.css @@ -0,0 +1,2 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.elementor-panel .elementor-control .e-control-error{color:#f59e0b}.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-content{flex-flow:row;align-items:center}.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-title{width:45%}.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-raw-html{width:55%}.elementor-panel .elementor-control .elementor-button.elementor-button-default.elementor-button-center{display:block;margin:0 auto}#elementor-element--promotion__dialog .dialog-header .eicon-pro-icon{visibility:hidden}.elementor-context-menu-list__item{position:relative}.elementor-context-menu-list__item--disabled .elementor-context-menu-list__item__title{opacity:.5}.elementor-context-menu-list__item__shortcut .eicon-advanced{font-size:16px;color:var(--e-a-color-txt-muted)}.elementor-context-menu-list__item__shortcut--link-fullwidth{position:absolute;left:0;top:0;width:100%;height:100%;z-index:2;cursor:pointer}#elementor-widget-template-empty-templates{margin-block-start:15px;text-align:center}.elementor-widget-template-empty-templates-title{padding:25px 0 30px}.elementor-widget-template-empty-templates-icon{font-size:96px}.elementor-widget-template-empty-templates-footer{color:var(--e-a-color-txt-muted);font-size:13px;font-style:italic;margin-block-end:15px}#elementor-panel-global-widget{height:100%}#elementor-panel-global-widget>*{background-color:var(--e-a-bg-default)}#elementor-global-widget-locked-header{border-block-end:var(--e-a-border)}#elementor-global-widget-locked-header.elementor-nerd-box{padding:40px 25px}#elementor-global-widget-locked-header.elementor-nerd-box .elementor-nerd-box-icon{margin-block-start:20px}#elementor-global-widget-locked-tools{margin-block-start:15px;padding:0 20px}.elementor-global-widget-locked-tool{display:flex;padding:20px 0;align-items:center}.elementor-global-widget-locked-tool .elementor-button{min-width:70px}.elementor-global-widget-locked-tool-description{flex-grow:1}#elementor-global-widget-locked-unlink{border-block-start:var(--e-a-border)}#elementor-global-templates .elementor-element{position:relative}#elementor-global-templates .elementor-element:before{position:absolute;font-family:eicons;content:"\e91f";top:5px;left:5px;font-size:10px}#elementor-global-templates .elementor-element:hover:before{color:var(--e-a-color-global)}#elementor-global-widget-loading{position:absolute;top:0;left:0;height:100%;width:100%;align-items:center;justify-content:center}#elementor-global-widget-loading:not(.elementor-hidden){display:flex}#elementor-global-widget-loading i{font-size:50px}.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields{margin:10px 0}.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields .elementor-control{padding:0}.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields:last-child{margin-block-end:0}.elementor-repeater-row--form-step .elementor-repeater-row-tools:hover{background-color:#babfc5}.elementor-repeater-row--form-step .elementor-repeater-row-tools div:not(.elementor-repeater-row-handle-sortable){background-color:#f1f2f3}.elementor-repeater-row--form-step .elementor-repeater-row-tools div:not(.elementor-repeater-row-handle-sortable):hover{background-color:#fff}.elementor-facebook-widget.fb_iframe_widget,.elementor-facebook-widget.fb_iframe_widget span{width:100%!important}.elementor-facebook-widget.fb_iframe_widget iframe{position:relative;width:100%!important}.elementor-facebook-widget.fb-like{height:1px}.elementor-widget-facebook-comments iframe{width:100%!important}#elementor-publish{height:100%;display:flex}#elementor-publish__modal .dialog-message{padding:0}#elementor-publish__modal .dialog-buttons-wrapper{display:flex}#elementor-publish__tabs{padding-block-start:50px}@media (max-width:1439px){#elementor-publish__tabs{width:28%}}#elementor-publish__screen{overflow:auto;padding:50px}.elementor-publish__tab{display:flex;align-items:center;position:relative;height:110px;padding:20px 15px;cursor:pointer}.elementor-publish__tab:hover{background-color:var(--e-a-bg-hover)}.elementor-publish__tab.elementor-active{background-color:var(--e-a-bg-active);color:var(--e-a-color-txt-accent)}.elementor-publish__tab.elementor-active:after{content:"";position:absolute;top:0;right:0;height:100%;width:3px;background-color:var(--e-a-border-color-accent)}.elementor-publish__tab__image{width:50px;flex-shrink:0}.elementor-publish__tab__image img{width:100%}.elementor-publish__tab__content{text-align:start;padding-inline-start:15px}.elementor-publish__tab__title{font-size:18px;font-weight:700}#elementor-theme-builder-conditions{margin:40px 0 60px}#elementor-theme-builder-conditions-view{overflow:hidden}#elementor-theme-builder-conditions .elementor-control{background-color:transparent;padding:0}#elementor-theme-builder-conditions .elementor-control:before{content:none}#elementor-theme-builder-conditions .elementor-control select{border-width:0;height:40px;padding:0 14px}#elementor-theme-builder-conditions .elementor-control-type-query{width:100px}#elementor-theme-builder-conditions .elementor-control-type-select .elementor-control-input-wrapper:after{left:10px}#elementor-theme-builder-conditions .elementor-control-type{width:120px}#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=include] .elementor-control-input-wrapper:before{content:"\e8cc"}#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=exclude] .elementor-control-input-wrapper:before{content:"\e8cd"}#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=exclude] select,#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=include] select{padding-inline-start:33px}#elementor-theme-builder-conditions .elementor-control-type .elementor-control-input-wrapper:before{font-family:eicons;position:absolute;top:50%;transform:translateY(-50%);right:13px;font-size:15px}#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls{display:flex;flex-grow:1;margin-inline-end:10px;width:70%;border:var(--e-a-border-bold);overflow:hidden;border-radius:var(--e-a-border-radius)}#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls .elementor-control:not(:first-child){flex-grow:1;border-inline-start:var(--e-a-border-bold);margin-inline-start:1px}#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls .elementor-control:not(:first-child) select{border-radius:0}#elementor-theme-builder-conditions .elementor-repeater-fields-wrapper{max-width:700px;width:100%;margin:auto}#elementor-theme-builder-conditions .elementor-repeater-fields{display:flex;align-items:center;justify-content:center;margin-block-start:10px}#elementor-theme-builder-conditions .elementor-control-field{display:block}#elementor-theme-builder-conditions .elementor-control-spinner,#elementor-theme-builder-conditions .elementor-control-title{display:none}#elementor-theme-builder-conditions .elementor-control-input-wrapper{width:100%;max-width:none}#elementor-theme-builder-conditions .select2-selection{height:40px;border:none;border-radius:0}#elementor-theme-builder-conditions .select2-selection__rendered{line-height:40px;padding-inline-start:15px;text-align:start}#elementor-theme-builder-conditions .select2-selection__arrow{height:30px}#elementor-theme-builder-conditions .select2-selection__arrow b{border-width:4px 4px 0;margin-block-start:2px;margin-inline-start:-7px}#elementor-theme-builder-conditions .elementor-repeater-tool-remove{font-size:18px;cursor:pointer;color:var(--e-a-color-txt)}#elementor-theme-builder-conditions .elementor-button-wrapper{margin-block-start:50px}#elementor-theme-builder-conditions .elementor-repeater-add{padding:12px 26px}.elementor-error .elementor-theme-builder-conditions-repeater-row-controls{border:1px solid var(--e-a-color-warning)}.elementor-conditions-conflict-message{margin-block-start:10px;font-size:11px;color:var(--e-a-color-warning);text-align:start;padding-inline-start:90px}.elementor-conditions-conflict-message a{color:var(--e-a-color-warning)}.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu{display:flex}.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item{width:100%}.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item i{margin-inline-end:5px}.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item>*{display:inline-block;line-height:40px}.elementor-conditions-select2-dropdown{border:none;border-radius:0}.elementor-conditions-select2-dropdown .select2-results__message{display:none}.elementor-conditions-select2-dropdown .select2-search--dropdown .select2-search__field{border-width:0 0 1px;border-radius:0}#elementor-toast.e-theme-builder-save-toaster .dialog-buttons-wrapper{flex-direction:column}#elementor-toast.e-theme-builder-save-toaster .dialog-buttons-message{text-align:center}#elementor-toast.e-theme-builder-save-toaster .dialog-open_site_editor{margin-block-end:10px}.elementor-control.elementor-control-sitemap_items.elementor-control-type-repeater .elementor-repeater-row-tools .elementor-repeater-row-item-title span{text-transform:capitalize}.elementor-editor-popup .elementor-tab-control-settings a:before{content:"\e922"}#elementor-publish .elementor-popup__display-settings .elementor-control-type-slider .elementor-control-input-wrapper{width:150px}#elementor-publish .elementor-popup__display-settings .elementor-control-input-wrapper{width:80px;max-width:100%}.elementor-popup__display-settings{text-align:start}.elementor-popup__display-settings_controls_group{display:flex;align-items:center;height:60px;border:var(--e-a-border);margin-block-end:10px;padding-inline-end:20px;border-radius:var(--e-a-border-radius)}.elementor-popup__display-settings_controls_group:hover{background-color:var(--e-a-bg-hover)}.elementor-popup__display-settings_controls_group.elementor-active{background-color:var(--e-a-bg-active);color:var(--e-a-color-txt-accent);border-color:var(--e-a-border-color-accent)}.elementor-popup__display-settings_controls_group:not(.elementor-active) .elementor-control:nth-child(2) h3{color:#9da5ae}.elementor-popup__display-settings_controls_group__icon{width:60px;height:60px;border-inline-end:var(--e-a-border);display:inline-flex;align-items:center;justify-content:center}.elementor-popup__display-settings_controls_group__icon img{width:35px}.elementor-popup__display-settings_controls_group .elementor-control{padding:0;margin:0}.elementor-popup__display-settings_controls_group .elementor-control:nth-child(2){width:230px}.elementor-popup__display-settings_controls_group .elementor-control:nth-child(2) h3{font-size:16px;font-weight:400}.elementor-popup__display-settings_controls_group .elementor-control:not(:nth-child(2)) .elementor-control-title:not(:empty){width:75px}.elementor-popup__display-settings_controls_group .elementor-control:not(:last-child){margin-inline-start:25px}.elementor-popup__display-settings_controls_group .elementor-control-type-slider .elementor-control-input-wrapper{display:flex}.elementor-popup__display-settings_controls_group .elementor-control-type-slider .elementor-slider-input{width:45%}.elementor-popup__display-settings_controls_group .elementor-control-type-switcher .elementor-control-field{justify-content:flex-end}.elementor-popup__display-settings .elementor-control-type-section{display:none}.elementor-popup__display-settings__group-toggle{flex-grow:1}#elementor-popup-timing__controls .select2-selection{min-height:27px}#elementor-popup-timing__controls .select2-selection__rendered{line-height:1}#elementor-popup-timing__controls .select2-selection__choice{font-size:10px}#elementor-popup__timing-controls-group--url .elementor-control-url_url .elementor-control-input-wrapper{width:200px}#elementor-popup__timing-controls-group--sources .elementor-control-sources_sources .elementor-control-input-wrapper{width:300px}#elementor-popup__timing-controls-group--devices .elementor-control-devices_devices .elementor-control-input-wrapper{width:330px}#elementor-popup__timing-controls-group--logged_in .elementor-control-logged_in_roles .elementor-control-input-wrapper{width:195px}#elementor-popup__timing-controls-group--browsers .elementor-control-browsers_browsers .elementor-control-input-wrapper{width:100px}#elementor-popup__timing-controls-group--browsers .elementor-control-browsers_browsers_options .elementor-control-input-wrapper{width:200px}#elementor-popup__timing-controls-group--times .elementor-control-field{display:unset}#elementor-popup__timing-controls-group--schedule .elementor-control-title{width:unset}#elementor-popup__timing-controls-group--schedule .elementor-control-input-wrapper{margin-block-start:5px}#elementor-popup__timing-controls-group--schedule div[class*=elementor-control-schedule_] .elementor-control-content .elementor-control-field{display:block}#elementor-popup__timing-controls-group--schedule div[class*=elementor-control-schedule_] .elementor-control-content .elementor-control-input-wrapper{width:108px}#elementor-popup__timing-controls-group--schedule .elementor-control-schedule_timezone{margin-inline-start:-17px}.elementor-control-type-raw_html .elementor-descriptor-subtle a{color:inherit;border-block-end-color:inherit;font-weight:500}.elementor-control-type-raw_html .elementor-descriptor-subtle a:hover{color:#0c0d0e}.e-page-transition-preview:before{content:"";height:1em;width:.8em;margin-inline-end:4px;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='10' height='13' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8.406 6.21a.35.35 0 010 .58l-7.21 4.857a.35.35 0 01-.546-.29V1.643a.35.35 0 01.546-.29l7.21 4.857z' stroke='%23fff' stroke-width='1.3'/%3E%3C/svg%3E");background-repeat:no-repeat;background-size:contain;display:inline-block;vertical-align:top;transform:rotate(180deg)}.elementor-template-query-control-actions{display:flex;align-items:center;justify-content:center;margin-block-start:15px;gap:15px}.e-control-display-conditions-promo__wrapper,.e-control-display-conditions__wrapper{display:flex;justify-content:space-between}.e-control-display-conditions-promo__desc,.e-control-display-conditions__desc{align-self:center}.e-control-display-conditions-promo__desc .eicon-lock,.e-control-display-conditions__desc .eicon-lock{margin-inline-start:15px}.e-control-display-conditions-promo__desc .eicon-lock:hover,.e-control-display-conditions__desc .eicon-lock:hover{color:var(--e-a-color-accent)}.e-control-display-conditions-promo.eicon-flow,.e-control-display-conditions.eicon-flow{align-self:flex-end;cursor:pointer;border:var(--e-a-border-bold);border-radius:var(--e-a-border-radius);padding:5px}.e-control-display-conditions-promo.eicon-flow.filled,.e-control-display-conditions.eicon-flow.filled{background-color:var(--e-a-bg-active);color:#e73cf6} \ No newline at end of file diff --git a/assets/css/editor.css b/assets/css/editor.css new file mode 100644 index 0000000..7ce7bb6 --- /dev/null +++ b/assets/css/editor.css @@ -0,0 +1,590 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.elementor-panel .elementor-control .e-control-error { + color: #F59E0B; +} +.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-content { + flex-flow: row; + align-items: center; +} +.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-title { + width: 45%; +} +.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-raw-html { + width: 55%; +} +.elementor-panel .elementor-control .elementor-button.elementor-button-default.elementor-button-center { + display: block; + margin: 0 auto; +} + +#elementor-element--promotion__dialog .dialog-header .eicon-pro-icon { + visibility: hidden; +} + +.elementor-context-menu-list__item { + position: relative; +} +.elementor-context-menu-list__item--disabled .elementor-context-menu-list__item__title { + opacity: 0.5; +} +.elementor-context-menu-list__item__shortcut .eicon-advanced { + font-size: 16px; + color: var(--e-a-color-txt-muted); +} +.elementor-context-menu-list__item__shortcut--link-fullwidth { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + z-index: 2; + cursor: pointer; +} + +#elementor-widget-template-empty-templates { + margin-block-start: 15px; + text-align: center; +} + +.elementor-widget-template-empty-templates-title { + padding: 25px 0 30px; +} + +.elementor-widget-template-empty-templates-icon { + font-size: 96px; +} + +.elementor-widget-template-empty-templates-footer { + color: var(--e-a-color-txt-muted); + font-size: 13px; + font-style: italic; + margin-block-end: 15px; +} + +#elementor-panel-global-widget { + height: 100%; +} +#elementor-panel-global-widget > * { + background-color: var(--e-a-bg-default); +} + +#elementor-global-widget-locked-header { + border-block-end: var(--e-a-border); +} +#elementor-global-widget-locked-header.elementor-nerd-box { + padding: 40px 25px; +} +#elementor-global-widget-locked-header.elementor-nerd-box .elementor-nerd-box-icon { + margin-block-start: 20px; +} + +#elementor-global-widget-locked-tools { + margin-block-start: 15px; + padding: 0 20px; +} + +.elementor-global-widget-locked-tool { + display: flex; + padding: 20px 0; + align-items: center; +} +.elementor-global-widget-locked-tool .elementor-button { + min-width: 70px; +} + +.elementor-global-widget-locked-tool-description { + flex-grow: 1; +} + +#elementor-global-widget-locked-unlink { + border-block-start: var(--e-a-border); +} + +#elementor-global-templates .elementor-element { + position: relative; +} +#elementor-global-templates .elementor-element:before { + position: absolute; + font-family: eicons; + content: "\e91f"; + top: 5px; + left: 5px; + font-size: 10px; +} +#elementor-global-templates .elementor-element:hover:before { + color: var(--e-a-color-global); +} + +#elementor-global-widget-loading { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + align-items: center; + justify-content: center; +} +#elementor-global-widget-loading:not(.elementor-hidden) { + display: flex; +} +#elementor-global-widget-loading i { + font-size: 50px; +} + +.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields { + margin: 10px 0; +} +.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields .elementor-control { + padding: 0; +} +.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields:last-child { + margin-block-end: 0; +} + +.elementor-repeater-row--form-step .elementor-repeater-row-tools:hover { + background-color: #BABFC5; +} +.elementor-repeater-row--form-step .elementor-repeater-row-tools div:not(.elementor-repeater-row-handle-sortable) { + background-color: #F1F2F3; +} +.elementor-repeater-row--form-step .elementor-repeater-row-tools div:not(.elementor-repeater-row-handle-sortable):hover { + background-color: #ffffff; +} + +.elementor-facebook-widget.fb_iframe_widget { + width: 100% !important; +} +.elementor-facebook-widget.fb_iframe_widget span { + width: 100% !important; +} +.elementor-facebook-widget.fb_iframe_widget iframe { + position: relative; + width: 100% !important; +} +.elementor-facebook-widget.fb-like { + height: 1px; +} + +.elementor-widget-facebook-comments iframe { + width: 100% !important; +} + +#elementor-publish { + height: 100%; + display: flex; +} +#elementor-publish__modal .dialog-message { + padding: 0; +} +#elementor-publish__modal .dialog-buttons-wrapper { + display: flex; +} +#elementor-publish__tabs { + padding-block-start: 50px; +} +@media (max-width: 1439px) { + #elementor-publish__tabs { + width: 28%; + } +} +#elementor-publish__screen { + overflow: auto; + padding: 50px; +} + +.elementor-publish__tab { + display: flex; + align-items: center; + position: relative; + height: 110px; + padding: 20px 15px; + cursor: pointer; +} +.elementor-publish__tab:hover { + background-color: var(--e-a-bg-hover); +} +.elementor-publish__tab.elementor-active { + background-color: var(--e-a-bg-active); + color: var(--e-a-color-txt-accent); +} +.elementor-publish__tab.elementor-active:after { + content: ""; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 3px; + background-color: var(--e-a-border-color-accent); +} +.elementor-publish__tab__image { + width: 50px; + flex-shrink: 0; +} +.elementor-publish__tab__image img { + width: 100%; +} +.elementor-publish__tab__content { + text-align: start; + padding-inline-start: 15px; +} +.elementor-publish__tab__title { + font-size: 18px; + font-weight: bold; +} + +#elementor-theme-builder-conditions { + margin: 40px 0 60px; +} +#elementor-theme-builder-conditions-view { + overflow: hidden; +} +#elementor-theme-builder-conditions .elementor-control { + background-color: transparent; + padding: 0; +} +#elementor-theme-builder-conditions .elementor-control:before { + content: none; +} +#elementor-theme-builder-conditions .elementor-control select { + border-width: 0; + height: 40px; + padding: 0 14px; +} +#elementor-theme-builder-conditions .elementor-control-type-query { + width: 100px; +} +#elementor-theme-builder-conditions .elementor-control-type-select .elementor-control-input-wrapper:after { + right: 10px; +} +#elementor-theme-builder-conditions .elementor-control-type { + width: 120px; +} +#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=include] .elementor-control-input-wrapper:before { + content: "\e8cc"; +} +#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=exclude] .elementor-control-input-wrapper:before { + content: "\e8cd"; +} +#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=exclude] select, #elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=include] select { + padding-inline-start: 33px; +} +#elementor-theme-builder-conditions .elementor-control-type .elementor-control-input-wrapper:before { + font-family: eicons; + position: absolute; + top: 50%; + transform: translateY(-50%); + left: 13px; + font-size: 15px; +} +#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls { + display: flex; + flex-grow: 1; + margin-inline-end: 10px; + width: 70%; + border: var(--e-a-border-bold); + overflow: hidden; + border-radius: var(--e-a-border-radius); +} +#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls .elementor-control:not(:first-child) { + flex-grow: 1; + border-inline-start: var(--e-a-border-bold); + margin-inline-start: 1px; +} +#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls .elementor-control:not(:first-child) select { + border-radius: 0; +} +#elementor-theme-builder-conditions .elementor-repeater-fields-wrapper { + max-width: 700px; + width: 100%; + margin: auto; +} +#elementor-theme-builder-conditions .elementor-repeater-fields { + display: flex; + align-items: center; + justify-content: center; + margin-block-start: 10px; +} +#elementor-theme-builder-conditions .elementor-control-field { + display: block; +} +#elementor-theme-builder-conditions .elementor-control-title, +#elementor-theme-builder-conditions .elementor-control-spinner { + display: none; +} +#elementor-theme-builder-conditions .elementor-control-input-wrapper { + width: 100%; + max-width: initial; +} +#elementor-theme-builder-conditions .select2-selection { + height: 40px; + border: none; + border-radius: 0; +} +#elementor-theme-builder-conditions .select2-selection__rendered { + line-height: 40px; + padding-inline-start: 15px; + text-align: start; +} +#elementor-theme-builder-conditions .select2-selection__arrow { + height: 30px; +} +#elementor-theme-builder-conditions .select2-selection__arrow b { + border-width: 4px 4px 0 4px; + margin-block-start: 2px; + margin-inline-start: -7px; +} +#elementor-theme-builder-conditions .elementor-repeater-tool-remove { + font-size: 18px; + cursor: pointer; + color: var(--e-a-color-txt); +} +#elementor-theme-builder-conditions .elementor-button-wrapper { + margin-block-start: 50px; +} +#elementor-theme-builder-conditions .elementor-repeater-add { + padding: 12px 26px; +} + +.elementor-error .elementor-theme-builder-conditions-repeater-row-controls { + border: 1px solid var(--e-a-color-warning); +} + +.elementor-conditions-conflict-message { + margin-block-start: 10px; + font-size: 11px; + color: var(--e-a-color-warning); + text-align: start; + padding-inline-start: 90px; +} +.elementor-conditions-conflict-message a { + color: var(--e-a-color-warning); +} + +.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu { + display: flex; +} +.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item { + width: 100%; +} +.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item i { + margin-inline-end: 5px; +} +.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item > * { + display: inline-block; + line-height: 40px; +} + +.elementor-conditions-select2-dropdown { + border: none; + border-radius: 0; +} +.elementor-conditions-select2-dropdown .select2-results__message { + display: none; +} +.elementor-conditions-select2-dropdown .select2-search--dropdown .select2-search__field { + border-width: 0 0 1px; + border-radius: 0; +} + +#elementor-toast.e-theme-builder-save-toaster .dialog-buttons-wrapper { + flex-direction: column; +} +#elementor-toast.e-theme-builder-save-toaster .dialog-buttons-message { + text-align: center; +} +#elementor-toast.e-theme-builder-save-toaster .dialog-open_site_editor { + margin-block-end: 10px; +} + +.elementor-control.elementor-control-sitemap_items.elementor-control-type-repeater .elementor-repeater-row-tools .elementor-repeater-row-item-title span { + text-transform: capitalize; +} + +.elementor-editor-popup .elementor-tab-control-settings a:before { + content: "\e922"; +} + +#elementor-publish .elementor-popup__display-settings .elementor-control-type-slider .elementor-control-input-wrapper { + width: 150px; +} +#elementor-publish .elementor-popup__display-settings .elementor-control-input-wrapper { + width: 80px; + max-width: 100%; +} + +.elementor-popup__display-settings { + text-align: start; +} +.elementor-popup__display-settings_controls_group { + display: flex; + align-items: center; + height: 60px; + border: var(--e-a-border); + margin-block-end: 10px; + padding-inline-end: 20px; + border-radius: var(--e-a-border-radius); +} +.elementor-popup__display-settings_controls_group:hover { + background-color: var(--e-a-bg-hover); +} +.elementor-popup__display-settings_controls_group.elementor-active { + background-color: var(--e-a-bg-active); + color: var(--e-a-color-txt-accent); + border-color: var(--e-a-border-color-accent); +} +.elementor-popup__display-settings_controls_group:not(.elementor-active) .elementor-control:nth-child(2) h3 { + color: #9DA5AE; +} +.elementor-popup__display-settings_controls_group__icon { + width: 60px; + height: 60px; + border-inline-end: var(--e-a-border); + display: inline-flex; + align-items: center; + justify-content: center; +} +.elementor-popup__display-settings_controls_group__icon img { + width: 35px; +} +.elementor-popup__display-settings_controls_group .elementor-control { + padding: 0; + margin: 0; +} +.elementor-popup__display-settings_controls_group .elementor-control:nth-child(2) { + width: 230px; +} +.elementor-popup__display-settings_controls_group .elementor-control:nth-child(2) h3 { + font-size: 16px; + font-weight: 400; +} +.elementor-popup__display-settings_controls_group .elementor-control:not(:nth-child(2)) .elementor-control-title:not(:empty) { + width: 75px; +} +.elementor-popup__display-settings_controls_group .elementor-control:not(:last-child) { + margin-inline-start: 25px; +} +.elementor-popup__display-settings_controls_group .elementor-control-type-slider .elementor-control-input-wrapper { + display: flex; +} +.elementor-popup__display-settings_controls_group .elementor-control-type-slider .elementor-slider-input { + width: 45%; +} +.elementor-popup__display-settings_controls_group .elementor-control-type-switcher .elementor-control-field { + justify-content: flex-end; +} +.elementor-popup__display-settings .elementor-control-type-section { + display: none; +} +.elementor-popup__display-settings__group-toggle { + flex-grow: 1; +} + +#elementor-popup-timing__controls .select2-selection { + min-height: 27px; +} +#elementor-popup-timing__controls .select2-selection__rendered { + line-height: 1; +} +#elementor-popup-timing__controls .select2-selection__choice { + font-size: 10px; +} + +#elementor-popup__timing-controls-group--url .elementor-control-url_url .elementor-control-input-wrapper { + width: 200px; +} +#elementor-popup__timing-controls-group--sources .elementor-control-sources_sources .elementor-control-input-wrapper { + width: 300px; +} +#elementor-popup__timing-controls-group--devices .elementor-control-devices_devices .elementor-control-input-wrapper { + width: 330px; +} +#elementor-popup__timing-controls-group--logged_in .elementor-control-logged_in_roles .elementor-control-input-wrapper { + width: 195px; +} +#elementor-popup__timing-controls-group--browsers .elementor-control-browsers_browsers .elementor-control-input-wrapper { + width: 100px; +} +#elementor-popup__timing-controls-group--browsers .elementor-control-browsers_browsers_options .elementor-control-input-wrapper { + width: 200px; +} + +#elementor-popup__timing-controls-group--times .elementor-control-field { + display: unset; +} + +#elementor-popup__timing-controls-group--schedule .elementor-control-title { + width: unset; +} +#elementor-popup__timing-controls-group--schedule .elementor-control-input-wrapper { + margin-block-start: 5px; +} +#elementor-popup__timing-controls-group--schedule div[class*=elementor-control-schedule_] .elementor-control-content .elementor-control-field { + display: block; +} +#elementor-popup__timing-controls-group--schedule div[class*=elementor-control-schedule_] .elementor-control-content .elementor-control-input-wrapper { + width: 108px; +} +#elementor-popup__timing-controls-group--schedule .elementor-control-schedule_timezone { + margin-inline-start: -17px; +} + +.elementor-control-type-raw_html .elementor-descriptor-subtle a { + color: inherit; + border-block-end-color: inherit; + font-weight: 500; +} +.elementor-control-type-raw_html .elementor-descriptor-subtle a:hover { + color: #0C0D0E; +} + +.e-page-transition-preview::before { + content: ""; + height: 1em; + width: 0.8em; + margin-inline-end: 4px; + background-image: url("data:image/svg+xml,%3Csvg width='10' height='13' viewBox='0 0 10 13' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8.40554 6.20969C8.6115 6.34841 8.6115 6.65153 8.40554 6.79026L1.19553 11.6467C0.963052 11.8033 0.65 11.6368 0.65 11.3565L0.650001 1.64349C0.650001 1.36319 0.963054 1.19661 1.19553 1.3532L8.40554 6.20969Z' stroke='white' stroke-width='1.3'/%3E%3C/svg%3E%0A"); + background-repeat: no-repeat; + background-size: contain; + display: inline-block; + vertical-align: top; +} + +.elementor-template-query-control-actions { + display: flex; + align-items: center; + justify-content: center; + margin-block-start: 15px; + gap: 15px; +} + +.e-control-display-conditions__wrapper, +.e-control-display-conditions-promo__wrapper { + display: flex; + justify-content: space-between; +} +.e-control-display-conditions__desc, +.e-control-display-conditions-promo__desc { + align-self: center; +} +.e-control-display-conditions__desc .eicon-lock, +.e-control-display-conditions-promo__desc .eicon-lock { + margin-inline-start: 15px; +} +.e-control-display-conditions__desc .eicon-lock:hover, +.e-control-display-conditions-promo__desc .eicon-lock:hover { + color: var(--e-a-color-accent); +} +.e-control-display-conditions.eicon-flow, +.e-control-display-conditions-promo.eicon-flow { + align-self: flex-end; + cursor: pointer; + border: var(--e-a-border-bold); + border-radius: var(--e-a-border-radius); + padding: 5px; +} +.e-control-display-conditions.eicon-flow.filled, +.e-control-display-conditions-promo.eicon-flow.filled { + background-color: var(--e-a-bg-active); + color: #E73CF6; +} +/*# sourceMappingURL=editor.css.map */ \ No newline at end of file diff --git a/assets/css/editor.min.css b/assets/css/editor.min.css new file mode 100644 index 0000000..a9c19e4 --- /dev/null +++ b/assets/css/editor.min.css @@ -0,0 +1,2 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.elementor-panel .elementor-control .e-control-error{color:#f59e0b}.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-content{flex-flow:row;align-items:center}.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-title{width:45%}.elementor-panel .elementor-control.forms-field-shortcode .elementor-control-raw-html{width:55%}.elementor-panel .elementor-control .elementor-button.elementor-button-default.elementor-button-center{display:block;margin:0 auto}#elementor-element--promotion__dialog .dialog-header .eicon-pro-icon{visibility:hidden}.elementor-context-menu-list__item{position:relative}.elementor-context-menu-list__item--disabled .elementor-context-menu-list__item__title{opacity:.5}.elementor-context-menu-list__item__shortcut .eicon-advanced{font-size:16px;color:var(--e-a-color-txt-muted)}.elementor-context-menu-list__item__shortcut--link-fullwidth{position:absolute;left:0;top:0;width:100%;height:100%;z-index:2;cursor:pointer}#elementor-widget-template-empty-templates{margin-block-start:15px;text-align:center}.elementor-widget-template-empty-templates-title{padding:25px 0 30px}.elementor-widget-template-empty-templates-icon{font-size:96px}.elementor-widget-template-empty-templates-footer{color:var(--e-a-color-txt-muted);font-size:13px;font-style:italic;margin-block-end:15px}#elementor-panel-global-widget{height:100%}#elementor-panel-global-widget>*{background-color:var(--e-a-bg-default)}#elementor-global-widget-locked-header{border-block-end:var(--e-a-border)}#elementor-global-widget-locked-header.elementor-nerd-box{padding:40px 25px}#elementor-global-widget-locked-header.elementor-nerd-box .elementor-nerd-box-icon{margin-block-start:20px}#elementor-global-widget-locked-tools{margin-block-start:15px;padding:0 20px}.elementor-global-widget-locked-tool{display:flex;padding:20px 0;align-items:center}.elementor-global-widget-locked-tool .elementor-button{min-width:70px}.elementor-global-widget-locked-tool-description{flex-grow:1}#elementor-global-widget-locked-unlink{border-block-start:var(--e-a-border)}#elementor-global-templates .elementor-element{position:relative}#elementor-global-templates .elementor-element:before{position:absolute;font-family:eicons;content:"\e91f";top:5px;left:5px;font-size:10px}#elementor-global-templates .elementor-element:hover:before{color:var(--e-a-color-global)}#elementor-global-widget-loading{position:absolute;top:0;left:0;height:100%;width:100%;align-items:center;justify-content:center}#elementor-global-widget-loading:not(.elementor-hidden){display:flex}#elementor-global-widget-loading i{font-size:50px}.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields{margin:10px 0}.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields .elementor-control{padding:0}.elementor-panel .elementor-control-type-fields_map .elementor-repeater-fields:last-child{margin-block-end:0}.elementor-repeater-row--form-step .elementor-repeater-row-tools:hover{background-color:#babfc5}.elementor-repeater-row--form-step .elementor-repeater-row-tools div:not(.elementor-repeater-row-handle-sortable){background-color:#f1f2f3}.elementor-repeater-row--form-step .elementor-repeater-row-tools div:not(.elementor-repeater-row-handle-sortable):hover{background-color:#fff}.elementor-facebook-widget.fb_iframe_widget,.elementor-facebook-widget.fb_iframe_widget span{width:100%!important}.elementor-facebook-widget.fb_iframe_widget iframe{position:relative;width:100%!important}.elementor-facebook-widget.fb-like{height:1px}.elementor-widget-facebook-comments iframe{width:100%!important}#elementor-publish{height:100%;display:flex}#elementor-publish__modal .dialog-message{padding:0}#elementor-publish__modal .dialog-buttons-wrapper{display:flex}#elementor-publish__tabs{padding-block-start:50px}@media (max-width:1439px){#elementor-publish__tabs{width:28%}}#elementor-publish__screen{overflow:auto;padding:50px}.elementor-publish__tab{display:flex;align-items:center;position:relative;height:110px;padding:20px 15px;cursor:pointer}.elementor-publish__tab:hover{background-color:var(--e-a-bg-hover)}.elementor-publish__tab.elementor-active{background-color:var(--e-a-bg-active);color:var(--e-a-color-txt-accent)}.elementor-publish__tab.elementor-active:after{content:"";position:absolute;top:0;left:0;height:100%;width:3px;background-color:var(--e-a-border-color-accent)}.elementor-publish__tab__image{width:50px;flex-shrink:0}.elementor-publish__tab__image img{width:100%}.elementor-publish__tab__content{text-align:start;padding-inline-start:15px}.elementor-publish__tab__title{font-size:18px;font-weight:700}#elementor-theme-builder-conditions{margin:40px 0 60px}#elementor-theme-builder-conditions-view{overflow:hidden}#elementor-theme-builder-conditions .elementor-control{background-color:transparent;padding:0}#elementor-theme-builder-conditions .elementor-control:before{content:none}#elementor-theme-builder-conditions .elementor-control select{border-width:0;height:40px;padding:0 14px}#elementor-theme-builder-conditions .elementor-control-type-query{width:100px}#elementor-theme-builder-conditions .elementor-control-type-select .elementor-control-input-wrapper:after{right:10px}#elementor-theme-builder-conditions .elementor-control-type{width:120px}#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=include] .elementor-control-input-wrapper:before{content:"\e8cc"}#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=exclude] .elementor-control-input-wrapper:before{content:"\e8cd"}#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=exclude] select,#elementor-theme-builder-conditions .elementor-control-type[data-elementor-condition-type=include] select{padding-inline-start:33px}#elementor-theme-builder-conditions .elementor-control-type .elementor-control-input-wrapper:before{font-family:eicons;position:absolute;top:50%;transform:translateY(-50%);left:13px;font-size:15px}#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls{display:flex;flex-grow:1;margin-inline-end:10px;width:70%;border:var(--e-a-border-bold);overflow:hidden;border-radius:var(--e-a-border-radius)}#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls .elementor-control:not(:first-child){flex-grow:1;border-inline-start:var(--e-a-border-bold);margin-inline-start:1px}#elementor-theme-builder-conditions .elementor-theme-builder-conditions-repeater-row-controls .elementor-control:not(:first-child) select{border-radius:0}#elementor-theme-builder-conditions .elementor-repeater-fields-wrapper{max-width:700px;width:100%;margin:auto}#elementor-theme-builder-conditions .elementor-repeater-fields{display:flex;align-items:center;justify-content:center;margin-block-start:10px}#elementor-theme-builder-conditions .elementor-control-field{display:block}#elementor-theme-builder-conditions .elementor-control-spinner,#elementor-theme-builder-conditions .elementor-control-title{display:none}#elementor-theme-builder-conditions .elementor-control-input-wrapper{width:100%;max-width:none}#elementor-theme-builder-conditions .select2-selection{height:40px;border:none;border-radius:0}#elementor-theme-builder-conditions .select2-selection__rendered{line-height:40px;padding-inline-start:15px;text-align:start}#elementor-theme-builder-conditions .select2-selection__arrow{height:30px}#elementor-theme-builder-conditions .select2-selection__arrow b{border-width:4px 4px 0;margin-block-start:2px;margin-inline-start:-7px}#elementor-theme-builder-conditions .elementor-repeater-tool-remove{font-size:18px;cursor:pointer;color:var(--e-a-color-txt)}#elementor-theme-builder-conditions .elementor-button-wrapper{margin-block-start:50px}#elementor-theme-builder-conditions .elementor-repeater-add{padding:12px 26px}.elementor-error .elementor-theme-builder-conditions-repeater-row-controls{border:1px solid var(--e-a-color-warning)}.elementor-conditions-conflict-message{margin-block-start:10px;font-size:11px;color:var(--e-a-color-warning);text-align:start;padding-inline-start:90px}.elementor-conditions-conflict-message a{color:var(--e-a-color-warning)}.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu{display:flex}.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item{width:100%}.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item i{margin-inline-end:5px}.elementor-panel-footer-theme-builder-buttons-wrapper .elementor-panel-footer-sub-menu-item>*{display:inline-block;line-height:40px}.elementor-conditions-select2-dropdown{border:none;border-radius:0}.elementor-conditions-select2-dropdown .select2-results__message{display:none}.elementor-conditions-select2-dropdown .select2-search--dropdown .select2-search__field{border-width:0 0 1px;border-radius:0}#elementor-toast.e-theme-builder-save-toaster .dialog-buttons-wrapper{flex-direction:column}#elementor-toast.e-theme-builder-save-toaster .dialog-buttons-message{text-align:center}#elementor-toast.e-theme-builder-save-toaster .dialog-open_site_editor{margin-block-end:10px}.elementor-control.elementor-control-sitemap_items.elementor-control-type-repeater .elementor-repeater-row-tools .elementor-repeater-row-item-title span{text-transform:capitalize}.elementor-editor-popup .elementor-tab-control-settings a:before{content:"\e922"}#elementor-publish .elementor-popup__display-settings .elementor-control-type-slider .elementor-control-input-wrapper{width:150px}#elementor-publish .elementor-popup__display-settings .elementor-control-input-wrapper{width:80px;max-width:100%}.elementor-popup__display-settings{text-align:start}.elementor-popup__display-settings_controls_group{display:flex;align-items:center;height:60px;border:var(--e-a-border);margin-block-end:10px;padding-inline-end:20px;border-radius:var(--e-a-border-radius)}.elementor-popup__display-settings_controls_group:hover{background-color:var(--e-a-bg-hover)}.elementor-popup__display-settings_controls_group.elementor-active{background-color:var(--e-a-bg-active);color:var(--e-a-color-txt-accent);border-color:var(--e-a-border-color-accent)}.elementor-popup__display-settings_controls_group:not(.elementor-active) .elementor-control:nth-child(2) h3{color:#9da5ae}.elementor-popup__display-settings_controls_group__icon{width:60px;height:60px;border-inline-end:var(--e-a-border);display:inline-flex;align-items:center;justify-content:center}.elementor-popup__display-settings_controls_group__icon img{width:35px}.elementor-popup__display-settings_controls_group .elementor-control{padding:0;margin:0}.elementor-popup__display-settings_controls_group .elementor-control:nth-child(2){width:230px}.elementor-popup__display-settings_controls_group .elementor-control:nth-child(2) h3{font-size:16px;font-weight:400}.elementor-popup__display-settings_controls_group .elementor-control:not(:nth-child(2)) .elementor-control-title:not(:empty){width:75px}.elementor-popup__display-settings_controls_group .elementor-control:not(:last-child){margin-inline-start:25px}.elementor-popup__display-settings_controls_group .elementor-control-type-slider .elementor-control-input-wrapper{display:flex}.elementor-popup__display-settings_controls_group .elementor-control-type-slider .elementor-slider-input{width:45%}.elementor-popup__display-settings_controls_group .elementor-control-type-switcher .elementor-control-field{justify-content:flex-end}.elementor-popup__display-settings .elementor-control-type-section{display:none}.elementor-popup__display-settings__group-toggle{flex-grow:1}#elementor-popup-timing__controls .select2-selection{min-height:27px}#elementor-popup-timing__controls .select2-selection__rendered{line-height:1}#elementor-popup-timing__controls .select2-selection__choice{font-size:10px}#elementor-popup__timing-controls-group--url .elementor-control-url_url .elementor-control-input-wrapper{width:200px}#elementor-popup__timing-controls-group--sources .elementor-control-sources_sources .elementor-control-input-wrapper{width:300px}#elementor-popup__timing-controls-group--devices .elementor-control-devices_devices .elementor-control-input-wrapper{width:330px}#elementor-popup__timing-controls-group--logged_in .elementor-control-logged_in_roles .elementor-control-input-wrapper{width:195px}#elementor-popup__timing-controls-group--browsers .elementor-control-browsers_browsers .elementor-control-input-wrapper{width:100px}#elementor-popup__timing-controls-group--browsers .elementor-control-browsers_browsers_options .elementor-control-input-wrapper{width:200px}#elementor-popup__timing-controls-group--times .elementor-control-field{display:unset}#elementor-popup__timing-controls-group--schedule .elementor-control-title{width:unset}#elementor-popup__timing-controls-group--schedule .elementor-control-input-wrapper{margin-block-start:5px}#elementor-popup__timing-controls-group--schedule div[class*=elementor-control-schedule_] .elementor-control-content .elementor-control-field{display:block}#elementor-popup__timing-controls-group--schedule div[class*=elementor-control-schedule_] .elementor-control-content .elementor-control-input-wrapper{width:108px}#elementor-popup__timing-controls-group--schedule .elementor-control-schedule_timezone{margin-inline-start:-17px}.elementor-control-type-raw_html .elementor-descriptor-subtle a{color:inherit;border-block-end-color:inherit;font-weight:500}.elementor-control-type-raw_html .elementor-descriptor-subtle a:hover{color:#0c0d0e}.e-page-transition-preview:before{content:"";height:1em;width:.8em;margin-inline-end:4px;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='10' height='13' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8.406 6.21a.35.35 0 010 .58l-7.21 4.857a.35.35 0 01-.546-.29V1.643a.35.35 0 01.546-.29l7.21 4.857z' stroke='%23fff' stroke-width='1.3'/%3E%3C/svg%3E");background-repeat:no-repeat;background-size:contain;display:inline-block;vertical-align:top}.elementor-template-query-control-actions{display:flex;align-items:center;justify-content:center;margin-block-start:15px;gap:15px}.e-control-display-conditions-promo__wrapper,.e-control-display-conditions__wrapper{display:flex;justify-content:space-between}.e-control-display-conditions-promo__desc,.e-control-display-conditions__desc{align-self:center}.e-control-display-conditions-promo__desc .eicon-lock,.e-control-display-conditions__desc .eicon-lock{margin-inline-start:15px}.e-control-display-conditions-promo__desc .eicon-lock:hover,.e-control-display-conditions__desc .eicon-lock:hover{color:var(--e-a-color-accent)}.e-control-display-conditions-promo.eicon-flow,.e-control-display-conditions.eicon-flow{align-self:flex-end;cursor:pointer;border:var(--e-a-border-bold);border-radius:var(--e-a-border-radius);padding:5px}.e-control-display-conditions-promo.eicon-flow.filled,.e-control-display-conditions.eicon-flow.filled{background-color:var(--e-a-bg-active);color:#e73cf6} \ No newline at end of file diff --git a/assets/css/frontend-lite-rtl.css b/assets/css/frontend-lite-rtl.css new file mode 100644 index 0000000..814c748 --- /dev/null +++ b/assets/css/frontend-lite-rtl.css @@ -0,0 +1,272 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.elementor-bg-transform .elementor-bg { + will-change: transform; +} +.elementor-bg-transform-zoom-in:hover .elementor-bg { + transform: scale(1.2); +} +.elementor-bg-transform-zoom-out .elementor-bg { + transform: scale(1.2); +} +.elementor-bg-transform-zoom-out:hover .elementor-bg { + transform: scale(1); +} +.elementor-bg-transform-move-left .elementor-bg { + transform: scale(1.2) translateX(8%); +} +.elementor-bg-transform-move-left:hover .elementor-bg { + transform: scale(1.2) translateX(-8%); +} +.elementor-bg-transform-move-right .elementor-bg { + transform: scale(1.2) translateX(-8%); +} +.elementor-bg-transform-move-right:hover .elementor-bg { + transform: scale(1.2) translateX(8%); +} +.elementor-bg-transform-move-up .elementor-bg { + transform: scale(1.2) translateY(8%); +} +.elementor-bg-transform-move-up:hover .elementor-bg { + transform: scale(1.2) translateY(-8%); +} +.elementor-bg-transform-move-down .elementor-bg { + transform: scale(1.2) translateY(-8%); +} +.elementor-bg-transform-move-down:hover .elementor-bg { + transform: scale(1.2) translateY(8%); +} + +/*---------------------------------------------------------------------------*/ +.elementor-animated-content { + --translate: 0, 0; +} +.elementor-animated-content:hover .elementor-animated-item--grow, .elementor-animated-content:focus .elementor-animated-item--grow { + transform: scale(1.1); +} +.elementor-animated-content:hover .elementor-animated-item--shrink, .elementor-animated-content:focus .elementor-animated-item--shrink { + transform: scale(0.85); +} +.elementor-animated-content:hover .elementor-animated-item--shrink-contained, .elementor-animated-content:focus .elementor-animated-item--shrink-contained { + transform: scale(1); +} +.elementor-animated-content:hover .elementor-animated-item--enter-zoom-out, .elementor-animated-content:hover .elementor-animated-item--enter-zoom-in, .elementor-animated-content:hover .elementor-animated-item--fade-in, .elementor-animated-content:focus .elementor-animated-item--enter-zoom-out, .elementor-animated-content:focus .elementor-animated-item--enter-zoom-in, .elementor-animated-content:focus .elementor-animated-item--fade-in { + transform: scale(1); + opacity: 1; +} +.elementor-animated-content:hover .elementor-animated-item--exit-zoom-out, .elementor-animated-content:hover .elementor-animated-item--exit-zoom-in, .elementor-animated-content:hover .elementor-animated-item--fade-out, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-out, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-in, .elementor-animated-content:focus .elementor-animated-item--fade-out { + opacity: 0; +} +.elementor-animated-content:hover .elementor-animated-item--exit-zoom-out, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-out { + transform: scale(0.2); +} +.elementor-animated-content:hover .elementor-animated-item--exit-zoom-in, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-in { + transform: scale(2); +} +.elementor-animated-content:hover .elementor-animated-item--enter-from-right, .elementor-animated-content:hover .elementor-animated-item--enter-from-left, .elementor-animated-content:hover .elementor-animated-item--enter-from-top, .elementor-animated-content:hover .elementor-animated-item--enter-from-bottom, .elementor-animated-content:focus .elementor-animated-item--enter-from-right, .elementor-animated-content:focus .elementor-animated-item--enter-from-left, .elementor-animated-content:focus .elementor-animated-item--enter-from-top, .elementor-animated-content:focus .elementor-animated-item--enter-from-bottom { + opacity: 1; + transform: translateY(0) translateX(0); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-right, .elementor-animated-content:focus .elementor-animated-item--exit-to-right { + transform: translateX(1000px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-left, .elementor-animated-content:focus .elementor-animated-item--exit-to-left { + transform: translateX(-1000px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-top, .elementor-animated-content:focus .elementor-animated-item--exit-to-top { + transform: translateY(-600px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-bottom, .elementor-animated-content:focus .elementor-animated-item--exit-to-bottom { + transform: translateY(600px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-right, .elementor-animated-content:hover .elementor-animated-item--exit-to-left, .elementor-animated-content:hover .elementor-animated-item--exit-to-top, .elementor-animated-content:hover .elementor-animated-item--exit-to-bottom, .elementor-animated-content:focus .elementor-animated-item--exit-to-right, .elementor-animated-content:focus .elementor-animated-item--exit-to-left, .elementor-animated-content:focus .elementor-animated-item--exit-to-top, .elementor-animated-content:focus .elementor-animated-item--exit-to-bottom { + opacity: 0; +} +.elementor-animated-content:hover .elementor-animated-item--move-right, .elementor-animated-content:focus .elementor-animated-item--move-right { + transform: translateX(30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-left, .elementor-animated-content:focus .elementor-animated-item--move-left { + transform: translateX(-30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-up, .elementor-animated-content:focus .elementor-animated-item--move-up { + transform: translateY(-30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-down, .elementor-animated-content:focus .elementor-animated-item--move-down { + transform: translateY(30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-right, .elementor-animated-content:focus .elementor-animated-item--move-contained-right { + --translate: 8%, 0; +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-left, .elementor-animated-content:focus .elementor-animated-item--move-contained-left { + --translate: -8%, 0; +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-top, .elementor-animated-content:focus .elementor-animated-item--move-contained-top { + --translate: 0, -8%; +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-bottom, .elementor-animated-content:focus .elementor-animated-item--move-contained-bottom { + --translate: 0, 8%; +} +.elementor-animated-content *[class^=elementor-animated-item] { + will-change: transform, opacity; +} +.elementor-animated-content .elementor-animated-item--shrink-contained { + transform: scale(1.17); +} +.elementor-animated-content .elementor-animated-item--enter-zoom-in { + transform: scale(0.2); +} +.elementor-animated-content .elementor-animated-item--enter-zoom-out { + transform: scale(2); +} +.elementor-animated-content .elementor-animated-item--enter-zoom-out, .elementor-animated-content .elementor-animated-item--enter-zoom-in, .elementor-animated-content .elementor-animated-item--fade-in { + opacity: 0; +} +.elementor-animated-content .elementor-animated-item--exit-zoom-out, .elementor-animated-content .elementor-animated-item--exit-zoom-in, .elementor-animated-content .elementor-animated-item--fade-out { + opacity: 1; + transform: scale(1); +} +.elementor-animated-content .elementor-animated-item--enter-from-right { + transform: translateX(1000px); +} +.elementor-animated-content .elementor-animated-item--enter-from-left { + transform: translateX(-1000px); +} +.elementor-animated-content .elementor-animated-item--enter-from-top { + transform: translateY(-600px); +} +.elementor-animated-content .elementor-animated-item--enter-from-bottom { + transform: translateY(500px); +} +.elementor-animated-content .elementor-animated-item--enter-from-right, .elementor-animated-content .elementor-animated-item--enter-from-left, .elementor-animated-content .elementor-animated-item--enter-from-top, .elementor-animated-content .elementor-animated-item--enter-from-bottom { + opacity: 0; +} +.elementor-animated-content .elementor-animated-item--exit-to-right, .elementor-animated-content .elementor-animated-item--exit-to-left, .elementor-animated-content .elementor-animated-item--exit-to-top, .elementor-animated-content .elementor-animated-item--exit-to-bottom { + opacity: 1; + transform: translateY(0) translateX(0); +} +.elementor-animated-content .elementor-animated-item--move-contained-right, .elementor-animated-content .elementor-animated-item--move-contained-left, .elementor-animated-content .elementor-animated-item--move-contained-top, .elementor-animated-content .elementor-animated-item--move-contained-bottom { + transform: scale(1.2) translate(var(--translate)); +} + +.elementor-editor-active .elementor.elementor-edit-mode .elementor-widget.elementor-global-widget:hover { + outline: 1px solid var(--e-p-border-global); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings { + background-color: var(--e-p-border-global); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting { + background-color: var(--e-p-border-global); + color: var(--e-p-border-global-invert); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting:hover { + background-color: var(--e-p-border-global-hover); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting.elementor-editor-element-save { + display: none; +} + +.elementor-theme-builder-content-area { + height: 400px; +} + +.elementor-location-header:before, +.elementor-location-footer:before { + content: ""; + display: table; + clear: both; +} + +.elementor-posts.elementor-posts--skin-archive_full_content article.elementor-post { + display: block; +} + +.elementor-sticky--active { + z-index: 99; +} + +.e-con.elementor-sticky--active { + z-index: var(--z-index, 99); +} + +[data-elementor-type=popup]:not(.elementor-edit-area) { + display: none; +} +[data-elementor-type=popup] .elementor-section-wrap:not(:empty) + #elementor-add-new-section { + display: none; +} + +.elementor-popup-modal { + display: flex; + pointer-events: none; + background-color: transparent; + -webkit-user-select: auto; + -moz-user-select: auto; + user-select: auto; +} +.elementor-popup-modal .dialog-header, .elementor-popup-modal .dialog-buttons-wrapper { + display: none; +} +.elementor-popup-modal .dialog-close-button { + display: none; + top: 20px; + margin-top: 0; + left: 20px; + opacity: 1; + z-index: 9999; + pointer-events: all; +} +.elementor-popup-modal .dialog-close-button svg { + fill: #1f2124; + height: 1em; + width: 1em; +} +.elementor-popup-modal .dialog-widget-content { + background-color: #FFFFFF; + width: initial; + overflow: visible; + max-width: 100%; + max-height: 100%; + border-radius: 0; + box-shadow: none; + pointer-events: all; +} +.elementor-popup-modal .dialog-message { + width: 640px; + max-width: 100vw; + max-height: 100vh; + padding: 0; + overflow: auto; + display: flex; +} +.elementor-popup-modal .elementor { + width: 100%; +} + +.elementor-motion-effects-element, .elementor-motion-effects-layer { + transition-property: transform, opacity; + transition-timing-function: cubic-bezier(0, 0.33, 0.07, 1.03); + transition-duration: 1s; +} + +.elementor-motion-effects-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + transform-origin: var(--e-transform-origin-y) var(--e-transform-origin-x); +} +.elementor-motion-effects-layer { + position: absolute; + top: 0; + left: 0; + background-repeat: no-repeat; + background-size: cover; +} +.elementor-motion-effects-perspective { + perspective: 1200px; +} +.elementor-motion-effects-element { + transform-origin: var(--e-transform-origin-y) var(--e-transform-origin-x); +} +/*# sourceMappingURL=frontend-lite-rtl.css.map */ \ No newline at end of file diff --git a/assets/css/frontend-lite-rtl.min.css b/assets/css/frontend-lite-rtl.min.css new file mode 100644 index 0000000..21751c9 --- /dev/null +++ b/assets/css/frontend-lite-rtl.min.css @@ -0,0 +1,2 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.elementor-bg-transform .elementor-bg{will-change:transform}.elementor-bg-transform-zoom-in:hover .elementor-bg,.elementor-bg-transform-zoom-out .elementor-bg{transform:scale(1.2)}.elementor-bg-transform-zoom-out:hover .elementor-bg{transform:scale(1)}.elementor-bg-transform-move-left .elementor-bg{transform:scale(1.2) translateX(8%)}.elementor-bg-transform-move-left:hover .elementor-bg,.elementor-bg-transform-move-right .elementor-bg{transform:scale(1.2) translateX(-8%)}.elementor-bg-transform-move-right:hover .elementor-bg{transform:scale(1.2) translateX(8%)}.elementor-bg-transform-move-up .elementor-bg{transform:scale(1.2) translateY(8%)}.elementor-bg-transform-move-down .elementor-bg,.elementor-bg-transform-move-up:hover .elementor-bg{transform:scale(1.2) translateY(-8%)}.elementor-bg-transform-move-down:hover .elementor-bg{transform:scale(1.2) translateY(8%)}.elementor-animated-content{--translate:0,0}.elementor-animated-content:focus .elementor-animated-item--grow,.elementor-animated-content:hover .elementor-animated-item--grow{transform:scale(1.1)}.elementor-animated-content:focus .elementor-animated-item--shrink,.elementor-animated-content:hover .elementor-animated-item--shrink{transform:scale(.85)}.elementor-animated-content:focus .elementor-animated-item--shrink-contained,.elementor-animated-content:hover .elementor-animated-item--shrink-contained{transform:scale(1)}.elementor-animated-content:focus .elementor-animated-item--enter-zoom-in,.elementor-animated-content:focus .elementor-animated-item--enter-zoom-out,.elementor-animated-content:focus .elementor-animated-item--fade-in,.elementor-animated-content:hover .elementor-animated-item--enter-zoom-in,.elementor-animated-content:hover .elementor-animated-item--enter-zoom-out,.elementor-animated-content:hover .elementor-animated-item--fade-in{transform:scale(1);opacity:1}.elementor-animated-content:focus .elementor-animated-item--exit-zoom-in,.elementor-animated-content:focus .elementor-animated-item--exit-zoom-out,.elementor-animated-content:focus .elementor-animated-item--fade-out,.elementor-animated-content:hover .elementor-animated-item--exit-zoom-in,.elementor-animated-content:hover .elementor-animated-item--exit-zoom-out,.elementor-animated-content:hover .elementor-animated-item--fade-out{opacity:0}.elementor-animated-content:focus .elementor-animated-item--exit-zoom-out,.elementor-animated-content:hover .elementor-animated-item--exit-zoom-out{transform:scale(.2)}.elementor-animated-content:focus .elementor-animated-item--exit-zoom-in,.elementor-animated-content:hover .elementor-animated-item--exit-zoom-in{transform:scale(2)}.elementor-animated-content:focus .elementor-animated-item--enter-from-bottom,.elementor-animated-content:focus .elementor-animated-item--enter-from-left,.elementor-animated-content:focus .elementor-animated-item--enter-from-right,.elementor-animated-content:focus .elementor-animated-item--enter-from-top,.elementor-animated-content:hover .elementor-animated-item--enter-from-bottom,.elementor-animated-content:hover .elementor-animated-item--enter-from-left,.elementor-animated-content:hover .elementor-animated-item--enter-from-right,.elementor-animated-content:hover .elementor-animated-item--enter-from-top{opacity:1;transform:translateY(0) translateX(0)}.elementor-animated-content:focus .elementor-animated-item--exit-to-right,.elementor-animated-content:hover .elementor-animated-item--exit-to-right{transform:translateX(1000px)}.elementor-animated-content:focus .elementor-animated-item--exit-to-left,.elementor-animated-content:hover .elementor-animated-item--exit-to-left{transform:translateX(-1000px)}.elementor-animated-content:focus .elementor-animated-item--exit-to-top,.elementor-animated-content:hover .elementor-animated-item--exit-to-top{transform:translateY(-600px)}.elementor-animated-content:focus .elementor-animated-item--exit-to-bottom,.elementor-animated-content:hover .elementor-animated-item--exit-to-bottom{transform:translateY(600px)}.elementor-animated-content:focus .elementor-animated-item--exit-to-bottom,.elementor-animated-content:focus .elementor-animated-item--exit-to-left,.elementor-animated-content:focus .elementor-animated-item--exit-to-right,.elementor-animated-content:focus .elementor-animated-item--exit-to-top,.elementor-animated-content:hover .elementor-animated-item--exit-to-bottom,.elementor-animated-content:hover .elementor-animated-item--exit-to-left,.elementor-animated-content:hover .elementor-animated-item--exit-to-right,.elementor-animated-content:hover .elementor-animated-item--exit-to-top{opacity:0}.elementor-animated-content:focus .elementor-animated-item--move-right,.elementor-animated-content:hover .elementor-animated-item--move-right{transform:translateX(30px)}.elementor-animated-content:focus .elementor-animated-item--move-left,.elementor-animated-content:hover .elementor-animated-item--move-left{transform:translateX(-30px)}.elementor-animated-content:focus .elementor-animated-item--move-up,.elementor-animated-content:hover .elementor-animated-item--move-up{transform:translateY(-30px)}.elementor-animated-content:focus .elementor-animated-item--move-down,.elementor-animated-content:hover .elementor-animated-item--move-down{transform:translateY(30px)}.elementor-animated-content:focus .elementor-animated-item--move-contained-right,.elementor-animated-content:hover .elementor-animated-item--move-contained-right{--translate:8%,0}.elementor-animated-content:focus .elementor-animated-item--move-contained-left,.elementor-animated-content:hover .elementor-animated-item--move-contained-left{--translate:-8%,0}.elementor-animated-content:focus .elementor-animated-item--move-contained-top,.elementor-animated-content:hover .elementor-animated-item--move-contained-top{--translate:0,-8%}.elementor-animated-content:focus .elementor-animated-item--move-contained-bottom,.elementor-animated-content:hover .elementor-animated-item--move-contained-bottom{--translate:0,8%}.elementor-animated-content [class^=elementor-animated-item]{will-change:transform,opacity}.elementor-animated-content .elementor-animated-item--shrink-contained{transform:scale(1.17)}.elementor-animated-content .elementor-animated-item--enter-zoom-in{transform:scale(.2)}.elementor-animated-content .elementor-animated-item--enter-zoom-out{transform:scale(2)}.elementor-animated-content .elementor-animated-item--enter-zoom-in,.elementor-animated-content .elementor-animated-item--enter-zoom-out,.elementor-animated-content .elementor-animated-item--fade-in{opacity:0}.elementor-animated-content .elementor-animated-item--exit-zoom-in,.elementor-animated-content .elementor-animated-item--exit-zoom-out,.elementor-animated-content .elementor-animated-item--fade-out{opacity:1;transform:scale(1)}.elementor-animated-content .elementor-animated-item--enter-from-right{transform:translateX(1000px)}.elementor-animated-content .elementor-animated-item--enter-from-left{transform:translateX(-1000px)}.elementor-animated-content .elementor-animated-item--enter-from-top{transform:translateY(-600px)}.elementor-animated-content .elementor-animated-item--enter-from-bottom{transform:translateY(500px)}.elementor-animated-content .elementor-animated-item--enter-from-bottom,.elementor-animated-content .elementor-animated-item--enter-from-left,.elementor-animated-content .elementor-animated-item--enter-from-right,.elementor-animated-content .elementor-animated-item--enter-from-top{opacity:0}.elementor-animated-content .elementor-animated-item--exit-to-bottom,.elementor-animated-content .elementor-animated-item--exit-to-left,.elementor-animated-content .elementor-animated-item--exit-to-right,.elementor-animated-content .elementor-animated-item--exit-to-top{opacity:1;transform:translateY(0) translateX(0)}.elementor-animated-content .elementor-animated-item--move-contained-bottom,.elementor-animated-content .elementor-animated-item--move-contained-left,.elementor-animated-content .elementor-animated-item--move-contained-right,.elementor-animated-content .elementor-animated-item--move-contained-top{transform:scale(1.2) translate(var(--translate))}.elementor-editor-active .elementor.elementor-edit-mode .elementor-widget.elementor-global-widget:hover{outline:1px solid var(--e-p-border-global)}.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings{background-color:var(--e-p-border-global)}.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting{background-color:var(--e-p-border-global);color:var(--e-p-border-global-invert)}.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting:hover{background-color:var(--e-p-border-global-hover)}.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting.elementor-editor-element-save{display:none}.elementor-theme-builder-content-area{height:400px}.elementor-location-footer:before,.elementor-location-header:before{content:"";display:table;clear:both}.elementor-posts.elementor-posts--skin-archive_full_content article.elementor-post{display:block}.elementor-sticky--active{z-index:99}.e-con.elementor-sticky--active{z-index:var(--z-index,99)}[data-elementor-type=popup] .elementor-section-wrap:not(:empty)+#elementor-add-new-section,[data-elementor-type=popup]:not(.elementor-edit-area){display:none}.elementor-popup-modal{display:flex;pointer-events:none;background-color:transparent;-webkit-user-select:auto;-moz-user-select:auto;user-select:auto}.elementor-popup-modal .dialog-buttons-wrapper,.elementor-popup-modal .dialog-header{display:none}.elementor-popup-modal .dialog-close-button{display:none;top:20px;margin-top:0;left:20px;opacity:1;z-index:9999;pointer-events:all}.elementor-popup-modal .dialog-close-button svg{fill:#1f2124;height:1em;width:1em}.elementor-popup-modal .dialog-widget-content{background-color:#fff;width:auto;overflow:visible;max-width:100%;max-height:100%;border-radius:0;box-shadow:none;pointer-events:all}.elementor-popup-modal .dialog-message{width:640px;max-width:100vw;max-height:100vh;padding:0;overflow:auto;display:flex}.elementor-popup-modal .elementor{width:100%}.elementor-motion-effects-element,.elementor-motion-effects-layer{transition-property:transform,opacity;transition-timing-function:cubic-bezier(0,.33,.07,1.03);transition-duration:1s}.elementor-motion-effects-container{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;transform-origin:var(--e-transform-origin-y) var(--e-transform-origin-x)}.elementor-motion-effects-layer{position:absolute;top:0;left:0;background-repeat:no-repeat;background-size:cover}.elementor-motion-effects-perspective{perspective:1200px}.elementor-motion-effects-element{transform-origin:var(--e-transform-origin-y) var(--e-transform-origin-x)} \ No newline at end of file diff --git a/assets/css/frontend-lite.css b/assets/css/frontend-lite.css new file mode 100644 index 0000000..696d18a --- /dev/null +++ b/assets/css/frontend-lite.css @@ -0,0 +1,272 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.elementor-bg-transform .elementor-bg { + will-change: transform; +} +.elementor-bg-transform-zoom-in:hover .elementor-bg { + transform: scale(1.2); +} +.elementor-bg-transform-zoom-out .elementor-bg { + transform: scale(1.2); +} +.elementor-bg-transform-zoom-out:hover .elementor-bg { + transform: scale(1); +} +.elementor-bg-transform-move-left .elementor-bg { + transform: scale(1.2) translateX(8%); +} +.elementor-bg-transform-move-left:hover .elementor-bg { + transform: scale(1.2) translateX(-8%); +} +.elementor-bg-transform-move-right .elementor-bg { + transform: scale(1.2) translateX(-8%); +} +.elementor-bg-transform-move-right:hover .elementor-bg { + transform: scale(1.2) translateX(8%); +} +.elementor-bg-transform-move-up .elementor-bg { + transform: scale(1.2) translateY(8%); +} +.elementor-bg-transform-move-up:hover .elementor-bg { + transform: scale(1.2) translateY(-8%); +} +.elementor-bg-transform-move-down .elementor-bg { + transform: scale(1.2) translateY(-8%); +} +.elementor-bg-transform-move-down:hover .elementor-bg { + transform: scale(1.2) translateY(8%); +} + +/*---------------------------------------------------------------------------*/ +.elementor-animated-content { + --translate: 0, 0; +} +.elementor-animated-content:hover .elementor-animated-item--grow, .elementor-animated-content:focus .elementor-animated-item--grow { + transform: scale(1.1); +} +.elementor-animated-content:hover .elementor-animated-item--shrink, .elementor-animated-content:focus .elementor-animated-item--shrink { + transform: scale(0.85); +} +.elementor-animated-content:hover .elementor-animated-item--shrink-contained, .elementor-animated-content:focus .elementor-animated-item--shrink-contained { + transform: scale(1); +} +.elementor-animated-content:hover .elementor-animated-item--enter-zoom-out, .elementor-animated-content:hover .elementor-animated-item--enter-zoom-in, .elementor-animated-content:hover .elementor-animated-item--fade-in, .elementor-animated-content:focus .elementor-animated-item--enter-zoom-out, .elementor-animated-content:focus .elementor-animated-item--enter-zoom-in, .elementor-animated-content:focus .elementor-animated-item--fade-in { + transform: scale(1); + opacity: 1; +} +.elementor-animated-content:hover .elementor-animated-item--exit-zoom-out, .elementor-animated-content:hover .elementor-animated-item--exit-zoom-in, .elementor-animated-content:hover .elementor-animated-item--fade-out, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-out, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-in, .elementor-animated-content:focus .elementor-animated-item--fade-out { + opacity: 0; +} +.elementor-animated-content:hover .elementor-animated-item--exit-zoom-out, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-out { + transform: scale(0.2); +} +.elementor-animated-content:hover .elementor-animated-item--exit-zoom-in, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-in { + transform: scale(2); +} +.elementor-animated-content:hover .elementor-animated-item--enter-from-right, .elementor-animated-content:hover .elementor-animated-item--enter-from-left, .elementor-animated-content:hover .elementor-animated-item--enter-from-top, .elementor-animated-content:hover .elementor-animated-item--enter-from-bottom, .elementor-animated-content:focus .elementor-animated-item--enter-from-right, .elementor-animated-content:focus .elementor-animated-item--enter-from-left, .elementor-animated-content:focus .elementor-animated-item--enter-from-top, .elementor-animated-content:focus .elementor-animated-item--enter-from-bottom { + opacity: 1; + transform: translateY(0) translateX(0); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-right, .elementor-animated-content:focus .elementor-animated-item--exit-to-right { + transform: translateX(1000px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-left, .elementor-animated-content:focus .elementor-animated-item--exit-to-left { + transform: translateX(-1000px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-top, .elementor-animated-content:focus .elementor-animated-item--exit-to-top { + transform: translateY(-600px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-bottom, .elementor-animated-content:focus .elementor-animated-item--exit-to-bottom { + transform: translateY(600px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-right, .elementor-animated-content:hover .elementor-animated-item--exit-to-left, .elementor-animated-content:hover .elementor-animated-item--exit-to-top, .elementor-animated-content:hover .elementor-animated-item--exit-to-bottom, .elementor-animated-content:focus .elementor-animated-item--exit-to-right, .elementor-animated-content:focus .elementor-animated-item--exit-to-left, .elementor-animated-content:focus .elementor-animated-item--exit-to-top, .elementor-animated-content:focus .elementor-animated-item--exit-to-bottom { + opacity: 0; +} +.elementor-animated-content:hover .elementor-animated-item--move-right, .elementor-animated-content:focus .elementor-animated-item--move-right { + transform: translateX(30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-left, .elementor-animated-content:focus .elementor-animated-item--move-left { + transform: translateX(-30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-up, .elementor-animated-content:focus .elementor-animated-item--move-up { + transform: translateY(-30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-down, .elementor-animated-content:focus .elementor-animated-item--move-down { + transform: translateY(30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-right, .elementor-animated-content:focus .elementor-animated-item--move-contained-right { + --translate: 8%, 0; +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-left, .elementor-animated-content:focus .elementor-animated-item--move-contained-left { + --translate: -8%, 0; +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-top, .elementor-animated-content:focus .elementor-animated-item--move-contained-top { + --translate: 0, -8%; +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-bottom, .elementor-animated-content:focus .elementor-animated-item--move-contained-bottom { + --translate: 0, 8%; +} +.elementor-animated-content *[class^=elementor-animated-item] { + will-change: transform, opacity; +} +.elementor-animated-content .elementor-animated-item--shrink-contained { + transform: scale(1.17); +} +.elementor-animated-content .elementor-animated-item--enter-zoom-in { + transform: scale(0.2); +} +.elementor-animated-content .elementor-animated-item--enter-zoom-out { + transform: scale(2); +} +.elementor-animated-content .elementor-animated-item--enter-zoom-out, .elementor-animated-content .elementor-animated-item--enter-zoom-in, .elementor-animated-content .elementor-animated-item--fade-in { + opacity: 0; +} +.elementor-animated-content .elementor-animated-item--exit-zoom-out, .elementor-animated-content .elementor-animated-item--exit-zoom-in, .elementor-animated-content .elementor-animated-item--fade-out { + opacity: 1; + transform: scale(1); +} +.elementor-animated-content .elementor-animated-item--enter-from-right { + transform: translateX(1000px); +} +.elementor-animated-content .elementor-animated-item--enter-from-left { + transform: translateX(-1000px); +} +.elementor-animated-content .elementor-animated-item--enter-from-top { + transform: translateY(-600px); +} +.elementor-animated-content .elementor-animated-item--enter-from-bottom { + transform: translateY(500px); +} +.elementor-animated-content .elementor-animated-item--enter-from-right, .elementor-animated-content .elementor-animated-item--enter-from-left, .elementor-animated-content .elementor-animated-item--enter-from-top, .elementor-animated-content .elementor-animated-item--enter-from-bottom { + opacity: 0; +} +.elementor-animated-content .elementor-animated-item--exit-to-right, .elementor-animated-content .elementor-animated-item--exit-to-left, .elementor-animated-content .elementor-animated-item--exit-to-top, .elementor-animated-content .elementor-animated-item--exit-to-bottom { + opacity: 1; + transform: translateY(0) translateX(0); +} +.elementor-animated-content .elementor-animated-item--move-contained-right, .elementor-animated-content .elementor-animated-item--move-contained-left, .elementor-animated-content .elementor-animated-item--move-contained-top, .elementor-animated-content .elementor-animated-item--move-contained-bottom { + transform: scale(1.2) translate(var(--translate)); +} + +.elementor-editor-active .elementor.elementor-edit-mode .elementor-widget.elementor-global-widget:hover { + outline: 1px solid var(--e-p-border-global); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings { + background-color: var(--e-p-border-global); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting { + background-color: var(--e-p-border-global); + color: var(--e-p-border-global-invert); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting:hover { + background-color: var(--e-p-border-global-hover); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting.elementor-editor-element-save { + display: none; +} + +.elementor-theme-builder-content-area { + height: 400px; +} + +.elementor-location-header:before, +.elementor-location-footer:before { + content: ""; + display: table; + clear: both; +} + +.elementor-posts.elementor-posts--skin-archive_full_content article.elementor-post { + display: block; +} + +.elementor-sticky--active { + z-index: 99; +} + +.e-con.elementor-sticky--active { + z-index: var(--z-index, 99); +} + +[data-elementor-type=popup]:not(.elementor-edit-area) { + display: none; +} +[data-elementor-type=popup] .elementor-section-wrap:not(:empty) + #elementor-add-new-section { + display: none; +} + +.elementor-popup-modal { + display: flex; + pointer-events: none; + background-color: transparent; + -webkit-user-select: auto; + -moz-user-select: auto; + user-select: auto; +} +.elementor-popup-modal .dialog-header, .elementor-popup-modal .dialog-buttons-wrapper { + display: none; +} +.elementor-popup-modal .dialog-close-button { + display: none; + top: 20px; + margin-top: 0; + right: 20px; + opacity: 1; + z-index: 9999; + pointer-events: all; +} +.elementor-popup-modal .dialog-close-button svg { + fill: #1f2124; + height: 1em; + width: 1em; +} +.elementor-popup-modal .dialog-widget-content { + background-color: #FFFFFF; + width: initial; + overflow: visible; + max-width: 100%; + max-height: 100%; + border-radius: 0; + box-shadow: none; + pointer-events: all; +} +.elementor-popup-modal .dialog-message { + width: 640px; + max-width: 100vw; + max-height: 100vh; + padding: 0; + overflow: auto; + display: flex; +} +.elementor-popup-modal .elementor { + width: 100%; +} + +.elementor-motion-effects-element, .elementor-motion-effects-layer { + transition-property: transform, opacity; + transition-timing-function: cubic-bezier(0, 0.33, 0.07, 1.03); + transition-duration: 1s; +} + +.elementor-motion-effects-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + transform-origin: var(--e-transform-origin-y) var(--e-transform-origin-x); +} +.elementor-motion-effects-layer { + position: absolute; + top: 0; + left: 0; + background-repeat: no-repeat; + background-size: cover; +} +.elementor-motion-effects-perspective { + perspective: 1200px; +} +.elementor-motion-effects-element { + transform-origin: var(--e-transform-origin-y) var(--e-transform-origin-x); +} +/*# sourceMappingURL=frontend-lite.css.map */ \ No newline at end of file diff --git a/assets/css/frontend-lite.min.css b/assets/css/frontend-lite.min.css new file mode 100644 index 0000000..40fe0a1 --- /dev/null +++ b/assets/css/frontend-lite.min.css @@ -0,0 +1,2 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +.elementor-bg-transform .elementor-bg{will-change:transform}.elementor-bg-transform-zoom-in:hover .elementor-bg,.elementor-bg-transform-zoom-out .elementor-bg{transform:scale(1.2)}.elementor-bg-transform-zoom-out:hover .elementor-bg{transform:scale(1)}.elementor-bg-transform-move-left .elementor-bg{transform:scale(1.2) translateX(8%)}.elementor-bg-transform-move-left:hover .elementor-bg,.elementor-bg-transform-move-right .elementor-bg{transform:scale(1.2) translateX(-8%)}.elementor-bg-transform-move-right:hover .elementor-bg{transform:scale(1.2) translateX(8%)}.elementor-bg-transform-move-up .elementor-bg{transform:scale(1.2) translateY(8%)}.elementor-bg-transform-move-down .elementor-bg,.elementor-bg-transform-move-up:hover .elementor-bg{transform:scale(1.2) translateY(-8%)}.elementor-bg-transform-move-down:hover .elementor-bg{transform:scale(1.2) translateY(8%)}.elementor-animated-content{--translate:0,0}.elementor-animated-content:focus .elementor-animated-item--grow,.elementor-animated-content:hover .elementor-animated-item--grow{transform:scale(1.1)}.elementor-animated-content:focus .elementor-animated-item--shrink,.elementor-animated-content:hover .elementor-animated-item--shrink{transform:scale(.85)}.elementor-animated-content:focus .elementor-animated-item--shrink-contained,.elementor-animated-content:hover .elementor-animated-item--shrink-contained{transform:scale(1)}.elementor-animated-content:focus .elementor-animated-item--enter-zoom-in,.elementor-animated-content:focus .elementor-animated-item--enter-zoom-out,.elementor-animated-content:focus .elementor-animated-item--fade-in,.elementor-animated-content:hover .elementor-animated-item--enter-zoom-in,.elementor-animated-content:hover .elementor-animated-item--enter-zoom-out,.elementor-animated-content:hover .elementor-animated-item--fade-in{transform:scale(1);opacity:1}.elementor-animated-content:focus .elementor-animated-item--exit-zoom-in,.elementor-animated-content:focus .elementor-animated-item--exit-zoom-out,.elementor-animated-content:focus .elementor-animated-item--fade-out,.elementor-animated-content:hover .elementor-animated-item--exit-zoom-in,.elementor-animated-content:hover .elementor-animated-item--exit-zoom-out,.elementor-animated-content:hover .elementor-animated-item--fade-out{opacity:0}.elementor-animated-content:focus .elementor-animated-item--exit-zoom-out,.elementor-animated-content:hover .elementor-animated-item--exit-zoom-out{transform:scale(.2)}.elementor-animated-content:focus .elementor-animated-item--exit-zoom-in,.elementor-animated-content:hover .elementor-animated-item--exit-zoom-in{transform:scale(2)}.elementor-animated-content:focus .elementor-animated-item--enter-from-bottom,.elementor-animated-content:focus .elementor-animated-item--enter-from-left,.elementor-animated-content:focus .elementor-animated-item--enter-from-right,.elementor-animated-content:focus .elementor-animated-item--enter-from-top,.elementor-animated-content:hover .elementor-animated-item--enter-from-bottom,.elementor-animated-content:hover .elementor-animated-item--enter-from-left,.elementor-animated-content:hover .elementor-animated-item--enter-from-right,.elementor-animated-content:hover .elementor-animated-item--enter-from-top{opacity:1;transform:translateY(0) translateX(0)}.elementor-animated-content:focus .elementor-animated-item--exit-to-right,.elementor-animated-content:hover .elementor-animated-item--exit-to-right{transform:translateX(1000px)}.elementor-animated-content:focus .elementor-animated-item--exit-to-left,.elementor-animated-content:hover .elementor-animated-item--exit-to-left{transform:translateX(-1000px)}.elementor-animated-content:focus .elementor-animated-item--exit-to-top,.elementor-animated-content:hover .elementor-animated-item--exit-to-top{transform:translateY(-600px)}.elementor-animated-content:focus .elementor-animated-item--exit-to-bottom,.elementor-animated-content:hover .elementor-animated-item--exit-to-bottom{transform:translateY(600px)}.elementor-animated-content:focus .elementor-animated-item--exit-to-bottom,.elementor-animated-content:focus .elementor-animated-item--exit-to-left,.elementor-animated-content:focus .elementor-animated-item--exit-to-right,.elementor-animated-content:focus .elementor-animated-item--exit-to-top,.elementor-animated-content:hover .elementor-animated-item--exit-to-bottom,.elementor-animated-content:hover .elementor-animated-item--exit-to-left,.elementor-animated-content:hover .elementor-animated-item--exit-to-right,.elementor-animated-content:hover .elementor-animated-item--exit-to-top{opacity:0}.elementor-animated-content:focus .elementor-animated-item--move-right,.elementor-animated-content:hover .elementor-animated-item--move-right{transform:translateX(30px)}.elementor-animated-content:focus .elementor-animated-item--move-left,.elementor-animated-content:hover .elementor-animated-item--move-left{transform:translateX(-30px)}.elementor-animated-content:focus .elementor-animated-item--move-up,.elementor-animated-content:hover .elementor-animated-item--move-up{transform:translateY(-30px)}.elementor-animated-content:focus .elementor-animated-item--move-down,.elementor-animated-content:hover .elementor-animated-item--move-down{transform:translateY(30px)}.elementor-animated-content:focus .elementor-animated-item--move-contained-right,.elementor-animated-content:hover .elementor-animated-item--move-contained-right{--translate:8%,0}.elementor-animated-content:focus .elementor-animated-item--move-contained-left,.elementor-animated-content:hover .elementor-animated-item--move-contained-left{--translate:-8%,0}.elementor-animated-content:focus .elementor-animated-item--move-contained-top,.elementor-animated-content:hover .elementor-animated-item--move-contained-top{--translate:0,-8%}.elementor-animated-content:focus .elementor-animated-item--move-contained-bottom,.elementor-animated-content:hover .elementor-animated-item--move-contained-bottom{--translate:0,8%}.elementor-animated-content [class^=elementor-animated-item]{will-change:transform,opacity}.elementor-animated-content .elementor-animated-item--shrink-contained{transform:scale(1.17)}.elementor-animated-content .elementor-animated-item--enter-zoom-in{transform:scale(.2)}.elementor-animated-content .elementor-animated-item--enter-zoom-out{transform:scale(2)}.elementor-animated-content .elementor-animated-item--enter-zoom-in,.elementor-animated-content .elementor-animated-item--enter-zoom-out,.elementor-animated-content .elementor-animated-item--fade-in{opacity:0}.elementor-animated-content .elementor-animated-item--exit-zoom-in,.elementor-animated-content .elementor-animated-item--exit-zoom-out,.elementor-animated-content .elementor-animated-item--fade-out{opacity:1;transform:scale(1)}.elementor-animated-content .elementor-animated-item--enter-from-right{transform:translateX(1000px)}.elementor-animated-content .elementor-animated-item--enter-from-left{transform:translateX(-1000px)}.elementor-animated-content .elementor-animated-item--enter-from-top{transform:translateY(-600px)}.elementor-animated-content .elementor-animated-item--enter-from-bottom{transform:translateY(500px)}.elementor-animated-content .elementor-animated-item--enter-from-bottom,.elementor-animated-content .elementor-animated-item--enter-from-left,.elementor-animated-content .elementor-animated-item--enter-from-right,.elementor-animated-content .elementor-animated-item--enter-from-top{opacity:0}.elementor-animated-content .elementor-animated-item--exit-to-bottom,.elementor-animated-content .elementor-animated-item--exit-to-left,.elementor-animated-content .elementor-animated-item--exit-to-right,.elementor-animated-content .elementor-animated-item--exit-to-top{opacity:1;transform:translateY(0) translateX(0)}.elementor-animated-content .elementor-animated-item--move-contained-bottom,.elementor-animated-content .elementor-animated-item--move-contained-left,.elementor-animated-content .elementor-animated-item--move-contained-right,.elementor-animated-content .elementor-animated-item--move-contained-top{transform:scale(1.2) translate(var(--translate))}.elementor-editor-active .elementor.elementor-edit-mode .elementor-widget.elementor-global-widget:hover{outline:1px solid var(--e-p-border-global)}.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings{background-color:var(--e-p-border-global)}.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting{background-color:var(--e-p-border-global);color:var(--e-p-border-global-invert)}.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting:hover{background-color:var(--e-p-border-global-hover)}.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting.elementor-editor-element-save{display:none}.elementor-theme-builder-content-area{height:400px}.elementor-location-footer:before,.elementor-location-header:before{content:"";display:table;clear:both}.elementor-posts.elementor-posts--skin-archive_full_content article.elementor-post{display:block}.elementor-sticky--active{z-index:99}.e-con.elementor-sticky--active{z-index:var(--z-index,99)}[data-elementor-type=popup] .elementor-section-wrap:not(:empty)+#elementor-add-new-section,[data-elementor-type=popup]:not(.elementor-edit-area){display:none}.elementor-popup-modal{display:flex;pointer-events:none;background-color:transparent;-webkit-user-select:auto;-moz-user-select:auto;user-select:auto}.elementor-popup-modal .dialog-buttons-wrapper,.elementor-popup-modal .dialog-header{display:none}.elementor-popup-modal .dialog-close-button{display:none;top:20px;margin-top:0;right:20px;opacity:1;z-index:9999;pointer-events:all}.elementor-popup-modal .dialog-close-button svg{fill:#1f2124;height:1em;width:1em}.elementor-popup-modal .dialog-widget-content{background-color:#fff;width:auto;overflow:visible;max-width:100%;max-height:100%;border-radius:0;box-shadow:none;pointer-events:all}.elementor-popup-modal .dialog-message{width:640px;max-width:100vw;max-height:100vh;padding:0;overflow:auto;display:flex}.elementor-popup-modal .elementor{width:100%}.elementor-motion-effects-element,.elementor-motion-effects-layer{transition-property:transform,opacity;transition-timing-function:cubic-bezier(0,.33,.07,1.03);transition-duration:1s}.elementor-motion-effects-container{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;transform-origin:var(--e-transform-origin-y) var(--e-transform-origin-x)}.elementor-motion-effects-layer{position:absolute;top:0;left:0;background-repeat:no-repeat;background-size:cover}.elementor-motion-effects-perspective{perspective:1200px}.elementor-motion-effects-element{transform-origin:var(--e-transform-origin-y) var(--e-transform-origin-x)} \ No newline at end of file diff --git a/assets/css/frontend-msie.css b/assets/css/frontend-msie.css new file mode 100644 index 0000000..a596131 --- /dev/null +++ b/assets/css/frontend-msie.css @@ -0,0 +1,11176 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +@charset "UTF-8"; +.elementor-bg-transform .elementor-bg { + will-change: transform; +} +.elementor-bg-transform-zoom-in:hover .elementor-bg { + transform: scale(1.2); +} +.elementor-bg-transform-zoom-out .elementor-bg { + transform: scale(1.2); +} +.elementor-bg-transform-zoom-out:hover .elementor-bg { + transform: scale(1); +} +.elementor-bg-transform-move-left .elementor-bg { + transform: scale(1.2) translateX(8%); +} +.elementor-bg-transform-move-left:hover .elementor-bg { + transform: scale(1.2) translateX(-8%); +} +.elementor-bg-transform-move-right .elementor-bg { + transform: scale(1.2) translateX(-8%); +} +.elementor-bg-transform-move-right:hover .elementor-bg { + transform: scale(1.2) translateX(8%); +} +.elementor-bg-transform-move-up .elementor-bg { + transform: scale(1.2) translateY(8%); +} +.elementor-bg-transform-move-up:hover .elementor-bg { + transform: scale(1.2) translateY(-8%); +} +.elementor-bg-transform-move-down .elementor-bg { + transform: scale(1.2) translateY(-8%); +} +.elementor-bg-transform-move-down:hover .elementor-bg { + transform: scale(1.2) translateY(8%); +} + +/*---------------------------------------------------------------------------*/ +.elementor-animated-content { + --translate: 0, 0; +} +.elementor-animated-content:hover .elementor-animated-item--grow, .elementor-animated-content:focus .elementor-animated-item--grow { + transform: scale(1.1); +} +.elementor-animated-content:hover .elementor-animated-item--shrink, .elementor-animated-content:focus .elementor-animated-item--shrink { + transform: scale(0.85); +} +.elementor-animated-content:hover .elementor-animated-item--shrink-contained, .elementor-animated-content:focus .elementor-animated-item--shrink-contained { + transform: scale(1); +} +.elementor-animated-content:hover .elementor-animated-item--enter-zoom-out, .elementor-animated-content:hover .elementor-animated-item--enter-zoom-in, .elementor-animated-content:hover .elementor-animated-item--fade-in, .elementor-animated-content:focus .elementor-animated-item--enter-zoom-out, .elementor-animated-content:focus .elementor-animated-item--enter-zoom-in, .elementor-animated-content:focus .elementor-animated-item--fade-in { + transform: scale(1); + opacity: 1; +} +.elementor-animated-content:hover .elementor-animated-item--exit-zoom-out, .elementor-animated-content:hover .elementor-animated-item--exit-zoom-in, .elementor-animated-content:hover .elementor-animated-item--fade-out, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-out, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-in, .elementor-animated-content:focus .elementor-animated-item--fade-out { + opacity: 0; +} +.elementor-animated-content:hover .elementor-animated-item--exit-zoom-out, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-out { + transform: scale(0.2); +} +.elementor-animated-content:hover .elementor-animated-item--exit-zoom-in, .elementor-animated-content:focus .elementor-animated-item--exit-zoom-in { + transform: scale(2); +} +.elementor-animated-content:hover .elementor-animated-item--enter-from-right, .elementor-animated-content:hover .elementor-animated-item--enter-from-left, .elementor-animated-content:hover .elementor-animated-item--enter-from-top, .elementor-animated-content:hover .elementor-animated-item--enter-from-bottom, .elementor-animated-content:focus .elementor-animated-item--enter-from-right, .elementor-animated-content:focus .elementor-animated-item--enter-from-left, .elementor-animated-content:focus .elementor-animated-item--enter-from-top, .elementor-animated-content:focus .elementor-animated-item--enter-from-bottom { + opacity: 1; + transform: translateY(0) translateX(0); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-right, .elementor-animated-content:focus .elementor-animated-item--exit-to-right { + transform: translateX(1000px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-left, .elementor-animated-content:focus .elementor-animated-item--exit-to-left { + transform: translateX(-1000px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-top, .elementor-animated-content:focus .elementor-animated-item--exit-to-top { + transform: translateY(-600px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-bottom, .elementor-animated-content:focus .elementor-animated-item--exit-to-bottom { + transform: translateY(600px); +} +.elementor-animated-content:hover .elementor-animated-item--exit-to-right, .elementor-animated-content:hover .elementor-animated-item--exit-to-left, .elementor-animated-content:hover .elementor-animated-item--exit-to-top, .elementor-animated-content:hover .elementor-animated-item--exit-to-bottom, .elementor-animated-content:focus .elementor-animated-item--exit-to-right, .elementor-animated-content:focus .elementor-animated-item--exit-to-left, .elementor-animated-content:focus .elementor-animated-item--exit-to-top, .elementor-animated-content:focus .elementor-animated-item--exit-to-bottom { + opacity: 0; +} +.elementor-animated-content:hover .elementor-animated-item--move-right, .elementor-animated-content:focus .elementor-animated-item--move-right { + transform: translateX(30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-left, .elementor-animated-content:focus .elementor-animated-item--move-left { + transform: translateX(-30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-up, .elementor-animated-content:focus .elementor-animated-item--move-up { + transform: translateY(-30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-down, .elementor-animated-content:focus .elementor-animated-item--move-down { + transform: translateY(30px); +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-right, .elementor-animated-content:focus .elementor-animated-item--move-contained-right { + --translate: 8%, 0; +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-left, .elementor-animated-content:focus .elementor-animated-item--move-contained-left { + --translate: -8%, 0; +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-top, .elementor-animated-content:focus .elementor-animated-item--move-contained-top { + --translate: 0, -8%; +} +.elementor-animated-content:hover .elementor-animated-item--move-contained-bottom, .elementor-animated-content:focus .elementor-animated-item--move-contained-bottom { + --translate: 0, 8%; +} +.elementor-animated-content *[class^=elementor-animated-item] { + will-change: transform, opacity; +} +.elementor-animated-content .elementor-animated-item--shrink-contained { + transform: scale(1.17); +} +.elementor-animated-content .elementor-animated-item--enter-zoom-in { + transform: scale(0.2); +} +.elementor-animated-content .elementor-animated-item--enter-zoom-out { + transform: scale(2); +} +.elementor-animated-content .elementor-animated-item--enter-zoom-out, .elementor-animated-content .elementor-animated-item--enter-zoom-in, .elementor-animated-content .elementor-animated-item--fade-in { + opacity: 0; +} +.elementor-animated-content .elementor-animated-item--exit-zoom-out, .elementor-animated-content .elementor-animated-item--exit-zoom-in, .elementor-animated-content .elementor-animated-item--fade-out { + opacity: 1; + transform: scale(1); +} +.elementor-animated-content .elementor-animated-item--enter-from-right { + transform: translateX(1000px); +} +.elementor-animated-content .elementor-animated-item--enter-from-left { + transform: translateX(-1000px); +} +.elementor-animated-content .elementor-animated-item--enter-from-top { + transform: translateY(-600px); +} +.elementor-animated-content .elementor-animated-item--enter-from-bottom { + transform: translateY(500px); +} +.elementor-animated-content .elementor-animated-item--enter-from-right, .elementor-animated-content .elementor-animated-item--enter-from-left, .elementor-animated-content .elementor-animated-item--enter-from-top, .elementor-animated-content .elementor-animated-item--enter-from-bottom { + opacity: 0; +} +.elementor-animated-content .elementor-animated-item--exit-to-right, .elementor-animated-content .elementor-animated-item--exit-to-left, .elementor-animated-content .elementor-animated-item--exit-to-top, .elementor-animated-content .elementor-animated-item--exit-to-bottom { + opacity: 1; + transform: translateY(0) translateX(0); +} +.elementor-animated-content .elementor-animated-item--move-contained-right, .elementor-animated-content .elementor-animated-item--move-contained-left, .elementor-animated-content .elementor-animated-item--move-contained-top, .elementor-animated-content .elementor-animated-item--move-contained-bottom { + transform: scale(1.2) translate(var(--translate)); +} + +.elementor-editor-active .elementor.elementor-edit-mode .elementor-widget.elementor-global-widget:hover { + outline: 1px solid var(--e-p-border-global); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings { + background-color: var(--e-p-border-global); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting { + background-color: var(--e-p-border-global); + color: var(--e-p-border-global-invert); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting:hover { + background-color: var(--e-p-border-global-hover); +} +.elementor-editor-active .elementor.elementor-edit-mode .elementor-global-widget .elementor-editor-widget-settings .elementor-editor-element-setting.elementor-editor-element-save { + display: none; +} + +.elementor-theme-builder-content-area { + height: 400px; +} + +.elementor-location-header:before, +.elementor-location-footer:before { + content: ""; + display: table; + clear: both; +} + +.elementor-posts.elementor-posts--skin-archive_full_content article.elementor-post { + display: block; +} + +.elementor-sticky--active { + z-index: 99; +} + +.e-con.elementor-sticky--active { + z-index: var(--z-index, 99); +} + +[data-elementor-type=popup]:not(.elementor-edit-area) { + display: none; +} +[data-elementor-type=popup] .elementor-section-wrap:not(:empty) + #elementor-add-new-section { + display: none; +} + +.elementor-popup-modal { + display: flex; + pointer-events: none; + background-color: transparent; + -webkit-user-select: auto; + -moz-user-select: auto; + user-select: auto; +} +.elementor-popup-modal .dialog-header, .elementor-popup-modal .dialog-buttons-wrapper { + display: none; +} +.elementor-popup-modal .dialog-close-button { + display: none; + top: 20px; + margin-top: 0; + right: 20px; + opacity: 1; + z-index: 9999; + pointer-events: all; +} +.elementor-popup-modal .dialog-close-button svg { + fill: #1f2124; + height: 1em; + width: 1em; +} +.elementor-popup-modal .dialog-widget-content { + background-color: #FFFFFF; + width: initial; + overflow: visible; + max-width: 100%; + max-height: 100%; + border-radius: 0; + box-shadow: none; + pointer-events: all; +} +.elementor-popup-modal .dialog-message { + width: 640px; + max-width: 100vw; + max-height: 100vh; + padding: 0; + overflow: auto; + display: flex; +} +.elementor-popup-modal .elementor { + width: 100%; +} + +.elementor-motion-effects-element, .elementor-motion-effects-layer { + transition-property: transform, opacity; + transition-timing-function: cubic-bezier(0, 0.33, 0.07, 1.03); + transition-duration: 1s; +} + +.elementor-motion-effects-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + transform-origin: var(--e-transform-origin-y) var(--e-transform-origin-x); +} +.elementor-motion-effects-layer { + position: absolute; + top: 0; + left: 0; + background-repeat: no-repeat; + background-size: cover; +} +.elementor-motion-effects-perspective { + perspective: 1200px; +} +.elementor-motion-effects-element { + transform-origin: var(--e-transform-origin-y) var(--e-transform-origin-x); +} + +body.woocommerce #content div.product .elementor-widget-woocommerce-product-images div.images, +body.woocommerce div.product .elementor-widget-woocommerce-product-images div.images, body.woocommerce-page #content div.product .elementor-widget-woocommerce-product-images div.images, +body.woocommerce-page div.product .elementor-widget-woocommerce-product-images div.images { + float: none; + width: 100%; + padding: 0; +} +body.rtl.woocommerce #content div.product .elementor-widget-woocommerce-product-images div.images, +body.rtl.woocommerce div.product .elementor-widget-woocommerce-product-images div.images, body.rtl.woocommerce-page #content div.product .elementor-widget-woocommerce-product-images div.images, +body.rtl.woocommerce-page div.product .elementor-widget-woocommerce-product-images div.images { + float: none; + padding: 0; +} + +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) form.cart { + margin: 0; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) form.cart.variations_form .woocommerce-variation-add-to-cart { + display: flex; + flex-wrap: nowrap; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) form.cart button:where(:not(:first-child)), +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) form.cart .button:where(:not(:first-child)) { + margin-top: 0; + margin-left: var(--button-spacing, 10px); +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) .e-loop-add-to-cart-form-container { + display: flex; + flex-wrap: wrap; + gap: var(--view-cart-spacing, 10px); +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) .e-loop-add-to-cart-form-container > * { + display: flex; + flex-basis: auto; + margin: 0; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) .quantity { + vertical-align: middle; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) .quantity .qty { + vertical-align: top; + margin-right: 0; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) .quantity input { + height: 100%; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-stacked form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-stacked form.cart.variations_form .woocommerce-variation-add-to-cart, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-auto form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-auto form.cart.variations_form .woocommerce-variation-add-to-cart { + display: block; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-stacked .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-auto .e-atc-qty-button-holder { + display: flex; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-stacked .e-loop-add-to-cart-form-container { + flex-wrap: wrap; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-stacked .e-loop-add-to-cart-form-container > * { + flex-basis: 100%; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-stacked .e-atc-qty-button-holder { + flex-wrap: wrap; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-stacked .e-atc-qty-button-holder > * { + flex-basis: 100%; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-stacked .e-atc-qty-button-holder button, +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-stacked .e-atc-qty-button-holder .button { + flex-basis: auto; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-stacked .e-atc-qty-button-holder button:where(:not(:first-child)), +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-stacked .e-atc-qty-button-holder .button:where(:not(:first-child)) { + margin-left: 0; + margin-top: var(--button-spacing, 10px); +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-auto .e-atc-qty-button-holder { + flex-wrap: nowrap; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-auto .e-atc-qty-button-holder .quantity { + margin-right: initial; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-auto .e-atc-qty-button-holder button, +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--layout-auto .e-atc-qty-button-holder .button { + vertical-align: middle; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: left; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-start; + text-align: left; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-left[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-left[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-start; + text-align: left; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-left .e-loop-add-to-cart-form-container { + justify-content: flex-start; + text-align: left; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: right; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-end; + text-align: right; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-right[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-right[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-end; + text-align: right; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-right .e-loop-add-to-cart-form-container { + justify-content: flex-end; + text-align: right; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: center; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: center; + text-align: center; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-center[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-center[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: center; + text-align: center; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-center .e-loop-add-to-cart-form-container { + justify-content: center; + text-align: center; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-left form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-right form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-center form.cart div.quantity { + margin-right: initial; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-left form.cart button, +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-left form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-right form.cart button, +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-right form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-center form.cart button, +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-center form.cart .button { + flex-basis: auto; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-justify:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) .elementor-button { + width: 100%; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-justify .e-loop-add-to-cart-form-container > * { + flex-basis: 100%; + justify-content: center; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-justify .e-loop-add-to-cart-form-container a.added_to_cart { + flex-basis: auto; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-justify form.cart div.quantity { + margin-right: auto; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-justify form.cart button, +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, +.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart--align-justify form.cart .button { + flex-basis: 100%; +} +@media (min-width: -1) { + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-left[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-left[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-left .e-loop-add-to-cart-form-container { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-right[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-right[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-right .e-loop-add-to-cart-form-container { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-center[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-center[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-center .e-loop-add-to-cart-form-container { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-left form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-right form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-center form.cart div.quantity { + margin-right: initial; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-left form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-left form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-right form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-right form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-center form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-center form.cart .button { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-justify:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) .elementor-button { + width: 100%; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-justify .e-loop-add-to-cart-form-container > * { + flex-basis: 100%; + justify-content: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-justify .e-loop-add-to-cart-form-container a.added_to_cart { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-justify form.cart div.quantity { + margin-right: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-justify form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-widescreen--align-justify form.cart .button { + flex-basis: 100%; + } +} +@media (max-width: -1) { + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-left[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-left[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-left .e-loop-add-to-cart-form-container { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-right[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-right[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-right .e-loop-add-to-cart-form-container { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-center[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-center[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-center .e-loop-add-to-cart-form-container { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-left form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-right form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-center form.cart div.quantity { + margin-right: initial; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-left form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-left form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-right form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-right form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-center form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-center form.cart .button { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-justify:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) .elementor-button { + width: 100%; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-justify .e-loop-add-to-cart-form-container > * { + flex-basis: 100%; + justify-content: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-justify .e-loop-add-to-cart-form-container a.added_to_cart { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-justify form.cart div.quantity { + margin-right: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-justify form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-laptop--align-justify form.cart .button { + flex-basis: 100%; + } +} +@media (max-width: -1) { + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-left[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-left[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-left .e-loop-add-to-cart-form-container { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-right[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-right[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-right .e-loop-add-to-cart-form-container { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-center[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-center[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-center .e-loop-add-to-cart-form-container { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-left form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-right form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-center form.cart div.quantity { + margin-right: initial; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-left form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-left form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-right form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-right form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-center form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-center form.cart .button { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-justify:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) .elementor-button { + width: 100%; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-justify .e-loop-add-to-cart-form-container > * { + flex-basis: 100%; + justify-content: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-justify .e-loop-add-to-cart-form-container a.added_to_cart { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-justify form.cart div.quantity { + margin-right: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-justify form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet_extra--align-justify form.cart .button { + flex-basis: 100%; + } +} +@media (max-width: 1024px) { + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-left[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-left[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-left .e-loop-add-to-cart-form-container { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-right[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-right[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-right .e-loop-add-to-cart-form-container { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-center[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-center[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-center .e-loop-add-to-cart-form-container { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-left form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-right form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-center form.cart div.quantity { + margin-right: initial; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-left form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-left form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-right form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-right form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-center form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-center form.cart .button { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-justify:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) .elementor-button { + width: 100%; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-justify .e-loop-add-to-cart-form-container > * { + flex-basis: 100%; + justify-content: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-justify .e-loop-add-to-cart-form-container a.added_to_cart { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-justify form.cart div.quantity { + margin-right: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-justify form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-tablet--align-justify form.cart .button { + flex-basis: 100%; + } +} +@media (max-width: -1) { + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-left[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-left[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-left .e-loop-add-to-cart-form-container { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-right[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-right[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-right .e-loop-add-to-cart-form-container { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-center[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-center[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-center .e-loop-add-to-cart-form-container { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-left form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-right form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-center form.cart div.quantity { + margin-right: initial; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-left form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-left form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-right form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-right form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-center form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-center form.cart .button { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-justify:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) .elementor-button { + width: 100%; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-justify .e-loop-add-to-cart-form-container > * { + flex-basis: 100%; + justify-content: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-justify .e-loop-add-to-cart-form-container a.added_to_cart { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-justify form.cart div.quantity { + margin-right: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-justify form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile_extra--align-justify form.cart .button { + flex-basis: 100%; + } +} +@media (max-width: 767px) { + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-left:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-left[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-left[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-left .e-loop-add-to-cart-form-container { + justify-content: flex-start; + text-align: left; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-right:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-right[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-right[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-right .e-loop-add-to-cart-form-container { + justify-content: flex-end; + text-align: right; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) { + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart:not(.grouped_form):not(.variations_form), :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-center:not([class*="--layout-stacked"]):not([class*="--layout-auto"]) form.cart.variations_form .woocommerce-variation-add-to-cart { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-center[class*="--layout-stacked"] .e-atc-qty-button-holder, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-center[class*="--layout-auto"] .e-atc-qty-button-holder { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-center .e-loop-add-to-cart-form-container { + justify-content: center; + text-align: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-left form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-right form.cart div.quantity, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-center form.cart div.quantity { + margin-right: initial; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-left form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-left form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-right form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-right form.cart .button, :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-center form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-center form.cart .button { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-justify:not([class*="--layout-stacked"]):not([class*="--layout-auto"]):not([class*=-product-add-to-cart]) .elementor-button { + width: 100%; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-justify .e-loop-add-to-cart-form-container > * { + flex-basis: 100%; + justify-content: center; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-justify .e-loop-add-to-cart-form-container a.added_to_cart { + flex-basis: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-justify form.cart div.quantity { + margin-right: auto; + } + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-justify form.cart button, + :is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart, + .elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-add-to-cart-mobile--align-justify form.cart .button { + flex-basis: 100%; + } +} + +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart):not(.e-add-to-cart--show-quantity-yes) form.cart .quantity { + display: none !important; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart):not(.e-add-to-cart--show-quantity-yes) form.cart button:where(:not(:first-child)), +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart):not(.e-add-to-cart--show-quantity-yes) form.cart .button:where(:not(:first-child)) { + margin-left: 0; +} +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart):not(.e-add-to-cart--show-quantity-yes)[class*="--layout-stacked"] form.cart button:where(:not(:first-child)), +:is(.elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .elementor-widget-woocommerce-product-add-to-cart):not(.e-add-to-cart--show-quantity-yes)[class*="--layout-stacked"] form.cart .button:where(:not(:first-child)) { + margin-top: 0; +} + +:is(.e-loop-item .elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .e-loop-item .elementor-widget-woocommerce-product-add-to-cart) form.cart input.qty.disabled { + pointer-events: none; +} +:is(.e-loop-item .elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .e-loop-item .elementor-widget-woocommerce-product-add-to-cart) form.cart .button { + text-align: center; +} +:is(.e-loop-item .elementor-widget-woocommerce-product-add-to-cart, .woocommerce div.product .e-loop-item .elementor-widget-woocommerce-product-add-to-cart) .added_to_cart { + margin: 0; + padding: 0; + display: flex; + align-items: center; +} + +:is(.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) .quantity { + vertical-align: middle; +} +:is(.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart) .quantity .qty { + vertical-align: top; + margin-right: 0; + width: 3.631em; + text-align: center; +} +:is(.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-button-info button.button.alt.elementor-button { + background-color: #5bc0de; +} +:is(.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-button-success button.button.alt.elementor-button { + background-color: #5cb85c; +} +:is(.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-button-warning button.button.alt.elementor-button { + background-color: #f0ad4e; +} +:is(.elementor-widget-wc-add-to-cart, .woocommerce div.product .elementor-widget-wc-add-to-cart).elementor-button-danger button.button.alt.elementor-button { + background-color: #d9534f; +} + +.woocommerce div.product.elementor { + /* Reset WC tabs style */ + /* End Reset WC tabs style */ +} +.woocommerce div.product.elementor ul.tabs:before { + position: static; + content: none; + width: auto; + bottom: auto; + left: auto; + border-bottom: 0; + z-index: auto; +} +.woocommerce div.product.elementor ul.tabs { + margin: 0; +} +.woocommerce div.product.elementor ul.tabs li { + padding: 0; +} +.woocommerce div.product.elementor ul.tabs li a { + padding: 0.8em 1.2em; + line-height: 1; +} +.woocommerce div.product.elementor ul.tabs li:after, +.woocommerce div.product.elementor ul.tabs li:before { + border: 0; + position: static; + bottom: auto; + width: auto; + height: auto; + content: none; + box-sizing: border-box; +} +.woocommerce div.product.elementor .woocommerce-tabs .panel { + margin: 0; + padding: 20px; + border-radius: 0; + border-width: 0; + border-top: 1px solid #d3ced2; + box-shadow: none; + margin-top: -1px; +} + +.woocommerce .elementor-product-price-block-yes.elementor-widget-woocommerce-product-price .price del, +.woocommerce .elementor-product-price-block-yes.elementor-widget-woocommerce-product-price .price ins { + display: block; +} + +.elementor-widget-woocommerce-product-meta .detail-container { + position: relative; +} +.elementor-widget-woocommerce-product-meta .detail-container:after { + position: absolute; + bottom: 0; + left: 0; + width: 100%; +} +.elementor-widget-woocommerce-product-meta .detail-label { + font-weight: bold; +} + +.elementor-woo-meta--view-inline .product_meta { + display: flex; + flex-wrap: wrap; +} +.elementor-woo-meta--view-inline .product_meta .detail-container:after { + width: auto; + left: auto; + right: auto; + position: absolute; + height: 100%; + top: 50%; + transform: translateY(-50%); + border-top: 0; + border-bottom: 0; + border-right: 0; + border-left-width: 1px; + border-style: solid; + right: -8px; +} +.elementor-woo-meta--view-table .product_meta { + display: flex; + flex-direction: column; +} +.elementor-woo-meta--view-table .product_meta .detail-container { + display: flex; +} +.elementor-woo-meta--view-table .product_meta .detail-label { + min-width: 108px; +} +.elementor-woo-meta--view-stacked .product_meta .detail-container { + display: block; +} + +.woocommerce .elementor-widget-woocommerce-product-rating .elementor-widget-container .woocommerce-product-rating { + margin-bottom: 0; + display: flex; + align-items: center; +} +.woocommerce .elementor-widget-woocommerce-product-rating .elementor-widget-container .star-rating { + margin-top: 0; +} + +.elementor-product-rating--align-left .woocommerce-product-rating { + justify-content: flex-start; +} +.elementor-product-rating--align-right .woocommerce-product-rating { + justify-content: flex-end; +} +.elementor-product-rating--align-center .woocommerce-product-rating { + justify-content: center; +} +.elementor-product-rating--align-justify .woocommerce-product-rating .woocommerce-review-link { + margin-left: auto; +} + +.elementor-products-grid ul.products.elementor-grid { + display: grid; + margin: 0; + grid-column-gap: 20px; + grid-row-gap: 40px; +} +.elementor-products-grid ul.products.elementor-grid:before, .elementor-products-grid ul.products.elementor-grid:after { + content: none; + display: none; +} +.elementor-products-grid ul.products.elementor-grid li.product { + width: auto; + padding: 0; + margin: 0; + float: none; + display: var(--button-align-display); + flex-direction: var(--button-align-direction); + justify-content: var(--button-align-justify); +} +.elementor-products-grid ul.products.elementor-grid li.product .onsale { + padding: 0; + display: none; +} +.elementor-products-grid ul.products.elementor-grid li.product a.woocommerce-loop-product__link { + display: block; + position: relative; +} + +.elementor-products-grid:not(.show-heading-yes) .products > h2 { + display: none; +} +.elementor-products-grid nav.woocommerce-pagination { + margin-top: 40px; +} +.elementor-products-grid:not(.elementor-show-pagination-border-yes) nav.woocommerce-pagination ul { + border: none 0; +} +.elementor-products-grid:not(.elementor-show-pagination-border-yes) nav.woocommerce-pagination ul li { + border-right: none 0; + border-left: none 0; +} + +.elementor-widget-woocommerce-products:not(.products-heading-show) .related > h2, +.elementor-widget-woocommerce-products:not(.products-heading-show) .upsells > h2, +.elementor-widget-woocommerce-products:not(.products-heading-show) .cross-sells > h2 { + display: none; +} +.elementor-widget-woocommerce-products.products-heading-show .related > h2, +.elementor-widget-woocommerce-products.products-heading-show .upsells > h2, +.elementor-widget-woocommerce-products.products-heading-show .cross-sells > h2 { + display: block; + text-align: var(--products-title-alignment, left); + color: var(--products-title-color); + margin-bottom: var(--products-title-spacing, 1rem); +} + +.elementor-product-loop-item--align-left ul.products li.product .star-rating { + margin-right: auto; +} +.elementor-product-loop-item--align-right ul.products li.product .star-rating { + margin-left: auto; +} +.elementor-product-loop-item--align-center ul.products li.product .star-rating { + margin-right: auto; + margin-left: auto; +} + +.woocommerce .elementor-element.elementor-products-grid ul.products li.product { + width: auto; +} +.woocommerce div.product .elementor-element.elementor-products-grid .related.products ul.products li.product, +.woocommerce div.product .elementor-element.elementor-products-grid .upsells.products ul.products li.product { + width: auto; +} + +@media (min-width: 1025px) { + .elementor-widget-wc-archive-products .woocommerce.columns-2 ul.products { + grid-template-columns: repeat(2, 1fr); + } + .elementor-widget-wc-archive-products .woocommerce.columns-3 ul.products { + grid-template-columns: repeat(3, 1fr); + } + .elementor-widget-wc-archive-products .woocommerce.columns-4 ul.products { + grid-template-columns: repeat(4, 1fr); + } + .elementor-widget-wc-archive-products .woocommerce.columns-5 ul.products { + grid-template-columns: repeat(5, 1fr); + } + .elementor-widget-wc-archive-products .woocommerce.columns-6 ul.products { + grid-template-columns: repeat(6, 1fr); + } + .elementor-widget-wc-archive-products .woocommerce.columns-7 ul.products { + grid-template-columns: repeat(7, 1fr); + } + .elementor-widget-wc-archive-products .woocommerce.columns-8 ul.products { + grid-template-columns: repeat(8, 1fr); + } + .elementor-widget-wc-archive-products .woocommerce.columns-9 ul.products { + grid-template-columns: repeat(9, 1fr); + } + .elementor-widget-wc-archive-products .woocommerce.columns-10 ul.products { + grid-template-columns: repeat(10, 1fr); + } + .elementor-widget-wc-archive-products .woocommerce.columns-11 ul.products { + grid-template-columns: repeat(11, 1fr); + } + .elementor-widget-wc-archive-products .woocommerce.columns-12 ul.products { + grid-template-columns: repeat(12, 1fr); + } +} +@media (max-width: 1024px) { + .elementor-widget-wc-archive-products .products { + grid-template-columns: repeat(3, 1fr); + } +} +@media (max-width: 767px) { + .elementor-widget-wc-archive-products .products { + grid-template-columns: repeat(2, 1fr); + } +} + +.elementor.product .woocommerce-product-gallery__trigger + .woocommerce-product-gallery__wrapper { + overflow: hidden; +} + +.woocommerce .elementor-widget-woocommerce-product-images span.onsale { + padding: 0; +} + +.elementor-menu-cart__wrapper { + text-align: var(--main-alignment, left); +} +.elementor-menu-cart__toggle_wrapper { + display: inline-block; + position: relative; +} +.elementor-menu-cart__toggle { + display: inline-block; +} +.elementor-menu-cart__toggle .elementor-button { + background-color: var(--toggle-button-background-color, transparent); + color: var(--toggle-button-text-color, #69727D); + border-style: var(--toggle-button-border-type, solid); + border-width: var(--toggle-button-border-width, 1px); + border-color: var(--toggle-button-border-color, #69727D); + border-radius: var(--toggle-button-border-radius, 0px); + display: inline-flex; + flex-direction: row-reverse; + align-items: center; + padding: var(--toggle-icon-padding, 12px 24px); +} +.elementor-menu-cart__toggle .elementor-button:hover { + color: var(--toggle-button-hover-text-color, #69727D); + background-color: var(--toggle-button-hover-background-color, transparent); + border-color: var(--toggle-button-hover-border-color, #69727D); +} +.elementor-menu-cart__toggle .elementor-button:hover .elementor-button-icon { + color: var(--toggle-button-icon-hover-color, #69727D); +} +.elementor-menu-cart__toggle .elementor-button:hover svg { + fill: var(--toggle-button-icon-hover-color, #69727D); +} +.elementor-menu-cart__toggle .elementor-button svg { + fill: var(--toggle-button-icon-color, #69727D); +} +.elementor-menu-cart__toggle .elementor-button-text { + margin-inline-end: 0.3em; +} +.elementor-menu-cart__toggle .elementor-button-icon { + position: relative; + transition: color 0.1s; + color: var(--toggle-button-icon-color, #69727D); + font-size: var(--toggle-icon-size, inherit); +} +.elementor-menu-cart__toggle .e-toggle-cart-custom-icon { + color: var(--toggle-button-icon-color, #69727D); + font-size: var(--toggle-icon-size, inherit); +} +.elementor-menu-cart__toggle .e-toggle-cart-custom-icon:hover { + color: var(--toggle-button-icon-hover-color, #69727D); +} +.elementor-menu-cart--items-indicator-bubble .elementor-menu-cart__toggle .elementor-button-icon .elementor-button-icon-qty[data-counter] { + display: block; + position: absolute; + min-width: 1.6em; + height: 1.6em; + line-height: 1.5em; + top: -0.7em; + inset-inline-end: -0.7em; + border-radius: 100%; + color: var(--items-indicator-text-color, #fff); + background-color: var(--items-indicator-background-color, #d9534f); + text-align: center; + font-size: 10px; +} +.elementor-menu-cart--items-indicator-plain .elementor-menu-cart__toggle .elementor-button-icon .elementor-button-icon-qty[data-counter] { + display: inline-block; + font-weight: normal; +} +.elementor-menu-cart--items-indicator-none .elementor-menu-cart__toggle .elementor-button-icon .elementor-button-icon-qty[data-counter] { + display: none; +} +.elementor-menu-cart__container { + transform: scale(1); + overflow: hidden; + position: fixed; + z-index: 9998; + top: 0; + left: 0; + width: 100vw; + height: 100%; + background-color: rgba(0, 0, 0, 0.25); + transition: background-color 0.4s, transform 0s; + text-align: left; +} +.elementor-menu-cart__main { + position: fixed; + left: var(--side-cart-alignment-left, auto); + right: var(--side-cart-alignment-right, 0); + transform: translateX(0); + top: 0; + bottom: 0; + display: flex; + flex-direction: column; + align-items: stretch; + font-size: 14px; + min-height: 200px; + width: 350px; + max-width: 100%; + transition: 0.3s; + padding: var(--cart-padding, 20px 30px); + background-color: var(--cart-background-color, #fff); + box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); + border-style: var(--cart-border-style, none); + border-color: var(--cart-border-color, initial); + border-radius: var(--cart-border-radius, 0px); + margin-top: var(--mini-cart-spacing, 0px); +} +.elementor-menu-cart__main .widget_shopping_cart_content { + height: 100%; + display: flex; + flex-direction: column; +} +.elementor-menu-cart__main .widget_shopping_cart_content .woocommerce-mini-cart__empty-message { + color: var(--empty-message-color, inherit); + text-align: var(--empty-message-alignment, left); +} +body.elementor-default .elementor-widget-woocommerce-menu-cart:not(.elementor-menu-cart--shown) .elementor-menu-cart__container { + background-color: transparent; + transform: scale(0); + transition: background-color 0.4s, transform 0s 0.4s; +} +body.elementor-default .elementor-widget-woocommerce-menu-cart:not(.elementor-menu-cart--shown) .elementor-menu-cart__container .dialog-lightbox-close-button { + display: none; +} +body.elementor-default .elementor-widget-woocommerce-menu-cart:not(.elementor-menu-cart--shown) .elementor-menu-cart__main { + overflow: hidden; + opacity: 0; + transform: var(--side-cart-alignment-transform, translateX(100%)); +} +.elementor-menu-cart__close-button { + width: var(--cart-close-icon-size, 25px); + height: var(--cart-close-icon-size, 25px); + position: relative; + margin: 0 0 20px; + align-self: flex-end; + cursor: pointer; + display: inline-block; + font-family: eicons; + font-size: 20px; + line-height: 1; + transition: 0.3s; +} +.elementor-menu-cart__close-button:before, .elementor-menu-cart__close-button:after { + content: ""; + position: absolute; + height: 3px; + width: 100%; + top: 50%; + left: 0; + margin-top: -1px; + background: var(--cart-close-button-color, #69727D); + border-radius: 1px; + transition: 0.3s; +} +.elementor-menu-cart__close-button:hover::before, .elementor-menu-cart__close-button:hover::after { + background: var(--cart-close-button-hover-color, #69727D); +} +.elementor-menu-cart__close-button::before { + transform: rotate(45deg); +} +.elementor-menu-cart__close-button::after { + transform: rotate(-45deg); +} +.elementor-menu-cart__close-button-custom { + position: relative; + margin: 0 0 20px; + align-self: flex-end; + cursor: pointer; + display: inline-block; + font-family: eicons; + font-size: 20px; + line-height: 1; + transition: 0.3s; +} +.elementor-menu-cart__close-button-custom:hover::before, .elementor-menu-cart__close-button-custom:hover::after { + background: var(--cart-close-button-hover-color, #69727D); +} +.elementor-menu-cart__close-button-custom .e-close-cart-custom-icon { + font-size: var(--cart-close-icon-size, 25px); + color: var(--cart-close-button-color, #69727D); +} +.elementor-menu-cart__close-button-custom .e-close-cart-custom-icon:hover { + color: var(--cart-close-button-hover-color, #69727D); +} +.elementor-menu-cart__close-button-custom svg { + fill: var(--cart-close-button-color, #69727D); + width: var(--cart-close-icon-size, 25px); + height: var(--cart-close-icon-size, 25px); +} +.elementor-menu-cart__close-button-custom svg:hover { + fill: var(--cart-close-button-hover-color, #69727D); +} +.elementor-menu-cart__products { + max-height: calc(100vh - 250px); + overflow: hidden; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} +.elementor-menu-cart__product { + display: grid; + grid-template-columns: 28% auto; + grid-template-rows: var(--price-quantity-position--grid-template-rows, auto auto); + position: relative; + border-width: 0 0 var(--divider-width, 1px); + border-bottom-style: var(--divider-style, solid); + border-bottom-color: var(--divider-color, #D5D8DC); +} +.elementor-menu-cart__product .variation { + display: grid; + grid-template-columns: max-content auto; + margin: 10px 0; + color: var(--product-variations-color, #1f2124); +} +.elementor-menu-cart__product .variation dt { + grid-column-start: 1; +} +.elementor-menu-cart__product .variation dd { + grid-column-start: 2; + margin-inline-start: 5px; +} +.elementor-menu-cart__product .variation dd p { + margin-bottom: 0; +} +.elementor-menu-cart__product-image { + grid-row-start: 1; + grid-row-end: 3; + width: 100%; +} +.elementor-menu-cart__product-image img, .elementor-menu-cart__product-image a { + display: block; +} +.elementor-menu-cart__product-name { + grid-column-start: 2; + grid-column-end: 3; + margin: 0; +} +.elementor-menu-cart__product-name a { + transition: 0.3s; +} +.elementor-menu-cart__product-price { + grid-column-start: 2; + grid-column-end: 3; + align-self: var(--price-quantity-position--align-self, end); + font-weight: 300; + color: var(--product-price-color, #D5D8DC); +} +.elementor-menu-cart__product-name, .elementor-menu-cart__product-price { + font-size: 14px; + padding-left: 20px; +} +.elementor-menu-cart__product-remove { + color: #69727D; + width: var(--remove-item-button-size, 22px); + height: var(--remove-item-button-size, 22px); + border-radius: var(--remove-item-button-size, 22px); + border: 1px solid var(--remove-item-button-color, #D5D8DC); + text-align: center; + overflow: hidden; + position: absolute; + right: 0; + bottom: 20px; + transition: 0.3s; +} +.elementor-menu-cart__product-remove:hover { + border: 1px solid var(--remove-item-button-hover-color, #D5D8DC); +} +.elementor-menu-cart__product-remove:hover::before, .elementor-menu-cart__product-remove:hover::after { + background: var(--remove-item-button-hover-color, #D5D8DC); +} +.elementor-menu-cart__product-remove:before, .elementor-menu-cart__product-remove:after { + content: ""; + position: absolute; + height: 1px; + width: 50%; + top: 50%; + left: 25%; + margin-top: -1px; + background: var(--remove-item-button-color, #D5D8DC); + z-index: 1; + transition: 0.3s; +} +.elementor-menu-cart__product-remove::before { + transform: rotate(45deg); +} +.elementor-menu-cart__product-remove::after { + transform: rotate(-45deg); +} +.elementor-menu-cart__product-remove > a { + display: block; + z-index: 2; + width: 100%; + height: 100%; + overflow: hidden; + opacity: 0; + position: absolute; +} +.elementor-menu-cart__product-remove > a.remove_from_cart_button { + display: var(--remove-from-cart-button, block); +} +.elementor-menu-cart__product-remove > a.elementor_remove_from_cart_button { + display: var(--elementor-remove-from-cart-button, none); +} +.elementor-menu-cart__product:last-child { + border: none; +} +.elementor-menu-cart__product:not(:first-of-type), .elementor-menu-cart__subtotal, .elementor-menu-cart__footer-buttons { + padding-top: var(--product-divider-gap, 20px); +} +.elementor-menu-cart__product { + padding-right: 30px; +} +.elementor-menu-cart__product, .elementor-menu-cart__subtotal { + padding-bottom: var(--product-divider-gap, 20px); +} +.elementor-menu-cart__subtotal { + font-size: 20px; + text-align: var(--menu-cart-subtotal-text-align, center); + font-weight: 600; + color: var(--menu-cart-subtotal-color, inherit); + border-style: var(--subtotal-divider-style, solid); + border-width: var(--subtotal-divider-top-width, 1px) var(--subtotal-divider-right-width, 1px) var(--subtotal-divider-bottom-width, 1px) var(--subtotal-divider-left-width, 1px); + border-color: var(--subtotal-divider-color, #D5D8DC); +} +.elementor-menu-cart__footer-buttons { + font-size: 20px; + text-align: var(--cart-footer-buttons-alignment-text-align, center); + display: var(--cart-footer-buttons-alignment-display, grid); + grid-template-columns: var(--cart-footer-layout, 1fr 1fr); + margin-top: var(--cart-buttons-position-margin, 0); + grid-column-gap: var(--space-between-buttons, 10px); + grid-row-gap: var(--space-between-buttons, 10px); +} +.elementor-menu-cart__footer-buttons .elementor-button { + display: inline-block; + border-radius: var(--cart-footer-buttons-border-radius, 0px); + height: -moz-fit-content; + height: fit-content; +} +.elementor-menu-cart__footer-buttons .elementor-button--view-cart { + display: var(--view-cart-button-display, inline-block); + color: var(--view-cart-button-text-color, #fff); + padding: var(--view-cart-button-padding, 15px); + background-color: var(--view-cart-button-background-color, #69727D); +} +.elementor-menu-cart__footer-buttons .elementor-button--view-cart:hover { + color: var(--view-cart-button-hover-text-color, #fff); + background-color: var(--view-cart-button-hover-background-color, #69727D); +} +.elementor-menu-cart__footer-buttons .elementor-button--checkout { + display: var(--checkout-button-display, inline-block); + color: var(--checkout-button-text-color, #fff); + padding: var(--checkout-button-padding, 15px); + background-color: var(--checkout-button-background-color, #69727D); +} +.elementor-menu-cart__footer-buttons .elementor-button--checkout:hover { + color: var(--checkout-button-hover-text-color, #fff); + background-color: var(--checkout-button-hover-background-color, #69727D); +} +@media (max-width: 767px) { + .elementor-menu-cart__footer-buttons .elementor-button { + padding-left: 10px; + padding-right: 10px; + } +} + +/* The following is all to apply settings from the controls */ +.elementor-widget-woocommerce-menu-cart { + /* Fix to prevent the cart modal flash when using the typography controls */ + /* Fix for short-lived legacy cart type */ +} +.elementor-widget-woocommerce-menu-cart:not(.elementor-menu-cart--show-subtotal-yes) .elementor-menu-cart__toggle .elementor-button-text { + display: none; +} +.elementor-widget-woocommerce-menu-cart.elementor-menu-cart--empty-indicator-hide .elementor-menu-cart__toggle .elementor-button-icon .elementor-button-icon-qty[data-counter="0"] { + display: none; +} +.elementor-widget-woocommerce-menu-cart:not(.elementor-menu-cart--show-remove-button-yes) .elementor-menu-cart__product { + padding-left: 0; + padding-right: 0; + grid-template-columns: 25% auto; +} +.elementor-widget-woocommerce-menu-cart:not(.elementor-menu-cart--show-remove-button-yes) .elementor-menu-cart__product-remove { + display: none; +} +.elementor-widget-woocommerce-menu-cart.remove-item-position--top .elementor-menu-cart__product-remove { + top: 0; + bottom: auto; +} +.elementor-widget-woocommerce-menu-cart.remove-item-position--top .elementor-menu-cart__products .cart_item:not(:first-of-type) .elementor-menu-cart__product-remove { + top: 20px; + bottom: auto; +} +.elementor-widget-woocommerce-menu-cart.remove-item-position--middle .elementor-menu-cart__product-remove { + transform: translateY(50%); + bottom: 50%; +} +.elementor-widget-woocommerce-menu-cart.remove-item-position--bottom .elementor-menu-cart__product-remove { + top: auto; + bottom: 20px; +} +.elementor-widget-woocommerce-menu-cart.elementor-menu-cart--cart-type-mini-cart .elementor-menu-cart__container { + position: absolute; + width: auto; + height: auto; + overflow: visible; + top: 100%; + bottom: auto; + background: none; + min-width: 330px; + left: 0; + right: auto; + transform: scale(1); + transition: background-color 0.4s, transform 0s; +} +.elementor-widget-woocommerce-menu-cart.elementor-menu-cart--cart-type-mini-cart .elementor-menu-cart__main { + width: auto; + height: auto; + position: relative; + top: auto; + bottom: auto; + right: auto; + left: auto; + overflow: visible; + transform: translateY(0); + transition: 0.3s; +} +@media (max-width: 767px) { + .elementor-widget-woocommerce-menu-cart.elementor-menu-cart--cart-type-mini-cart .elementor-menu-cart__container { + min-width: 300px; + } +} +body.elementor-default .elementor-widget-woocommerce-menu-cart.elementor-menu-cart--cart-type-mini-cart:not(.elementor-menu-cart--shown) .elementor-menu-cart__container { + transform: scale(0); + transition: background-color 0.4s, transform 0s 0.4s; +} +body.elementor-default .elementor-widget-woocommerce-menu-cart.elementor-menu-cart--cart-type-mini-cart:not(.elementor-menu-cart--shown) .elementor-menu-cart__main { + opacity: 0; + transform: translateY(-10px); +} +.elementor-edit-area-active .elementor-widget-woocommerce-menu-cart.elementor-widget.elementor-loading.elementor-menu-cart--shown { + opacity: 1; + /** + * This keeps side-cart and mini-cart widget above other widgets + * so there's no flash when using the typography controls + */ +} +.elementor-edit-area-active .elementor-widget-woocommerce-menu-cart.elementor-widget.elementor-loading.elementor-menu-cart--shown .elementor-menu-cart__container { + z-index: 9999; +} +.elementor-widget-woocommerce-menu-cart.elementor-menu-cart--cart-type-dropdown .elementor-menu-cart__container { + display: none; +} + +.elementor-widget-woocommerce-purchase-summary { + font-size: 14px; + font-family: Roboto, sans-serif; + color: #69727D; + /* override theme defaults for themes that have hover background colors on these elements. The hover background colors do not play well with the widget's initial design. */ +} +.elementor-widget-woocommerce-purchase-summary table tbody tr:hover > td, .elementor-widget-woocommerce-purchase-summary table tbody tr:hover > th { + background-color: initial; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-thankyou-order-details { + padding-inline-start: 0; + margin: 0 0 2em; + display: flex; + flex-wrap: wrap; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-thankyou-order-details li { + border-right-style: var(--payment-details-border-type, solid); + border-right-color: var(--payment-details-border-color, #D5D8DC); + border-right-width: var(--payment-details-border-width, 1px); + color: var(--payment-details-titles-color, #000000); + font-weight: 700; + font-size: 14px; + text-transform: capitalize; + margin-right: var(--payment-details-space-between, 4em); + padding-right: var(--payment-details-space-between, 4em); + float: unset; + margin-bottom: 30px; + /* By default, WooCommerce styles the order data as Bold, and the 'Headings' as normal text. In Elementor's default design, the headings are bold, and the `` element which contains the order data, needs to be reset to look like normal text. */ +} +@media (max-width: 1024px) { + .elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-thankyou-order-details li { + border-right: none; + border-bottom-width: var(--payment-details-border-width, 1px); + border-bottom-style: var(--payment-details-border-type, solid); + border-bottom-color: var(--payment-details-border-color, #D5D8DC); + margin-right: 0; + padding-right: 0; + width: 100%; + padding-top: calc(var(--payment-details-space-between, 20px) / 2); + padding-bottom: calc(var(--payment-details-space-between, 20px) / 2); + justify-content: space-between; + display: flex; + margin-bottom: 0; + } +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-thankyou-order-details li strong { + color: var(--payment-details-items-color, #69727D); + font-weight: 400; + font-size: 14px; + margin-top: var(--payment-details-titles-spacing, 10px); + text-transform: none; + line-height: initial; + text-shadow: none; + font-style: normal; + letter-spacing: 0px; +} +@media (max-width: 1024px) { + .elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-thankyou-order-details li strong { + margin-top: 0; + } +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-bacs-bank-details .wc-bacs-bank-details { + padding-inline-start: 0; + display: flex; + flex-wrap: wrap; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-bacs-bank-details .wc-bacs-bank-details li { + border-right-style: var(--bank-details-border-type, solid); + border-right-color: var(--bank-details-border-color, #D5D8DC); + border-right-width: var(--bank-details-border-width, 1px); + color: var(--bank-details-titles-color, #000000); + font-weight: 700; + font-size: 14px; + text-transform: capitalize; + margin-right: var(--bank-details-space-between, 4em); + padding-right: var(--bank-details-space-between, 4em); + float: unset; + margin-bottom: 30px; + /* By default, WooCommerce styles the order data as Bold, and the 'Headings' as normal text. In Elementor's default design, the headings are bold, and the `` element which contains the order data, needs to be reset to look like normal text. */ +} +@media (max-width: 1024px) { + .elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-bacs-bank-details .wc-bacs-bank-details li { + border-right: none; + border-bottom-width: var(--bank-details-border-width, 1px); + border-bottom-style: var(--bank-details-border-type, solid); + border-bottom-color: var(--bank-details-border-color, #D5D8DC); + margin-right: 0; + padding-right: 0; + width: 100%; + padding-top: calc(var(--bank-details-space-between, 20px) / 2); + padding-bottom: calc(var(--bank-details-space-between, 20px) / 2); + justify-content: space-between; + display: flex; + margin-bottom: 0; + } +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-bacs-bank-details .wc-bacs-bank-details li strong { + color: var(--bank-details-items-color, #69727D); + font-weight: 400; + font-size: 14px; + margin-top: var(--bank-details-titles-spacing, 10px); + text-transform: none; + line-height: initial; + text-shadow: none; + font-style: normal; + letter-spacing: 0px; +} +@media (max-width: 1024px) { + .elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-bacs-bank-details .wc-bacs-bank-details li strong { + margin-top: 0; + } +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-bacs-bank-details .wc-bacs-bank-details li:last-of-type { + border-right: none; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .wc-item-meta li p, +.elementor-widget-woocommerce-purchase-summary .woocommerce .wc-item-meta .wc-item-meta-label { + color: var(--order-details-variations-color, #69727D); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table { + font-size: 14px; + margin-bottom: 0; + padding: var(--sections-padding, 15px 30px); + background-color: var(--sections-background-color, #ffffff); + border-style: var(--sections-border-type, solid); + border-color: var(--sections-border-color, #D5D8DC); + border-radius: var(--sections-border-radius, 3px); + border-width: 1px; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table .button.alt { + background-color: transparent; + border-style: var(--buttons-border-type, solid); + border-color: var(--buttons-border-color, #5bc0de); + border-radius: var(--button-border-radius, 3px); + border-width: 2px; + vertical-align: middle; + color: var(--button-normal-text-color, #69727D); + padding: var(--button-padding, 5px 10px); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table .button.alt:hover { + color: var(--button-hover-text-color, #69727D); + transition-duration: var(--button-hover-transition-duration, 0.3s); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table td, +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table th { + border: 0px; + border-top-style: var(--tables-divider-border-type, solid); + border-top-width: var(--tables-divider-border-width, 1px); + border-top-color: var(--tables-divider-border-color, #D5D8DC); + padding-top: calc(var(--order-details-rows-gap, 18px) / 2); + padding-bottom: calc(var(--order-details-rows-gap, 18px) / 2); + padding-left: 0; + padding-right: 0; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table thead tr th { + color: var(--order-details-titles-totals-color, #000000); + border-top: none; + padding-top: 0; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table thead tr th span { + color: var(--order-details-titles-totals-color, #000000); +} +@media (min-width: 1025px) { + .elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table tbody td { + vertical-align: top; + line-height: unset; + } +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table tbody td .woocommerce-Price-amount { + color: var(--order-details-items-color, #69727D); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table tfoot th, +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table tfoot td { + color: var(--order-details-titles-totals-color, #000000); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table tfoot tr:last-child th, +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table tfoot tr:last-child td { + padding-bottom: 0; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table .product-quantity, +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table td.download-remaining, +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table td.download-expires { + font-weight: 400; + color: var(--order-details-items-color, #69727D); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .shop_table .product-purchase-note td { + border-top: none; + padding-top: 0; + color: var(--general-text-color, #69727D); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-table--order-downloads tr td:before { + color: var(--order-details-titles-totals-color, #000000); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .order-again .button { + background: transparent; + border: 2px solid #5bc0de; + border-style: var(--buttons-border-type, solid); + border-color: var(--buttons-border-color, #5bc0de); + border-width: 2px; + border-radius: var(--button-border-radius, 3px); + vertical-align: middle; + color: var(--button-normal-text-color, #69727D); + margin-top: 40px; + margin-bottom: 0; + padding: var(--button-padding, 12px 32px); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .order-again .button:hover { + color: var(--button-hover-text-color, #69727D); + transition-duration: var(--button-hover-transition-duration, 0.3s); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce h2 { + color: var(--titles-color, #000000); + margin-bottom: var(--titles-spacing, 45px); + font-weight: 400; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-order-details h2 { + text-align: var(--order-summary-alignment, inherit); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .wc-bacs-bank-details-heading { + text-align: var(--bank-details-alignment, inherit); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-order-downloads__title { + text-align: var(--downloads-alignment, inherit); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-customer-details > h2, +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-column--billing-address h2 { + text-align: var(--billing-details-alignment, inherit); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-column--shipping-address h2 { + text-align: var(--shipping-details-alignment, inherit); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce a { + color: var(--order-details-product-links-normal-color, #5bc0de); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce a:hover { + color: var(--order-details-product-links-hover-color, #5bc0de); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce p { + margin-bottom: 20px; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-thankyou-order-received { + margin-bottom: var(--sections-spacing, 40px); + color: var(--confirmation-message-color, #69727D); + text-align: var(--confirmation-message-alignment, inherit); + display: var(--confirmation-message-display, none); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce strong { + color: var(--general-text-color, #000000); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce table tbody > tr:nth-child(odd) > td, +.elementor-widget-woocommerce-purchase-summary .woocommerce table tbody > tr:nth-child(odd) > th { + background-color: transparent; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce address { + padding: var(--sections-padding, 15px 30px); + background-color: var(--sections-background-color, #ffffff); + border-style: var(--sections-border-type, solid); + border-color: var(--sections-border-color, #D5D8DC); + border-radius: var(--sections-border-radius, 3px); + border-width: 1px; + color: var(--general-text-color, #69727D); +} +@media (max-width: 767px) { + .elementor-widget-woocommerce-purchase-summary .woocommerce-column--2 { + margin-top: 2em; + } +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .woocommerce-thankyou-order-details + p { + color: var(--general-text-color, #69727D); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .wc-bacs-bank-details-account-name { + color: var(--account-title-color, #000000); + font-weight: 700; + font-size: 14px; + margin-bottom: var(--account-title-spacing, 1rem); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce section { + margin-top: var(--sections-spacing, 4em); +} +.elementor-widget-woocommerce-purchase-summary .woocommerce .wc-bacs-bank-details:last-child { + margin-bottom: 0; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce-table__line-item.order_item .woocommerce-table__product-name.product-purchase-note-is-below { + padding-bottom: 0; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce-table__line-item.order_item .woocommerce-table__product-total.product-total.product-purchase-note-is-below { + padding-bottom: 0; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce-table--order-details { + table-layout: fixed; +} +.elementor-widget-woocommerce-purchase-summary .woocommerce-table--order-details td { + word-wrap: break-word; +} + +/* Select2 dropdown options are outside of the widget container */ +.e-woo-select2-wrapper .select2-results__option { + font-family: Roboto, sans-serif; + font-size: 14px; + color: #69727D; +} +.e-woo-select2-wrapper .select2-results__option:focus { + color: #69727D; +} + +.elementor-widget-woocommerce-checkout-page { + background-color: transparent; + font-family: Roboto, sans-serif; + font-size: 14px; + line-height: 21px; + color: #69727D; + /* one column layout */ + /* override theme defaults for themes that have hover background colors on these elements. The hover background colors do not play well with the widget's initial design. */ + /* Make select2 appear like other inputs start */ + /* Make select2 appear like other inputs end */ +} +.elementor-widget-woocommerce-checkout-page.e-checkout-layout-one-column .e-checkout__container { + grid-template-columns: auto; +} +.elementor-widget-woocommerce-checkout-page ::-moz-placeholder { + color: var(--forms-fields-normal-color, inherit); + font-family: inherit; + opacity: 0.6; +} +.elementor-widget-woocommerce-checkout-page ::placeholder { + color: var(--forms-fields-normal-color, inherit); + font-family: inherit; + opacity: 0.6; +} +.elementor-widget-woocommerce-checkout-page table tbody tr:hover > td, .elementor-widget-woocommerce-checkout-page table tbody tr:hover > th { + background-color: transparent; +} +.elementor-widget-woocommerce-checkout-page .select2-container--default .select2-selection--single { + color: var(--forms-fields-normal-color, #69727D); + background-color: #F9FAFA; + border-radius: var(--forms-fields-border-radius, 0px); + border: none; + height: 45px; +} +.elementor-widget-woocommerce-checkout-page .select2-container--default .select2-selection--single:focus { + color: var(--forms-fields-focus-color, #69727D); + background-color: #F9FAFA; + border-color: initial; + transition-duration: var(--forms-fields-focus-transition-duration, 0.3s); +} +.elementor-widget-woocommerce-checkout-page .select2-container--default .select2-selection--single .select2-selection__placeholder { + color: var(--forms-fields-normal-color, #69727D); +} +.elementor-widget-woocommerce-checkout-page .select2-container--default .select2-selection--single .select2-selection__rendered { + color: var(--forms-fields-normal-color, #69727D); + line-height: 45px; + padding-left: 1rem; + padding-right: 1rem; +} +.elementor-widget-woocommerce-checkout-page .select2-container--default .select2-selection--single .select2-selection__arrow { + height: 45px; +} +.elementor-widget-woocommerce-checkout-page .select2-container--open .select2-dropdown--below { + background-color: #F9FAFA; +} +.elementor-widget-woocommerce-checkout-page .e-description { + color: var(--sections-descriptions-color, #69727D); + padding-bottom: var(--sections-descriptions-spacing, 0px); + font-size: 14px; + font-weight: 400; +} +.elementor-widget-woocommerce-checkout-page .e-woocommerce-login-section { + margin-bottom: 24px; +} +.elementor-widget-woocommerce-checkout-page .e-woocommerce-login-section .e-checkout-secondary-title { + text-align: var(--login-title-alignment, left); +} +.elementor-widget-woocommerce-checkout-page .e-woocommerce-login-nudge { + margin-top: var(--sections-secondary-title-spacing, 24px); + margin-bottom: 15px; +} +.elementor-widget-woocommerce-checkout-page .e-coupon-anchor { + margin-top: var(--sections-secondary-title-spacing, 24px); +} +.elementor-widget-woocommerce-checkout-page .e-coupon-box { + margin-top: 24px; +} +.elementor-widget-woocommerce-checkout-page .e-coupon-anchor-description { + color: var(--forms-labels-color, #69727D); + margin-bottom: var(--forms-label-spacing, 3px); +} +.elementor-widget-woocommerce-checkout-page .e-login-wrap { + display: flex; + align-items: center; +} +.elementor-widget-woocommerce-checkout-page .e-login-wrap-start { + flex: 75%; +} +.elementor-widget-woocommerce-checkout-page .e-login-wrap-end { + flex: 20%; + text-align: right; +} +@media (max-width: 1024px) { + .elementor-widget-woocommerce-checkout-page .e-login-wrap { + display: block; + } + .elementor-widget-woocommerce-checkout-page .e-login-wrap-end { + text-align: var(--login-button-alignment, left); + margin-top: 15px; + } + .elementor-widget-woocommerce-checkout-page .e-login-wrap-end label.e-login-label { + display: none; + } +} +.elementor-widget-woocommerce-checkout-page .e-login-actions-wrap { + display: flex; + justify-content: space-between; + margin-top: 6px; +} +.elementor-widget-woocommerce-checkout-page .e-login-actions-wrap-end .lost_password { + margin-bottom: 0; + font-size: 12px; +} +@media (max-width: 1024px) { + .elementor-widget-woocommerce-checkout-page .e-login-actions-wrap-end .lost_password { + font-size: 11px; + } +} +.elementor-widget-woocommerce-checkout-page .e-apply-coupon { + width: 90%; /* 90% to match the design */ +} +@media (max-width: 1024px) { + .elementor-widget-woocommerce-checkout-page .e-apply-coupon { + width: var(--coupon-button-width, auto); + } +} +.elementor-widget-woocommerce-checkout-page .e-checkout__container { + display: grid; + flex-wrap: wrap; + grid-template-columns: 56% auto; /* column widths - to match the design */ + align-items: stretch; + grid-column-gap: var(--sections-margin, 24px); + grid-row-gap: var(--sections-margin, 24px); +} +@media (max-width: 1024px) { + .elementor-widget-woocommerce-checkout-page .e-checkout__container { + grid-template-columns: repeat(1, 1fr); + } +} +.elementor-widget-woocommerce-checkout-page .e-checkout-secondary-title { + color: var(--sections-secondary-title-color, #69727D); + margin-bottom: 0; +} +.elementor-widget-woocommerce-checkout-page .e-woocommerce-coupon-nudge { + text-align: var(--coupon-title-alignment, left); +} +.elementor-widget-woocommerce-checkout-page #ship-to-different-address { + margin-top: 13px; + padding-left: var(--shipping-heading-padding-start, 30px); +} +.elementor-widget-woocommerce-checkout-page #ship-to-different-address span { + font-weight: 400; +} +.elementor-widget-woocommerce-checkout-page a { + color: var(--links-normal-color, #5bc0de); +} +.elementor-widget-woocommerce-checkout-page a:hover { + color: var(--links-hover-color, #5bc0de); +} +.elementor-widget-woocommerce-checkout-page .woocommerce { + /* Need to use these specific selectors to override WooCommerce CSS */ + /* Need the `); + $buttonOpenAI.on('click', event => { + event.preventDefault(); + const isRTL = elementorCommon.config.isRTL; + const rootElement = document.createElement('div'); + document.body.append(rootElement); + + // eslint-disable-next-line react/no-deprecated + ReactDOM.render( /*#__PURE__*/_react.default.createElement(_elementorAiAdmin.default, { + type: 'code', + getControlValue: () => document.querySelector('.CodeMirror').CodeMirror.getValue(), + setControlValue: value => document.querySelector('.CodeMirror').CodeMirror.setValue(value), + additionalOptions: { + codeLanguage: 'html' + }, + onClose: () => { + ReactDOM.unmountComponentAtNode(rootElement); // eslint-disable-line react/no-deprecated + rootElement.parentNode.removeChild(rootElement); + }, + isRTL: isRTL + }), rootElement); + }); + jQuery('.elementor-field.location.elementor-field-select').after($buttonOpenAI); + } + setOptionsPlacementVisibility(state) { + const $optionsPlacement = jQuery('.elementor-custom-code-options-placement'); + $optionsPlacement.toggleClass('show', state); + } +} +exports["default"] = CustomCode; +elementorProAdmin.customCode = new CustomCode(); +})(); + +/******/ })() +; +//# sourceMappingURL=custom-code.js.map \ No newline at end of file diff --git a/assets/js/custom-code.min.js b/assets/js/custom-code.min.js new file mode 100644 index 0000000..53a5513 --- /dev/null +++ b/assets/js/custom-code.min.js @@ -0,0 +1,2 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +(()=>{var e={5896:(e,t,n)=>{"use strict";var o=n(7363);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.BaseContext=void 0;class BaseContext extends o.Component{constructor(e){super(e),this.state={action:{current:null,loading:!1,error:null,errorMeta:{}},updateActionState:this.updateActionState.bind(this),resetActionState:this.resetActionState.bind(this)}}executeAction(e,t){return this.updateActionState({current:e,loading:!0,error:null,errorMeta:{}}),t().then((e=>(this.resetActionState(),Promise.resolve(e)))).catch((t=>(this.updateActionState({current:e,loading:!1,error:t.message,errorMeta:t}),Promise.reject(t))))}updateActionState(e){return this.setState((t=>({action:{...t.action,...e}})))}resetActionState(){this.updateActionState({current:null,loading:!1,error:null,errorMeta:{}})}}t.BaseContext=BaseContext;t.default=BaseContext},6455:(e,t,n)=>{"use strict";var o=n(3615),i=n(8003).__,s=n(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Context=t.ConditionsProvider=void 0;var r=s(n(7363)),a=s(n(3180)),d=s(n(7163)),u=s(n(5896)),l=n(6338);const c=t.Context=r.default.createContext();class ConditionsProvider extends u.default{static propTypes={children:o.any.isRequired,currentTemplate:o.object.isRequired,onConditionsSaved:o.func.isRequired,validateConflicts:o.bool};static defaultProps={validateConflicts:!0};static actions={FETCH_CONFIG:"fetch-config",SAVE:"save",CHECK_CONFLICTS:"check-conflicts"};conditionsConfig=null;constructor(e){super(e),this.state={...this.state,conditions:{},updateConditionItemState:this.updateConditionItemState.bind(this),removeConditionItemInState:this.removeConditionItemInState.bind(this),createConditionItemInState:this.createConditionItemInState.bind(this),findConditionItemInState:this.findConditionItemInState.bind(this),saveConditions:this.saveConditions.bind(this)}}componentDidMount(){this.executeAction(ConditionsProvider.actions.FETCH_CONFIG,(()=>d.default.create())).then((e=>this.conditionsConfig=e)).then(this.normalizeConditionsState.bind(this)).then(this.setSubIdTitles.bind(this))}saveConditions(){const e=Object.values(this.state.conditions).map((e=>e.forDb()));return this.executeAction(ConditionsProvider.actions.SAVE,(()=>$e.data.update(l.TemplatesConditions.signature,{conditions:e},{id:this.props.currentTemplate.id}))).then((()=>{const e=Object.values(this.state.conditions).map((e=>e.forContext()));this.props.onConditionsSaved(this.props.currentTemplate.id,{conditions:e,instances:this.conditionsConfig.calculateInstances(Object.values(this.state.conditions)),isActive:!(!Object.keys(this.state.conditions).length||"publish"!==this.props.currentTemplate.status)})}))}checkConflicts(e){return this.executeAction(ConditionsProvider.actions.CHECK_CONFLICTS,(()=>$e.data.get(l.TemplatesConditionsConflicts.signature,{post_id:this.props.currentTemplate.id,condition:e.clone().toString()}))).then((t=>this.updateConditionItemState(e.id,{conflictErrors:Object.values(t.data)},!1)))}fetchSubIdsTitles(e){return new Promise((t=>elementorCommon.ajax.loadObjects({action:"query_control_value_titles",ids:_.isArray(e.subId)?e.subId:[e.subId],data:{get_titles:e.subIdAutocomplete,unique_id:elementorCommon.helpers.getUniqueId()},success(e){t(e)}})))}normalizeConditionsState(){this.updateConditionsState((()=>this.props.currentTemplate.conditions.reduce(((e,t)=>{const n=new a.default({...t,default:this.props.currentTemplate.defaultCondition,options:this.conditionsConfig.getOptions(),subOptions:this.conditionsConfig.getSubOptions(t.name),subIdAutocomplete:this.conditionsConfig.getSubIdAutocomplete(t.sub),supIdOptions:t.subId?[{value:t.subId,label:t.subId}]:[]});return{...e,[n.id]:n}}),{}))).then((()=>{Object.values(this.state.conditions).forEach((e=>this.checkConflicts(e)))}))}setSubIdTitles(){return Object.values(this.state.conditions).forEach((e=>{if(e.subId)return this.fetchSubIdsTitles(e).then((t=>this.updateConditionItemState(e.id,{subIdOptions:[{label:Object.values(t)[0],value:e.subId}]},!1)))}))}updateConditionItemState(e,t,n=!0){t.name&&(t.subOptions=this.conditionsConfig.getSubOptions(t.name)),(t.sub||t.name)&&(t.subIdAutocomplete=this.conditionsConfig.getSubIdAutocomplete(t.sub),t.subIdOptions=[]),this.updateConditionsState((n=>{const o=n[e];return{...n,[e]:o.clone().set(t)}})).then((()=>{n&&this.checkConflicts(this.findConditionItemInState(e))}))}removeConditionItemInState(e){this.updateConditionsState((t=>{const n={...t};return delete n[e],n}))}createConditionItemInState(e=!0){const t=this.props.currentTemplate.defaultCondition,n=new a.default({name:t,default:t,options:this.conditionsConfig.getOptions(),subOptions:this.conditionsConfig.getSubOptions(t),subIdAutocomplete:this.conditionsConfig.getSubIdAutocomplete("")});this.updateConditionsState((e=>({...e,[n.id]:n}))).then((()=>{e&&this.checkConflicts(n)}))}findConditionItemInState(e){return Object.values(this.state.conditions).find((t=>t.id===e))}updateConditionsState(e){return new Promise((t=>this.setState((t=>({conditions:e(t.conditions)})),t)))}render(){if(this.state.action.current===ConditionsProvider.actions.FETCH_CONFIG){if(this.state.error)return r.default.createElement("h3",null,i("Error:","elementor-pro")," ",this.state.error);if(this.state.loading)return r.default.createElement("h3",null,i("Loading","elementor-pro"),"...")}return r.default.createElement(c.Provider,{value:this.state},this.props.children)}}t.ConditionsProvider=ConditionsProvider;t.default=ConditionsProvider},3180:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;class Condition{id=elementorCommon.helpers.getUniqueId();default="";type="include";name="";sub="";subId="";options=[];subOptions=[];subIdAutocomplete=[];subIdOptions=[];conflictErrors=[];constructor(e){this.set(e)}set(e){return Object.assign(this,e),this}clone(){return Object.assign(new Condition,this)}remove(e){return Array.isArray(e)||(e=[e]),e.forEach((e=>{delete this[e]})),this}only(e){Array.isArray(e)||(e=[e]);const t=Object.keys(this).filter((t=>!e.includes(t)));return this.remove(t),this}toJson(){return JSON.stringify(this)}toString(){return this.forDb().filter((e=>e)).join("/")}forDb(){return[this.type,this.name,this.sub,this.subId]}forContext(){return{type:this.type,name:this.name,sub:this.sub,subId:this.subId}}}t.default=Condition},7163:(e,t,n)=>{"use strict";var o=n(8003).__;Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.ConditionsConfig=void 0;var i=n(6338);class ConditionsConfig{static instance;config=null;constructor(e){this.config=e}static create(){return ConditionsConfig.instance?Promise.resolve(ConditionsConfig.instance):$e.data.get(i.ConditionsConfig.signature,{},{refresh:!0}).then((e=>(ConditionsConfig.instance=new ConditionsConfig(e.data),ConditionsConfig.instance)))}getOptions(){return this.getSubOptions("general",!0).map((({label:e,value:t})=>({label:e,value:t})))}getSubOptions(e,t=!1){const n=this.config[e];return n?[{label:n.all_label,value:t?e:""},...n.sub_conditions.map((e=>{const t=this.config[e];return{label:t.label,value:e,children:t.sub_conditions.length?this.getSubOptions(e,!0):null}}))]:[]}getSubIdAutocomplete(e){const t=this.config[e];if(!t||"object"!=typeof t.controls)return{};const n=Object.values(t.controls);return n?.[0]?.autocomplete?n[0].autocomplete:{}}calculateInstances(e){let t=e.reduce(((e,t)=>{if("exclude"===t.type)return e;const n=t.sub||t.name,o=this.config[n];if(!o)return e;const i=t.subId?`${o.label} #${t.subId}`:o.all_label;return{...e,[n]:i}}),{});return 0===Object.keys(t).length&&(t=[o("No instances","elementor-pro")]),t}}t.ConditionsConfig=ConditionsConfig;t.default=ConditionsConfig},1471:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.ConditionsConfig=void 0;class ConditionsConfig extends $e.modules.CommandData{static signature="site-editor/conditions-config";static getEndpointFormat(){return"site-editor/conditions-config/{id}"}}t.ConditionsConfig=ConditionsConfig;t.default=ConditionsConfig},6338:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"ConditionsConfig",{enumerable:!0,get:function(){return i.ConditionsConfig}}),Object.defineProperty(t,"Templates",{enumerable:!0,get:function(){return o.Templates}}),Object.defineProperty(t,"TemplatesConditions",{enumerable:!0,get:function(){return s.TemplatesConditions}}),Object.defineProperty(t,"TemplatesConditionsConflicts",{enumerable:!0,get:function(){return r.TemplatesConditionsConflicts}});var o=n(4837),i=n(1471),s=n(801),r=n(7691)},7691:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.TemplatesConditionsConflicts=void 0;class TemplatesConditionsConflicts extends $e.modules.CommandData{static signature="site-editor/templates-conditions-conflicts";static getEndpointFormat(){return`${TemplatesConditionsConflicts.signature}/{id}`}}t.TemplatesConditionsConflicts=TemplatesConditionsConflicts;t.default=TemplatesConditionsConflicts},801:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.TemplatesConditions=void 0;class TemplatesConditions extends $e.modules.CommandData{static signature="site-editor/templates-conditions";static getEndpointFormat(){return"site-editor/templates-conditions/{id}"}}t.TemplatesConditions=TemplatesConditions;t.default=TemplatesConditions},4837:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Templates=void 0;class Templates extends $e.modules.CommandData{static signature="site-editor/templates";static getEndpointFormat(){return"site-editor/templates/{id}"}}t.Templates=Templates;t.default=Templates},1717:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var o=function _interopRequireWildcard(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(t);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var r=i?Object.getOwnPropertyDescriptor(e,s):null;r&&(r.get||r.set)?Object.defineProperty(o,s,r):o[s]=e[s]}return o.default=e,n&&n.set(e,o),o}(n(6338));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:t})(e)}class Component extends $e.modules.ComponentBase{static namespace="site-editor";getNamespace(){return this.constructor.namespace}defaultData(){return this.importCommands(o)}}t.default=Component},22:(e,t,n)=>{"use strict";var o=n(8003).__,i=n(3615),s=n(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=ConditionConflicts;var r=s(n(7363)),a=n(6779);function ConditionConflicts(e){if(!e.conflicts.length)return"";const t=e.conflicts.map((e=>r.default.createElement(a.Button,{key:e.template_id,target:"_blank",url:e.edit_url,text:e.template_title})));return r.default.createElement(a.Text,{className:"e-site-editor-conditions__conflict",variant:"sm"},o("Elementor recognized that you have set this location for other templates: ","elementor-pro")," ",t)}ConditionConflicts.propTypes={conflicts:i.array.isRequired}},3117:(e,t,n)=>{"use strict";var o=n(3615),i=n(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=ConditionName;var s=i(n(7363)),r=n(6779);function ConditionName(e){if("general"!==e.default)return"";return s.default.createElement("div",{className:"e-site-editor-conditions__input-wrapper"},s.default.createElement(r.Select,{options:e.options,value:e.name,onChange:t=>e.updateConditions(e.id,{name:t.target.value,sub:"",subId:""})}))}ConditionName.propTypes={updateConditions:o.func.isRequired,id:o.string.isRequired,name:o.string.isRequired,options:o.array.isRequired,default:o.string.isRequired},ConditionName.defaultProps={name:""}},7122:(e,t,n)=>{"use strict";var o=n(8003).__,i=n(3615),s=n(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=ConditionSubId;var r=s(n(7363)),a=n(6779);function ConditionSubId(e){const t=r.default.useMemo((()=>Object.keys(e.subIdAutocomplete).length?function getSettings(e){return{allowClear:!1,placeholder:o("All","elementor-pro"),dir:elementorCommon.config.isRTL?"rtl":"ltr",ajax:{transport:(t,n,o)=>elementorCommon.ajax.addRequest("pro_panel_posts_control_filter_autocomplete",{data:{q:t.data.q,autocomplete:e},success:n,error:o}),data:e=>({q:e.term,page:e.page}),cache:!0},escapeMarkup:e=>e,minimumInputLength:1}}(e.subIdAutocomplete):null),[e.subIdAutocomplete]);if(!e.sub||!t)return"";return r.default.createElement("div",{className:"e-site-editor-conditions__input-wrapper"},r.default.createElement(a.Select2,{onChange:t=>e.updateConditions(e.id,{subId:t.target.value}),value:e.subId,settings:t,options:e.subIdOptions}))}ConditionSubId.propTypes={subIdAutocomplete:i.object,id:i.string.isRequired,sub:i.string,subId:i.string,updateConditions:i.func,subIdOptions:i.array},ConditionSubId.defaultProps={subId:"",subIdOptions:[]}},3046:(e,t,n)=>{"use strict";var o=n(3615),i=n(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=ConditionSub;var s=i(n(7363)),r=n(6779);function ConditionSub(e){if("general"===e.name||!e.subOptions.length)return"";return s.default.createElement("div",{className:"e-site-editor-conditions__input-wrapper"},s.default.createElement(r.Select,{options:e.subOptions,value:e.sub,onChange:t=>e.updateConditions(e.id,{sub:t.target.value,subId:""})}))}ConditionSub.propTypes={updateConditions:o.func.isRequired,id:o.string.isRequired,name:o.string.isRequired,sub:o.string.isRequired,subOptions:o.array.isRequired},ConditionSub.defaultProps={sub:"",subOptions:{}}},6130:(e,t,n)=>{"use strict";var o=n(8003).__,i=n(3615),s=n(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=ConditionType;var r=s(n(7363)),a=n(6779);function ConditionType(e){const t=r.default.createRef(),n=[{label:o("Include","elementor-pro"),value:"include"},{label:o("Exclude","elementor-pro"),value:"exclude"}];return r.default.useEffect((()=>{t.current.setAttribute("data-elementor-condition-type",e.type)})),r.default.createElement("div",{className:"e-site-editor-conditions__input-wrapper e-site-editor-conditions__input-wrapper--condition-type",ref:t},r.default.createElement(a.Select,{options:n,value:e.type,onChange:t=>{e.updateConditions(e.id,{type:t.target.value})}}))}ConditionType.propTypes={updateConditions:i.func.isRequired,id:i.string.isRequired,type:i.string.isRequired},ConditionType.defaultProps={type:""}},9031:(e,t,n)=>{"use strict";var o=n(8003).__,i=n(3615),s=n(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=ConditionsRows;var r=s(n(7363)),a=s(n(3119)),d=n(6455),u=n(6779),l=s(n(6130)),c=s(n(3117)),p=s(n(3046)),m=s(n(7122)),f=s(n(22));function ConditionsRows(e){const{conditions:t,createConditionItemInState:n,updateConditionItemState:i,removeConditionItemInState:s,saveConditions:C,action:h,resetActionState:b}=r.default.useContext(d.Context),_=Object.values(t).map((e=>r.default.createElement("div",{key:e.id},r.default.createElement("div",{className:"e-site-editor-conditions__row"},r.default.createElement("div",{className:`e-site-editor-conditions__row-controls ${e.conflictErrors.length&&"e-site-editor-conditions__row-controls--error"}`},r.default.createElement(l.default,(0,a.default)({},e,{updateConditions:i})),r.default.createElement("div",{className:"e-site-editor-conditions__row-controls-inner"},r.default.createElement(c.default,(0,a.default)({},e,{updateConditions:i})),r.default.createElement(p.default,(0,a.default)({},e,{updateConditions:i})),r.default.createElement(m.default,(0,a.default)({},e,{updateConditions:i})))),r.default.createElement(u.Button,{className:"e-site-editor-conditions__remove-condition",text:o("Delete","elementor-pro"),icon:"eicon-close",hideText:!0,onClick:()=>s(e.id)})),r.default.createElement(f.default,{conflicts:e.conflictErrors})))),g=h.current===d.ConditionsProvider.actions.SAVE&&h.loading;return r.default.createElement(r.default.Fragment,null,h.error&&r.default.createElement(u.Dialog,{text:h.error,dismissButtonText:o("Go Back","elementor-pro"),dismissButtonOnClick:b,approveButtonText:o("Learn More","elementor-pro"),approveButtonColor:"link",approveButtonUrl:"https://go.elementor.com/app-theme-builder-conditions-load-issue",approveButtonTarget:"_target"}),r.default.createElement("div",{className:"e-site-editor-conditions__rows"},_),r.default.createElement("div",{className:"e-site-editor-conditions__add-button-container"},r.default.createElement(u.Button,{className:"e-site-editor-conditions__add-button",variant:"contained",size:"lg",text:o("Add Condition","elementor-pro"),onClick:n})),r.default.createElement("div",{className:"e-site-editor-conditions__footer"},r.default.createElement(u.Button,{variant:"contained",color:"primary",size:"lg",hideText:g,icon:g?"eicon-loading eicon-animation-spin":"",text:o("Save & Close","elementor-pro"),onClick:()=>C().then(e.onAfterSave)})))}ConditionsRows.propTypes={onAfterSave:i.func}},8808:(e,t,n)=>{"use strict";var o=n(8003).__,i=n(3615),s=n(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=ConditionsModal;var r=function _interopRequireWildcard(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(t);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var r=i?Object.getOwnPropertyDescriptor(e,s):null;r&&(r.get||r.set)?Object.defineProperty(o,s,r):o[s]=e[s]}return o.default=e,n&&n.set(e,o),o}(n(7363)),a=n(6779),d=s(n(3849)),u=s(n(7163));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:t})(e)}function ConditionsModal(){const[e,t]=(0,r.useState)(!1),[n,i]=(0,r.useState)({conditions:null,instances:null}),s=(0,r.useRef)(!1),l=elementorProAdmin.customCode.post,c=(0,r.useMemo)((()=>({$form:jQuery("#post"),$formConditions:jQuery(""),$publishButton:jQuery("#publish"),title:{$label:jQuery("#title-prompt-text"),$input:jQuery("#title")}})),[]),onPostSubmit=()=>{const{title:e}=c;e.$input.attr("value").length||(e.$label.addClass("screen-reader-text"),e.$input.attr("value",o("Elementor Custom-Code #","elementor-pro")+elementorProAdmin.customCode.post.ID))},onPublishClick=n=>{if("auto-draft"===l.post_status&&!e&&!s.current){n.preventDefault();const e=[{name:"general",sub:"",subId:"",type:"include"}];i((t=>({...t,conditions:e}))),t(!0)}};return(0,r.useEffect)((()=>{(async()=>{const e=await u.default.create();$e.data.get("site-editor/templates-conditions",{id:l.ID},{refresh:!0}).then((t=>{const n=Object.values(t.data).map((e=>({type:e.type,name:e.name,sub:e.sub_name,subId:e.sub_id}))),o=Object.values(e.calculateInstances(n)).join(",");i((e=>({...e,conditions:n,instances:o})))}))})(),c.$publishButton.on("click",onPublishClick),c.$form.on("submit",onPostSubmit)}),[]),l&&n.conditions?r.default.createElement(r.default.Fragment,null,r.default.createElement(a.Text,{tag:"span",className:"post-conditions-display"},r.default.createElement("b",null,n.instances+" ")),r.default.createElement(a.Button,{onClick:()=>t(!0),text:o("Edit","elementor"),variant:"underlined"}),r.default.createElement(a.ModalProvider,{show:e,setShow:t,title:o("Publish Settings","elementor"),icon:"eps-app__logo eicon-elementor"},r.default.createElement(a.CssGrid,{columns:1,spacing:700},r.default.createElement("section",null,r.default.createElement(d.default,{id:l.ID,status:l.post_status,conditions:n.conditions,onConditionsSaved:e=>{const n=e.conditions,o=Object.values(e.instances).join(","),{$form:r,$formConditions:a,$publishButton:d}=c;s.current=!0,i((e=>({...e,conditions:n,instances:o}))),"auto-draft"!==l.post_status&&"draft"!==l.post_status||a.attr("type","hidden").attr("name","_conditions").attr("value",JSON.stringify(n)).appendTo(r),d.trigger("click"),t(!1)},onAfterSave:()=>{}}))))):r.default.createElement(r.default.Fragment,null,r.default.createElement(a.Text,{tag:"span"},o("Loading","elementor")),r.default.createElement(a.Icon,{className:"spinner"}))}ConditionsModal.propTypes={children:i.object}},3849:(e,t,n)=>{"use strict";var o=n(8003).__,i=n(3615),s=n(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=Conditions;var r=s(n(7363)),a=n(6779),d=s(n(6455)),u=s(n(9031));function Conditions(e){const t={...e,defaultCondition:"general"};return r.default.createElement("section",{className:"e-site-editor-conditions"},r.default.createElement("div",{className:"e-site-editor-conditions__header"},r.default.createElement("img",{className:"e-site-editor-conditions__header-image",src:`${elementorAppProConfig.baseUrl}/modules/theme-builder/assets/images/conditions-tab.svg`,alt:o("Conditions","elementor-pro")}),r.default.createElement(a.Heading,{variant:"h1",tag:"h1"},o("Where Do You Want to Display Your Code?","elementor-pro")),r.default.createElement(a.Text,{variant:"md"},o("Set the conditions that determine where your code snippet is used throughout your site.","elementor-pro"),r.default.createElement("br",null),o("For example, choose 'Entire Site' to display the code snippet across your site.","elementor-pro"))),r.default.createElement(d.default,{validateConflicts:!1,currentTemplate:t,onConditionsSaved:(t,n)=>{$e.data.setCache($e.components.get("site-editor"),"site-editor/templates-conditions",{id:t},n.conditions),e.onConditionsSaved(n)}},r.default.createElement(u.default,{onAfterSave:e.onAfterSave})))}Conditions.propTypes={id:i.number,status:i.string.isRequired,conditions:i.array,onConditionsSaved:i.func,onAfterSave:i.func.isRequired},Conditions.defaultProps={conditions:[],onConditionsSaved:()=>{}}},8772:(e,t,n)=>{"use strict";var o=n(331);function emptyFunction(){}function emptyFunctionWithReset(){}emptyFunctionWithReset.resetWarningCache=emptyFunction,e.exports=function(){function shim(e,t,n,i,s,r){if(r!==o){var a=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw a.name="Invariant Violation",a}}function getShim(){return shim}shim.isRequired=shim;var e={array:shim,bigint:shim,bool:shim,func:shim,number:shim,object:shim,string:shim,symbol:shim,any:shim,arrayOf:getShim,element:shim,elementType:shim,instanceOf:getShim,node:shim,objectOf:getShim,oneOf:getShim,oneOfType:getShim,shape:getShim,exact:getShim,checkPropTypes:emptyFunctionWithReset,resetWarningCache:emptyFunction};return e.PropTypes=e,e}},3615:(e,t,n)=>{e.exports=n(8772)()},331:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},7363:e=>{"use strict";e.exports=React},7443:e=>{"use strict";e.exports=__UNSTABLE__elementorAI.App},6779:e=>{"use strict";e.exports=elementorAppPackages.appUi},8003:e=>{"use strict";e.exports=wp.i18n},3119:e=>{function _extends(){return e.exports=_extends=Object.assign?Object.assign.bind():function(e){for(var t=1;t{e.exports=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}},e.exports.__esModule=!0,e.exports.default=e.exports}},t={};function __webpack_require__(n){var o=t[n];if(void 0!==o)return o.exports;var i=t[n]={exports:{}};return e[n](i,i.exports,__webpack_require__),i.exports}(()=>{"use strict";var e=__webpack_require__(8003).__,t=__webpack_require__(3203);var n=t(__webpack_require__(7363)),o=t(__webpack_require__(1717)),i=t(__webpack_require__(8808)),s=t(__webpack_require__(7443));class CustomCode extends elementorModules.Module{constructor(){super(),jQuery(this.initialize.bind(this))}initialize(){$e.components.register(new o.default),ReactDOM.render(n.default.createElement(i.default,null),document.querySelector(".post-conditions")),this.addTipsyToFields(),this.addDescription(),this.addLocationChangeHandler(),this.addOpenAIButton(),this.setOptionsPlacementVisibility("elementor_body_end"===jQuery("#location").val())}addTipsyToFields(){jQuery(".elementor-field-label i[data-info]").tipsy({title(){return this.getAttribute("data-info")},gravity:()=>"s"})}addDescription(){const t="

"+e("Manage and create all of your custom code here.
Organize all of your custom code and incorporate code snippets in your site. Add tracking codes, meta titles, and other scripts. Set display conditions, locations, and priority all from one place.","elementor-pro")+' '+e("Learn more","elementor-pro")+"

";jQuery(t).insertBefore(".wp-header-end")}addLocationChangeHandler(){jQuery("#location").on("change",(e=>{this.setOptionsPlacementVisibility("elementor_body_end"===e.target.value)}))}addOpenAIButton(){const t=jQuery(``);t.on("click",(e=>{e.preventDefault();const t=elementorCommon.config.isRTL,o=document.createElement("div");document.body.append(o),ReactDOM.render(n.default.createElement(s.default,{type:"code",getControlValue:()=>document.querySelector(".CodeMirror").CodeMirror.getValue(),setControlValue:e=>document.querySelector(".CodeMirror").CodeMirror.setValue(e),additionalOptions:{codeLanguage:"html"},onClose:()=>{ReactDOM.unmountComponentAtNode(o),o.parentNode.removeChild(o)},isRTL:t}),o)})),jQuery(".elementor-field.location.elementor-field-select").after(t)}setOptionsPlacementVisibility(e){jQuery(".elementor-custom-code-options-placement").toggleClass("show",e)}}elementorProAdmin.customCode=new CustomCode})()})(); \ No newline at end of file diff --git a/assets/js/display-conditions.js b/assets/js/display-conditions.js new file mode 100644 index 0000000..f2a9ee5 --- /dev/null +++ b/assets/js/display-conditions.js @@ -0,0 +1,5508 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ "../core/app/assets/js/utils.js": +/*!**************************************!*\ + !*** ../core/app/assets/js/utils.js ***! + \**************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.replaceUtmPlaceholders = exports.htmlDecodeTextContent = exports.arrayToClassName = void 0; +// Copied from Core. +const arrayToClassName = (array, action) => { + return array.filter(item => 'object' === typeof item ? Object.entries(item)[0][1] : item).map(item => { + const value = 'object' === typeof item ? Object.entries(item)[0][0] : item; + return action ? action(value) : value; + }).join(' '); +}; +exports.arrayToClassName = arrayToClassName; +const htmlDecodeTextContent = input => { + const doc = new DOMParser().parseFromString(input, 'text/html'); + return doc.documentElement.textContent; +}; +exports.htmlDecodeTextContent = htmlDecodeTextContent; +const replaceUtmPlaceholders = (link = '', utms = {}) => { + if (!link || !utms) { + return link; + } + Object.keys(utms).forEach(key => { + const match = new RegExp(`%%${key}%%`, 'g'); + link = link.replace(match, utms[key]); + }); + return link; +}; +exports.replaceUtmPlaceholders = replaceUtmPlaceholders; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/app.js": +/*!*************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/app.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var PropTypes = __webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js"); + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _content = _interopRequireDefault(__webpack_require__(/*! ./components/content */ "../modules/display-conditions/assets/js/editor/components/content.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const App = props => { + const [dialogOpen, setDialogOpen] = (0, _react.useState)(true), + fadeDuration = 500; + (0, _react.useEffect)(() => { + if (!dialogOpen) { + const timeoutId = setTimeout(() => { + props.onClose(); + }, fadeDuration); + return () => clearTimeout(timeoutId); + } + }, [dialogOpen]); + const handleCloseDialog = () => { + setDialogOpen(false); + }; + return /*#__PURE__*/_react.default.createElement(_ui.DirectionProvider, { + rtl: props.isRTL + }, /*#__PURE__*/_react.default.createElement(_ui.LocalizationProvider, null, /*#__PURE__*/_react.default.createElement(_ui.ThemeProvider, { + colorScheme: props.colorScheme + }, /*#__PURE__*/_react.default.createElement(_ui.Dialog, { + open: dialogOpen, + fullWidth: true, + maxWidth: "lg", + TransitionComponent: _ui.Fade, + transitionDuration: { + enter: fadeDuration, + exit: fadeDuration + }, + sx: { + '& .MuiDialog-paper': { + height: 'calc(100vh - 4rem)', + maxHeight: 775 + } + } + }, /*#__PURE__*/_react.default.createElement(_content.default, { + getControlValue: props.getControlValue, + setControlValue: props.setControlValue, + fetchData: props.fetchData, + onClose: handleCloseDialog, + conditionsConfig: props.conditionsConfig, + setCacheNoticeStatus: props.setCacheNoticeStatus + }))))); +}; +App.propTypes = { + colorScheme: PropTypes.oneOf(['auto', 'light', 'dark']), + isRTL: PropTypes.bool, + getControlValue: PropTypes.func.isRequired, + setControlValue: PropTypes.func.isRequired, + fetchData: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, + conditionsConfig: PropTypes.object.isRequired, + setCacheNoticeStatus: PropTypes.func.isRequired +}; +var _default = exports["default"] = App; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/behavior.js": +/*!******************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/behavior.js ***! + \******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); +var _app = _interopRequireDefault(__webpack_require__(/*! ./app */ "../modules/display-conditions/assets/js/editor/app.js")); +class DisplayConditionsBehavior extends Marionette.Behavior { + ui() { + const iconClass = '.eicon-flow.e-control-display-conditions'; + return { + displayConditionsButton: iconClass, + displayConditionsPromoButton: `${iconClass}-promo` + }; + } + events() { + return { + 'click @ui.displayConditionsButton': 'onClickControlButtonDisplayConditions', + 'mouseenter @ui.displayConditionsPromoButton': 'onHoverControlButtonDisplayConditions' + }; + } + onClickControlButtonDisplayConditions(event) { + event.stopPropagation(); + this.mount(); + } + onHoverControlButtonDisplayConditions(event) { + event.stopPropagation(); + elementor.promotion.showDialog({ + title: __('Display Conditions', 'elementor-pro'), + content: __('Upgrade to Elementor Pro Advanced to get the Display Conditions feature as well as additional professional and ecommerce widgets', 'elementor-pro'), + targetElement: this.el, + actionButton: { + url: 'https://go.elementor.com/go-pro-advanced-display-conditions/', + text: __('Upgrade Now', 'elementor-pro'), + classes: ['elementor-button', 'go-pro'] + } + }); + } + getRootElement() { + let rootElement = window.parent.document.getElementById('elementor-conditions__modal'); + if (!!rootElement) { + return rootElement; + } + rootElement = document.createElement('div'); + rootElement.setAttribute('id', 'elementor-conditions__modal'); + return rootElement; + } + mount() { + const colorScheme = elementor?.getPreferences?.('ui_theme') || 'auto', + isRTL = elementorCommon.config.isRTL, + rootElement = this.getRootElement(); + window.parent.document.body.appendChild(rootElement); + ReactDOM.render( /*#__PURE__*/_react.default.createElement(_app.default // eslint-disable-line react/no-deprecated + , { + colorScheme: colorScheme, + isRTL: isRTL, + getControlValue: this.getOption('getControlValue'), + setControlValue: this.getOption('setControlValue'), + fetchData: this.getOption('fetchData'), + onClose: () => this.unmount(rootElement), + conditionsConfig: this.getOption('conditionsConfig'), + setCacheNoticeStatus: this.getOption('setCacheNoticeStatus') + }), rootElement); + } + unmount(rootElement) { + // eslint-disable-next-line react/no-deprecated + ReactDOM.unmountComponentAtNode(rootElement); + rootElement.remove(); + } +} +exports["default"] = DisplayConditionsBehavior; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/cache-notice.js": +/*!*********************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/cache-notice.js ***! + \*********************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var PropTypes = __webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js"); + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _i18n = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const CacheNotice = ({ + setCacheNoticeStatus +}) => { + const [open, setOpen] = (0, _react.useState)(true); + const handleClose = async () => { + const response = await setCacheNoticeStatus(); + if (response) { + setOpen(false); + } + }; + return /*#__PURE__*/_react.default.createElement(_ui.Box, null, /*#__PURE__*/_react.default.createElement(_ui.Collapse, { + in: open, + sx: { + px: 3 + } + }, /*#__PURE__*/_react.default.createElement(_ui.Alert, { + color: "info", + severity: "error", + variant: "standard", + onClose: handleClose, + sx: { + mt: 3 + } + }, (0, _i18n.__)('Keep in mind: Certain cache plugins can conflict with your display conditions. ', 'elementor-pro'), /*#__PURE__*/_react.default.createElement(_ui.Link, { + href: "https://go.elementor.com/app-display-conditions-cache-notice/", + underline: "hover", + color: "info.main", + target: "_blank", + sx: { + '&:hover': { + color: theme => theme.palette.info.main + } + } + }, (0, _i18n.__)('Learn more', 'elementor-pro'))))); +}; +CacheNotice.propTypes = { + setCacheNoticeStatus: PropTypes.func.isRequired +}; +var _default = exports["default"] = CacheNotice; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/conditions-repeater-row.js": +/*!********************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/conditions-repeater-row.js ***! + \********************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var React = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var PropTypes = _interopRequireWildcard(__webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js")); +var _useConditions = _interopRequireDefault(__webpack_require__(/*! ../hooks/use-conditions */ "../modules/display-conditions/assets/js/editor/hooks/use-conditions.js")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _controlRenderer = _interopRequireDefault(__webpack_require__(/*! ./control-renderer */ "../modules/display-conditions/assets/js/editor/components/control-renderer.js")); +var _conditionSelectControl = _interopRequireDefault(__webpack_require__(/*! ./controls/ui/condition-select-control */ "../modules/display-conditions/assets/js/editor/components/controls/ui/condition-select-control.js")); +var _conditionSelectOption = _interopRequireDefault(__webpack_require__(/*! ./controls/ui/condition-select-option */ "../modules/display-conditions/assets/js/editor/components/controls/ui/condition-select-option.js")); +var _rowControls = _interopRequireDefault(__webpack_require__(/*! ./ui/row-controls */ "../modules/display-conditions/assets/js/editor/components/ui/row-controls.js")); +var _utils = __webpack_require__(/*! ../utils/utils */ "../modules/display-conditions/assets/js/editor/utils/utils.js"); +var _constants = __webpack_require__(/*! ../utils/constants */ "../modules/display-conditions/assets/js/editor/utils/constants.js"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const ConditionsRepeaterRow = ({ + andConditionIndex, + orConditionIndex +}) => { + const { + selectedConditions, + conditionsConfig, + dispatch + } = (0, _useConditions.default)(), + { + conditions: availableConditions, + flattenedConditionOptions + } = conditionsConfig, + orCondition = selectedConditions[orConditionIndex]; + const andCondition = orCondition[andConditionIndex], + selectedConditionKey = andCondition?.condition; + const conditionControls = availableConditions[selectedConditionKey]?.controls || {}, + controlCount = Object.keys(conditionControls).length; + const handleChangeCondition = event => { + const conditionKey = event.target.value, + conditionToChange = { + condition: conditionKey, + ...(0, _utils.getConditionInitialState)(availableConditions, conditionKey) + }; + dispatch({ + type: _constants.ACTION_TYPES.CHANGE_CONDITION_TYPE, + orConditionIndex, + andConditionIndex, + conditionToChange + }); + }; + return /*#__PURE__*/React.createElement(_ui.Container, { + maxWidth: "md", + sx: { + display: 'flex', + gap: 0.5, + mb: 1, + position: 'relative' + }, + className: `and-condition-repeater-row and-condition-${andConditionIndex}` + }, /*#__PURE__*/React.createElement(_conditionSelectControl.default, { + id: "condition-select", + value: andCondition.condition || '', + onChange: event => handleChangeCondition(event, andConditionIndex), + controlCount: controlCount + }, flattenedConditionOptions.map(({ + key, + label, + isGroup + }) => isGroup ? /*#__PURE__*/React.createElement(_ui.ListSubheader, { + key: key + }, /*#__PURE__*/React.createElement(_conditionSelectOption.default, { + variant: 'inherit', + controlCount: controlCount + }, label)) : /*#__PURE__*/React.createElement(_ui.MenuItem, { + key: key, + value: key + }, /*#__PURE__*/React.createElement(_conditionSelectOption.default, { + controlCount: controlCount + }, label)))), Object.keys(conditionControls).map(controlKey => /*#__PURE__*/React.createElement(_controlRenderer.default, { + key: controlKey, + controlKey: controlKey, + andConditionIndex: andConditionIndex, + orConditionIndex: orConditionIndex, + controlCount: controlCount + })), /*#__PURE__*/React.createElement(_rowControls.default, { + orConditionIndex: orConditionIndex, + andConditionIndex: andConditionIndex + })); +}; +ConditionsRepeaterRow.propTypes = { + andConditionIndex: PropTypes.number.isRequired, + orConditionIndex: PropTypes.number.isRequired +}; +var _default = exports["default"] = ConditionsRepeaterRow; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/conditions-selectors.js": +/*!*****************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/conditions-selectors.js ***! + \*****************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var PropTypes = __webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js"); + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); +var _useConditions = _interopRequireDefault(__webpack_require__(/*! ../hooks/use-conditions */ "../modules/display-conditions/assets/js/editor/hooks/use-conditions.js")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _conditionsRepeaterRow = _interopRequireDefault(__webpack_require__(/*! ./conditions-repeater-row */ "../modules/display-conditions/assets/js/editor/components/conditions-repeater-row.js")); +const ConditionsSelectors = ({ + orConditionIndex +}) => { + const { + selectedConditions + } = (0, _useConditions.default)(), + orCondition = selectedConditions[orConditionIndex]; + return /*#__PURE__*/_react.default.createElement(_ui.Box, { + sx: { + my: 2, + gap: 1 + }, + className: `or-condition-repeater or-condition-${orConditionIndex}` + }, orCondition.map((andCondition, andConditionIndex) => /*#__PURE__*/_react.default.createElement(_conditionsRepeaterRow.default, { + key: 'or-condition-row-' + andConditionIndex, + andConditionIndex: andConditionIndex, + orConditionIndex: orConditionIndex + }))); +}; +ConditionsSelectors.propTypes = { + orConditionIndex: PropTypes.number.isRequired +}; +var _default = exports["default"] = ConditionsSelectors; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/conditions.js": +/*!*******************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/conditions.js ***! + \*******************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var React = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _hierarchyIcon = _interopRequireDefault(__webpack_require__(/*! ./icons/hierarchy-icon */ "../modules/display-conditions/assets/js/editor/components/icons/hierarchy-icon.js")); +var _orRowGroup = _interopRequireDefault(__webpack_require__(/*! ./or-row-group */ "../modules/display-conditions/assets/js/editor/components/or-row-group.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const Conditions = props => { + return /*#__PURE__*/React.createElement(_ui.Box, { + display: "flex", + justifyContent: "center", + alignItems: "flex-start", + sx: { + flex: 1, + overflow: 'auto' + } + }, /*#__PURE__*/React.createElement(_ui.Stack, { + maxWidth: "md", + width: "100%", + justifyContent: "center", + textAlign: "center", + sx: { + pt: 5, + pb: 10, + px: 6 + } + }, /*#__PURE__*/React.createElement(_hierarchyIcon.default, { + fontSize: "large", + sx: { + mb: 1, + mx: 'auto' + } + }), /*#__PURE__*/React.createElement(_ui.Typography, { + component: "h5", + variant: "h5", + color: "text.primary" + }, __('Set one or more conditions for this element', 'elementor-pro')), /*#__PURE__*/React.createElement(_ui.Typography, { + variant: "subtitle1", + color: "text.tertiary", + sx: { + mb: 4 + } + }, __('It will only appear on your website when all the conditions are met.', 'elementor-pro'), ' ', /*#__PURE__*/React.createElement(_ui.Link, { + href: "https://go.elementor.com/app-display-conditions/", + target: "_blank", + rel: "noreferrer", + color: "info.main", + underline: "hover", + sx: { + '&:hover': { + color: theme => theme.palette.info.main + } + } + }, __('Learn more', 'elementor-pro'))), /*#__PURE__*/React.createElement(_orRowGroup.default, props))); +}; +var _default = exports["default"] = Conditions; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/content.js": +/*!****************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/content.js ***! + \****************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var PropTypes = __webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js"); + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _conditionsReducer = __webpack_require__(/*! ../reducers/conditions-reducer */ "../modules/display-conditions/assets/js/editor/reducers/conditions-reducer.js"); +var _conditionsContext = __webpack_require__(/*! ../contexts/conditions-context */ "../modules/display-conditions/assets/js/editor/contexts/conditions-context.js"); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _utils = __webpack_require__(/*! ../utils/utils */ "../modules/display-conditions/assets/js/editor/utils/utils.js"); +var _header = _interopRequireDefault(__webpack_require__(/*! ./header */ "../modules/display-conditions/assets/js/editor/components/header.js")); +var _footer = _interopRequireDefault(__webpack_require__(/*! ./footer */ "../modules/display-conditions/assets/js/editor/components/footer.js")); +var _conditions = _interopRequireDefault(__webpack_require__(/*! ./conditions */ "../modules/display-conditions/assets/js/editor/components/conditions.js")); +var _cacheNotice = _interopRequireDefault(__webpack_require__(/*! ./cache-notice */ "../modules/display-conditions/assets/js/editor/components/cache-notice.js")); +var _constants = __webpack_require__(/*! ../utils/constants */ "../modules/display-conditions/assets/js/editor/utils/constants.js"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const Content = ({ + getControlValue, + setControlValue, + conditionsConfig, + onClose, + fetchData, + setCacheNoticeStatus +}) => { + const inputValue = getControlValue(), + controlValue = inputValue || [], + initialState = { + conditionsConfig, + selectedConditions: controlValue, + fetchData + }; + const [showConditions, setShowConditions] = _react.default.useState(true), + [conditionsStore, dispatch] = (0, _react.useReducer)(_conditionsReducer.conditionsReducer, initialState), + [saveButtonDisplay, setSaveButtonDisplay] = (0, _react.useState)(false), + { + selectedConditions + } = conditionsStore; + (0, _react.useEffect)(() => { + if (!saveButtonDisplay) { + setSaveButtonDisplay(true); + } + }, [selectedConditions]); + (0, _react.useEffect)(() => { + setSaveButtonDisplay(false); + }, []); + const handleEmptyFieldsAndGetFirstInvalidIndex = () => { + let hasFoundInvalidCondition = false, + invalidOrConditionIndex = null, + invalidAndConditionIndex = null; + selectedConditions.forEach((orCondition, orConditionIndex) => { + const { + hasFoundInvalidConditionInConditionSet, + invalidAndConditionIndex: andConditionIndex + } = handleEmptyFieldsPerConditionSet(orCondition, orConditionIndex); + if (hasFoundInvalidConditionInConditionSet && !hasFoundInvalidCondition) { + hasFoundInvalidCondition = true; + invalidAndConditionIndex = andConditionIndex; + invalidOrConditionIndex = orConditionIndex; + } + }); + return { + hasFoundInvalidCondition, + invalidOrConditionIndex, + invalidAndConditionIndex + }; + }; + const handleEmptyFieldsPerConditionSet = (orCondition, orConditionIndex) => { + let hasFoundInvalidConditionInConditionSet = false, + invalidAndConditionIndex = null; + orCondition.forEach((andCondition, andConditionIndex) => { + const { + condition: conditionKey + } = andCondition, + requiredKeys = getRequiredControlKeys(conditionKey); + const isCurrentConditionInvalid = handleInvalidRequiredKeysPerCondition({ + requiredKeys, + andCondition, + orConditionIndex, + andConditionIndex + }); + if (isCurrentConditionInvalid && !hasFoundInvalidConditionInConditionSet) { + invalidAndConditionIndex = andConditionIndex; + hasFoundInvalidConditionInConditionSet = true; + } + }); + return { + hasFoundInvalidConditionInConditionSet, + invalidAndConditionIndex + }; + }; + const handleInvalidRequiredKeysPerCondition = ({ + requiredKeys, + andCondition, + orConditionIndex, + andConditionIndex + }) => { + const { + condition: conditionKey + } = andCondition; + let hasFoundInvalidCondition = false; + requiredKeys.forEach(controlKey => { + const value = andCondition[controlKey], + { + type, + variant = null + } = conditionsConfig.conditions[conditionKey].controls[controlKey]; + if (value?.length || (0, _utils.shouldEmptyValuePassValidation)(andCondition.condition, andCondition.comparator)) { + return; + } + if (!hasFoundInvalidCondition) { + hasFoundInvalidCondition = true; + } + dispatch({ + type: _constants.ACTION_TYPES.SET_ERRORS, + andConditionIndex, + orConditionIndex, + errors: { + [controlKey]: (0, _utils.getInvalidInputFeedback)(type, variant, value, true) + } + }); + }); + return hasFoundInvalidCondition; + }; + const handleSave = () => { + const { + hasFoundInvalidCondition, + invalidOrConditionIndex, + invalidAndConditionIndex + } = handleEmptyFieldsAndGetFirstInvalidIndex(); + if (hasFoundInvalidCondition) { + const className = `.or-condition-repeater.or-condition-${invalidOrConditionIndex} .and-condition-repeater-row.and-condition-${invalidAndConditionIndex}`, + conditionRepeaterRows = document.querySelector(className); + setTimeout(() => conditionRepeaterRows?.scrollIntoView({ + behavior: 'smooth' + }), 100); + return; + } + setControlValue([JSON.stringify(getSanitizedConditions())]); + onClose(); + }; + const getRequiredControlKeys = condition => { + const { + controls + } = conditionsConfig.conditions[condition]; + return Object.keys(controls).filter(key => controls[key].required); + }; + const getSanitizedConditions = () => { + return selectedConditions.map(orCondition => { + return orCondition.map(andCondition => { + const formattedCondition = { + ...andCondition + }; + delete formattedCondition.errors; + return formattedCondition; + }); + }); + }; + return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_header.default, { + onClose: onClose + }), /*#__PURE__*/_react.default.createElement(_ui.Divider, { + orientation: "horizontal" + }), conditionsConfig.show_cache_notice && /*#__PURE__*/_react.default.createElement(_cacheNotice.default, { + setCacheNoticeStatus: setCacheNoticeStatus + }), /*#__PURE__*/_react.default.createElement(_conditionsContext.ConditionsContext.Provider, { + value: { + dispatch, + ...conditionsStore + } + }, /*#__PURE__*/_react.default.createElement(_conditions.default, { + showConditions: showConditions, + setShowConditions: setShowConditions + })), /*#__PURE__*/_react.default.createElement(_ui.Divider, { + orientation: "horizontal" + }), /*#__PURE__*/_react.default.createElement(_footer.default, { + onClickSaveButton: () => handleSave(), + showConditions: showConditions, + setShowConditions: setShowConditions, + isButtonDisabled: saveButtonDisplay + })); +}; +Content.propTypes = { + getControlValue: PropTypes.func.isRequired, + setControlValue: PropTypes.func.isRequired, + fetchData: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, + conditionsConfig: PropTypes.object.isRequired, + setCacheNoticeStatus: PropTypes.func.isRequired +}; +var _default = exports["default"] = Content; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/control-renderer.js": +/*!*************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/control-renderer.js ***! + \*************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var React = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var PropTypes = _interopRequireWildcard(__webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js")); +var _useConditions = _interopRequireDefault(__webpack_require__(/*! ../hooks/use-conditions */ "../modules/display-conditions/assets/js/editor/hooks/use-conditions.js")); +var _selectControl = _interopRequireDefault(__webpack_require__(/*! ./controls/select-control */ "../modules/display-conditions/assets/js/editor/components/controls/select-control.js")); +var _autocompleteControl = _interopRequireDefault(__webpack_require__(/*! ./controls/autocomplete-control */ "../modules/display-conditions/assets/js/editor/components/controls/autocomplete-control.js")); +var _queryControl = _interopRequireDefault(__webpack_require__(/*! ./controls/query-control */ "../modules/display-conditions/assets/js/editor/components/controls/query-control.js")); +var _textFieldControl = _interopRequireDefault(__webpack_require__(/*! ./controls/text-field-control */ "../modules/display-conditions/assets/js/editor/components/controls/text-field-control.js")); +var _datePickerControl = _interopRequireDefault(__webpack_require__(/*! ./controls/date-picker-control */ "../modules/display-conditions/assets/js/editor/components/controls/date-picker-control.js")); +var _timePickerControl = _interopRequireDefault(__webpack_require__(/*! ./controls/time-picker-control */ "../modules/display-conditions/assets/js/editor/components/controls/time-picker-control.js")); +var _constants = __webpack_require__(/*! ../utils/constants */ "../modules/display-conditions/assets/js/editor/utils/constants.js"); +var _utils = __webpack_require__(/*! ../utils/utils */ "../modules/display-conditions/assets/js/editor/utils/utils.js"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const ControlRenderer = ({ + controlKey, + andConditionIndex, + orConditionIndex, + controlCount +}) => { + const { + conditionsConfig, + selectedConditions, + dispatch + } = (0, _useConditions.default)(), + { + conditions: availableConditions + } = conditionsConfig, + orCondition = selectedConditions[orConditionIndex], + andCondition = orCondition[andConditionIndex]; + const selectedConditionKey = andCondition.condition, + { + controls = {} + } = availableConditions[selectedConditionKey], + control = ({} = controls[controlKey]), + { + options = {} + } = control; + if ('__settings' === controlKey) { + return null; + } + const extractControlPropsFromGlobals = defaultAltValue => { + const valueProps = getControlValueRelatedProps(defaultAltValue), + invalidInputProps = getControlInvalidInputRelatedProps(), + controlProps = { + controlKey, + control, + conditionIndex: andConditionIndex, + condition: andCondition, + conditions: availableConditions, + options, + onChangeOption: handleChangeOption, + controlCount + }; + if ((0, _utils.shouldDisableControl)(controlKey, controlProps.condition.comparator)) { + controlProps.disabled = true; + } + return { + ...valueProps, + ...invalidInputProps, + ...controlProps + }; + }; + const getControlValueRelatedProps = defaultAltValue => { + defaultAltValue = (0, _utils.getControlValue)(defaultAltValue, _constants.DEFAULT_CONTROL_VALUES[control.type]); + const defaultValue = (0, _utils.getControlValue)(control?.default, Object.keys(options)[0] || defaultAltValue), + value = (0, _utils.getControlValue)(andCondition[controlKey], defaultValue); + const placeholder = control?.placeholder || '', + isMultiple = control?.multiple || false; + return { + defaultValue, + value, + placeholder, + isMultiple + }; + }; + const getControlInvalidInputRelatedProps = () => { + const conditionErrors = andCondition.errors || {}, + controlErrors = conditionErrors[controlKey] || {}, + errorMessage = controlErrors.shouldShow && controlErrors.message || '', + shouldShowError = Boolean(errorMessage); + return { + errorMessage, + shouldShowError + }; + }; + const handleChangeOption = value => { + const { + type, + variant + } = controls[controlKey], + error = (0, _utils.getInvalidInputFeedback)(type, variant, value); + dispatch({ + type: _constants.ACTION_TYPES.CHANGE_CONTROL_VALUE, + orConditionIndex, + andConditionIndex, + controlKey, + value + }); + dispatch({ + type: _constants.ACTION_TYPES.SET_ERRORS, + andConditionIndex, + orConditionIndex, + errors: { + [controlKey]: error + } + }); + }; + const getDateAndTimeBasedControl = variant => { + switch (variant) { + case 'date': + return /*#__PURE__*/React.createElement(_datePickerControl.default, extractControlPropsFromGlobals()); + case 'time': + return /*#__PURE__*/React.createElement(_timePickerControl.default, extractControlPropsFromGlobals()); + } + }; + switch (control.type) { + case _constants.CONTROL_TYPES.SELECT: + return /*#__PURE__*/React.createElement(_selectControl.default, extractControlPropsFromGlobals()); + case _constants.CONTROL_TYPES.MULTIPLE_SELECT: + return /*#__PURE__*/React.createElement(_autocompleteControl.default, extractControlPropsFromGlobals()); + case _constants.CONTROL_TYPES.DATE_TIME: + return getDateAndTimeBasedControl(control?.variant); + case _constants.CONTROL_TYPES.QUERY: + return /*#__PURE__*/React.createElement(_queryControl.default, extractControlPropsFromGlobals()); + } + return /*#__PURE__*/React.createElement(_textFieldControl.default, extractControlPropsFromGlobals()); +}; +ControlRenderer.propTypes = { + controlKey: PropTypes.string.isRequired, + andConditionIndex: PropTypes.number.isRequired, + orConditionIndex: PropTypes.number.isRequired, + controlCount: PropTypes.number.isRequired +}; +var _default = exports["default"] = ControlRenderer; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/controls/autocomplete-control.js": +/*!**************************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/controls/autocomplete-control.js ***! + \**************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _extends2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/extends */ "../node_modules/@babel/runtime/helpers/extends.js")); +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var React = _react; +var PropTypes = _interopRequireWildcard(__webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _conditionSelectOption = _interopRequireDefault(__webpack_require__(/*! ./ui/condition-select-option */ "../modules/display-conditions/assets/js/editor/components/controls/ui/condition-select-option.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const formatValue = valueToFormat => { + return Array.isArray(valueToFormat) ? valueToFormat : [valueToFormat]; +}; +const AutocompleteControl = ({ + conditions, + condition, + controlKey, + onChangeOption, + options, + value, + shouldShowError, + errorMessage, + isMultiple, + controlCount +}) => { + const [controlValue, setControlValue] = (0, _react.useState)(formatValue(value)), + label = controlValue?.length ? '' : conditions[condition.condition].label || ''; + (0, _react.useEffect)(() => { + setControlValue(formatValue(value)); + }, [condition]); + const handleChangeOption = newValue => { + onChangeOption(newValue); + setControlValue(newValue); + }; + return /*#__PURE__*/React.createElement(_ui.Autocomplete, { + multiple: isMultiple, + id: `select-${controlKey}`, + value: controlValue, + options: Object.keys(options), + getOptionLabel: optionKey => options[optionKey], + sx: { + flex: 1 + }, + ChipProps: { + sx: { + '&.MuiAutocomplete-tag': { + maxWidth: '100px' + } + } + }, + renderInput: params => /*#__PURE__*/React.createElement(_ui.TextField, (0, _extends2.default)({ + error: shouldShowError, + helperText: errorMessage + }, params, { + placeholder: label, + color: "secondary" + })), + ListboxProps: { + sx: { + maxHeight: 280 + } + }, + size: "small", + onChange: (_event, newValues) => handleChangeOption(formatValue(newValues)), + renderOption: (optionProps, option) => /*#__PURE__*/React.createElement(_ui.Typography, (0, _extends2.default)({ + component: "li" + }, optionProps), /*#__PURE__*/React.createElement(_conditionSelectOption.default, { + component: "span", + variant: "inherit", + noWrap: true, + controlCount: controlCount + }, options[option])), + forcePopupIcon: !Object.keys(options).length <= 1 + }); +}; +AutocompleteControl.propTypes = { + conditions: PropTypes.object.isRequired, + condition: PropTypes.object.isRequired, + controlKey: PropTypes.string.isRequired, + onChangeOption: PropTypes.func.isRequired, + value: PropTypes.array.isRequired, + options: PropTypes.object.isRequired, + errorMessage: PropTypes.string.isRequired, + shouldShowError: PropTypes.bool.isRequired, + isMultiple: PropTypes.bool.isRequired, + optionsStyles: PropTypes.object.isRequired, + menuStyles: PropTypes.object.isRequired, + controlCount: PropTypes.number.isRequired +}; +var _default = exports["default"] = AutocompleteControl; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/controls/date-picker-control.js": +/*!*************************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/controls/date-picker-control.js ***! + \*************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var React = _react; +var PropTypes = _interopRequireWildcard(__webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _dayjs = _interopRequireDefault(__webpack_require__(/*! dayjs */ "../node_modules/dayjs/dayjs.min.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const dateFormat = 'MM-DD-YYYY'; +const formattedValue = dateString => { + return (0, _dayjs.default)(dateString, dateFormat, true).isValid() ? (0, _dayjs.default)(dateString, dateFormat) : null; +}; +const DatePickerControl = ({ + condition, + onChangeOption, + controlKey, + value, + shouldShowError, + errorMessage +}) => { + const [controlValue, setControlValue] = (0, _react.useState)(formattedValue(value)); + (0, _react.useEffect)(() => { + setControlValue(formattedValue(value)); + }, [condition]); + const handleChangeOption = newValue => { + if ((0, _dayjs.default)(newValue, dateFormat, true).isValid()) { + onChangeOption(newValue.format(dateFormat)); + setControlValue(formattedValue(newValue)); + } else { + onChangeOption(''); + } + }; + return /*#__PURE__*/React.createElement(_ui.DatePicker, { + value: controlValue, + sx: { + flex: 1 + }, + id: `select-${controlKey}`, + slotProps: { + openPickerButton: { + size: 'small' + }, + textField: { + size: 'small', + color: 'secondary', + error: shouldShowError, + helperText: errorMessage + } + }, + onChange: newValue => handleChangeOption(newValue) + }); +}; +DatePickerControl.propTypes = { + condition: PropTypes.object.isRequired, + controlKey: PropTypes.string.isRequired, + onChangeOption: PropTypes.func.isRequired, + value: PropTypes.string, + errorMessage: PropTypes.string.isRequired, + shouldShowError: PropTypes.bool.isRequired +}; +var _default = exports["default"] = DatePickerControl; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/controls/query-control.js": +/*!*******************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/controls/query-control.js ***! + \*******************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _extends2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/extends */ "../node_modules/@babel/runtime/helpers/extends.js")); +var PropTypes = _interopRequireWildcard(__webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js")); +var _i18n = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +var _conditionsContext = __webpack_require__(/*! ../../contexts/conditions-context */ "../modules/display-conditions/assets/js/editor/contexts/conditions-context.js"); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _utils = __webpack_require__(/*! elementor-pro-app/utils */ "../core/app/assets/js/utils.js"); +var _conditionSelectOption = _interopRequireDefault(__webpack_require__(/*! ./ui/condition-select-option */ "../modules/display-conditions/assets/js/editor/components/controls/ui/condition-select-option.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const formatValue = valueToFormat => { + return Array.isArray(valueToFormat) ? valueToFormat : [valueToFormat]; +}; +const QueryControl = ({ + conditions, + condition, + control, + controlKey, + onChangeOption, + value, + shouldShowError, + errorMessage, + isMultiple, + controlCount +}) => { + const { + fetchData + } = (0, _react.useContext)(_conditionsContext.ConditionsContext), + [controlValue, setControlValue] = (0, _react.useState)(formatValue(value)), + [options, setOptions] = (0, _react.useState)([]), + [loading, setLoading] = (0, _react.useState)(false), + label = controlValue?.length ? '' : conditions[condition.condition].label || ''; + (0, _react.useEffect)(() => { + setControlValue(formatValue(value)); + }, [condition]); + const handleSearchInputChange = async (event, newInputValue, selectedValues) => { + if ('' === newInputValue) { + setOptions([]); + return; + } + setLoading(true); + const results = await fetchData(newInputValue, control); + + // Filter out options that are already selected + const filteredResults = results.filter(option => { + option.text = (0, _utils.htmlDecodeTextContent)(option.text); + return !selectedValues.some(selectedOption => selectedOption?.id === option?.id); + }); + setOptions(filteredResults); + setLoading(false); + }; + const handleChangeOption = newValue => { + onChangeOption(newValue); + setControlValue(newValue); + }; + return /*#__PURE__*/_react.default.createElement(_ui.Autocomplete, { + multiple: isMultiple, + id: `select-${controlKey}`, + value: controlValue, + options: options, + getOptionLabel: option => option ? option.text : '', + isOptionEqualToValue: (option, optionToCompare) => option.id === optionToCompare.id, + filterOptions: x => x, + noOptionsText: (0, _i18n.__)('No results', 'elementor-pro'), + loading: loading, + loadingText: (0, _i18n.__)('Searching...', 'elementor-pro'), + size: "small", + sx: { + flex: 1 + }, + ChipProps: { + sx: { + '&.MuiAutocomplete-tag': { + maxWidth: '100px' + } + } + }, + renderInput: params => /*#__PURE__*/_react.default.createElement(_ui.TextField, (0, _extends2.default)({}, params, { + placeholder: label, + color: "secondary", + error: shouldShowError, + helperText: errorMessage, + InputProps: { + ...params.InputProps, + endAdornment: /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, loading ? /*#__PURE__*/_react.default.createElement(_ui.CircularProgress, { + color: "inherit", + size: 20 + }) : null, params.InputProps.endAdornment) + } + })), + ListboxProps: { + sx: { + maxHeight: 280 + } + }, + onChange: (_event, newValues) => handleChangeOption(newValues), + onInputChange: (event, newInputValue) => handleSearchInputChange(event, newInputValue, controlValue), + renderOption: (optionProps, option) => /*#__PURE__*/_react.default.createElement(_ui.Typography, (0, _extends2.default)({ + component: "li" + }, optionProps), /*#__PURE__*/_react.default.createElement(_conditionSelectOption.default, { + component: "span", + variant: "inherit", + noWrap: true, + controlCount: controlCount + }, option.text)) + }); +}; +QueryControl.propTypes = { + conditions: PropTypes.object.isRequired, + condition: PropTypes.object.isRequired, + onChangeOption: PropTypes.func.isRequired, + controlKey: PropTypes.string.isRequired, + control: PropTypes.object.isRequired, + value: PropTypes.array.isRequired, + errorMessage: PropTypes.string.isRequired, + shouldShowError: PropTypes.bool.isRequired, + isMultiple: PropTypes.bool.isRequired, + controlCount: PropTypes.number.isRequired +}; +var _default = exports["default"] = QueryControl; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/controls/select-control.js": +/*!********************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/controls/select-control.js ***! + \********************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var React = _react; +var PropTypes = _interopRequireWildcard(__webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _conditionSelectControl = _interopRequireDefault(__webpack_require__(/*! ./ui/condition-select-control */ "../modules/display-conditions/assets/js/editor/components/controls/ui/condition-select-control.js")); +var _conditionSelectOption = _interopRequireDefault(__webpack_require__(/*! ./ui/condition-select-option */ "../modules/display-conditions/assets/js/editor/components/controls/ui/condition-select-option.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const SelectControl = ({ + condition, + control, + controlKey, + onChangeOption, + options, + value, + controlCount +}) => { + const [controlValue, setControlValue] = (0, _react.useState)(value); + (0, _react.useEffect)(() => { + setControlValue(value); + }, [condition]); + const handleChangeOption = newValue => { + onChangeOption(newValue); + setControlValue(newValue); + }; + const getOptions = () => { + return Object.entries(options).map(([optionKey, optionValue]) => { + if ('group' === optionValue.type) { + return /*#__PURE__*/React.createElement(_ui.ListSubheader, { + key: optionKey + }, /*#__PURE__*/React.createElement(_conditionSelectOption.default, { + variant: 'inherit', + controlCount: controlCount + }, optionValue.label)); + } + const isDisabled = control?.disabled_options?.includes(optionKey); + return /*#__PURE__*/React.createElement(_ui.MenuItem, { + key: optionKey, + value: optionKey, + disabled: isDisabled, + className: isDisabled && 'hidden' === control?.disabled_type ? 'elementor-hidden' : '' + }, /*#__PURE__*/React.createElement(_conditionSelectOption.default, { + controlCount: controlCount + }, optionValue)); + }); + }; + return /*#__PURE__*/React.createElement(_conditionSelectControl.default, { + id: `select-${controlKey}`, + value: controlValue, + onChange: event => handleChangeOption(event.target.value), + disabled: Object.keys(options).length <= 1, + controlCount: controlCount + }, getOptions()); +}; +SelectControl.propTypes = { + condition: PropTypes.object.isRequired, + control: PropTypes.object.isRequired, + controlKey: PropTypes.string.isRequired, + onChangeOption: PropTypes.func.isRequired, + options: PropTypes.object.isRequired, + value: PropTypes.string.isRequired, + controlCount: PropTypes.number.isRequired +}; +var _default = exports["default"] = SelectControl; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/controls/text-field-control.js": +/*!************************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/controls/text-field-control.js ***! + \************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _extends2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/extends */ "../node_modules/@babel/runtime/helpers/extends.js")); +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var React = _react; +var PropTypes = _interopRequireWildcard(__webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _utils = __webpack_require__(/*! ../../utils/utils */ "../modules/display-conditions/assets/js/editor/utils/utils.js"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const TextFieldControl = ({ + condition, + controlKey, + control, + onChangeOption, + value, + errorMessage, + shouldShowError, + placeholder, + disabled +}) => { + const [controlValue, setControlValue] = (0, _react.useState)(value), + { + step = 1, + min = 0, + variant = null + } = control; + const numericProps = 'number' === variant ? { + type: 'number', + inputProps: { + step, + min + } + } : {}; + (0, _react.useEffect)(() => { + setControlValue(value); + }, [condition]); + const handleChangeOption = (newValue, controlVariant) => { + let integerValue = null; + if ('number' === controlVariant && (0, _utils.hasDecimalSeparator)(newValue)) { + integerValue = Math.floor(parseFloat(newValue)); + } + onChangeOption(integerValue ?? newValue.trim()); + setControlValue(integerValue ?? newValue); + }; + return /*#__PURE__*/React.createElement(_ui.TextField, (0, _extends2.default)({}, numericProps, { + sx: { + flex: 1 + }, + error: shouldShowError, + helperText: errorMessage, + value: controlValue, + id: `text-${controlKey}`, + variant: "outlined", + onChange: event => handleChangeOption(event.target.value, variant), + size: "small", + color: "secondary", + placeholder: placeholder, + disabled: disabled ?? false + })); +}; +TextFieldControl.propTypes = { + condition: PropTypes.object.isRequired, + controlKey: PropTypes.string.isRequired, + control: PropTypes.object.isRequired, + onChangeOption: PropTypes.func.isRequired, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + errorMessage: PropTypes.string.isRequired, + shouldShowError: PropTypes.bool.isRequired, + placeholder: PropTypes.string.isRequired, + disabled: PropTypes.bool +}; +var _default = exports["default"] = TextFieldControl; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/controls/time-picker-control.js": +/*!*************************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/controls/time-picker-control.js ***! + \*************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var React = _react; +var PropTypes = _interopRequireWildcard(__webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _dayjs = _interopRequireDefault(__webpack_require__(/*! dayjs */ "../node_modules/dayjs/dayjs.min.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const timeFormat = 'HH:mm', + dateFormat = 'MM-DD-YYYY ' + timeFormat; +const formattedValue = dateString => { + return (0, _dayjs.default)(dateString, timeFormat, true).isValid() ? (0, _dayjs.default)(dateString, timeFormat) : null; +}; +const TimePickerControl = ({ + condition, + controlKey, + onChangeOption, + value, + shouldShowError, + errorMessage +}) => { + const lastInputValue = (0, _react.useRef)(formattedValue(value)), + [controlValue, setControlValue] = (0, _react.useState)(lastInputValue.current); + (0, _react.useEffect)(() => { + setControlValue(lastInputValue.current); + }, [condition]); + const handleChangeOption = newValue => { + const dateString = (0, _dayjs.default)(newValue, dateFormat, true).isValid() ? newValue.format(dateFormat) : ''; + onChangeOption(dateString); + lastInputValue.current = newValue; + setControlValue(newValue); + }; + return /*#__PURE__*/React.createElement(_ui.TimePicker, { + sx: { + flex: 1 + }, + id: `select-${controlKey}`, + value: controlValue, + slotProps: { + textField: { + size: 'small', + error: shouldShowError, + helperText: errorMessage + } + }, + onChange: newValue => handleChangeOption(newValue) + }); +}; +TimePickerControl.propTypes = { + condition: PropTypes.object.isRequired, + control: PropTypes.object.isRequired, + controlKey: PropTypes.string.isRequired, + onChangeOption: PropTypes.func.isRequired, + value: PropTypes.string, + errorMessage: PropTypes.string.isRequired, + shouldShowError: PropTypes.bool.isRequired +}; +var _default = exports["default"] = TimePickerControl; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/controls/ui/condition-select-control.js": +/*!*********************************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/controls/ui/condition-select-control.js ***! + \*********************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); +var _extends2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/extends */ "../node_modules/@babel/runtime/helpers/extends.js")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _utils = __webpack_require__(/*! ../../../utils/utils */ "../modules/display-conditions/assets/js/editor/utils/utils.js"); +var PropTypes = _interopRequireWildcard(__webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const ConditionSelect = ({ + controlCount, + ...props +}) => /*#__PURE__*/_react.default.createElement(_ui.Select, (0, _extends2.default)({}, props, { + size: "small", + sx: { + flex: 1, + textAlign: 'start', + alignSelf: 'flex-start', + '.MuiSelect-select .MuiTypography-root': { + maxWidth: (0, _utils.getControlValueMaxWidth)(controlCount) + } + }, + color: "secondary", + MenuProps: { + PaperProps: { + sx: { + maxHeight: 280, + '& .MuiListSubheader-root': { + position: 'initial' + } + } + }, + classes: { + paper: 'e-conditions-select-menu' + } + } +})); +ConditionSelect.propTypes = { + controlCount: PropTypes.number.isRequired +}; +var _default = exports["default"] = ConditionSelect; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/controls/ui/condition-select-option.js": +/*!********************************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/controls/ui/condition-select-option.js ***! + \********************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); +var _extends2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/extends */ "../node_modules/@babel/runtime/helpers/extends.js")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _utils = __webpack_require__(/*! ../../../utils/utils */ "../modules/display-conditions/assets/js/editor/utils/utils.js"); +var PropTypes = _interopRequireWildcard(__webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const ConditionSelectOption = ({ + controlCount, + sx = {}, + ...props +}) => /*#__PURE__*/_react.default.createElement(_ui.Typography, (0, _extends2.default)({ + variant: "inherit", + noWrap: true +}, props, { + sx: { + maxWidth: (0, _utils.getSelectOptionMaxWidth)(controlCount), + ...sx + } +})); +ConditionSelectOption.propTypes = { + sx: PropTypes.object, + isDropdownItem: PropTypes.bool, + controlCount: PropTypes.number.isRequired +}; +var _default = exports["default"] = ConditionSelectOption; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/footer.js": +/*!***************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/footer.js ***! + \***************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; +/* provided dependency */ var PropTypes = __webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js"); + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var React = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const Footer = ({ + onClickSaveButton, + isButtonDisabled +}) => { + return /*#__PURE__*/React.createElement(_ui.Stack, { + direction: "row", + justifyContent: "flex-end", + sx: { + py: 1, + px: 3 + } + }, /*#__PURE__*/React.createElement(_ui.Button, { + variant: "contained", + className: "save-and-close-button", + disabled: !isButtonDisabled, + onClick: onClickSaveButton + }, __('Save & Close', 'elementor-pro'))); +}; +Footer.propTypes = { + onClickSaveButton: PropTypes.func, + isButtonDisabled: PropTypes.bool.isRequired +}; +var _default = exports["default"] = Footer; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/header.js": +/*!***************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/header.js ***! + \***************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); +var PropTypes = _interopRequireWildcard(__webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js")); +var _i18n = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _elementorLogo = _interopRequireDefault(__webpack_require__(/*! ./icons/elementor-logo */ "../modules/display-conditions/assets/js/editor/components/icons/elementor-logo.js")); +var _icons = __webpack_require__(/*! @elementor/icons */ "@elementor/icons"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const Header = ({ + onClose +}) => { + return /*#__PURE__*/_react.default.createElement(_ui.AppBar, { + sx: { + fontWeight: 'normal' + }, + color: "transparent", + position: "relative" + }, /*#__PURE__*/_react.default.createElement(_ui.Toolbar, { + variant: "dense" + }, /*#__PURE__*/_react.default.createElement(_elementorLogo.default, { + sx: { + mr: 1 + } + }), /*#__PURE__*/_react.default.createElement(_ui.Typography, { + component: "span", + variant: "subtitle2", + sx: { + fontWeight: 'bold', + textTransform: 'uppercase' + } + }, (0, _i18n.__)('Display Conditions', 'elementor-pro')), /*#__PURE__*/_react.default.createElement(_ui.Stack, { + direction: "row", + spacing: 1, + alignItems: "center", + sx: { + ml: 'auto' + } + }, /*#__PURE__*/_react.default.createElement(_ui.IconButton, { + size: "small", + "aria-label": (0, _i18n.__)('Close', 'elementor-pro'), + onClick: onClose, + sx: { + '&.MuiButtonBase-root': { + mr: -1 + } + } + }, /*#__PURE__*/_react.default.createElement(_icons.XIcon, null))))); +}; +Header.propTypes = { + onClose: PropTypes.func.isRequired +}; +var _default = exports["default"] = Header; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/icons/elementor-logo.js": +/*!*****************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/icons/elementor-logo.js ***! + \*****************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _extends2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/extends */ "../node_modules/@babel/runtime/helpers/extends.js")); +var React = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const ElementorLogo = props => { + return /*#__PURE__*/React.createElement(_ui.SvgIcon, (0, _extends2.default)({ + viewBox: "0 0 32 32" + }, props), /*#__PURE__*/React.createElement("path", { + fillRule: "evenodd", + clipRule: "evenodd", + d: "M2.69648 24.8891C0.938383 22.2579 0 19.1645 0 16C0 11.7566 1.68571 7.68687 4.68629 4.68629C7.68687 1.68571 11.7566 0 16 0C19.1645 0 22.2579 0.938383 24.8891 2.69648C27.5203 4.45459 29.5711 6.95344 30.7821 9.87706C31.9931 12.8007 32.3099 16.0177 31.6926 19.1214C31.0752 22.2251 29.5514 25.0761 27.3137 27.3137C25.0761 29.5514 22.2251 31.0752 19.1214 31.6926C16.0177 32.3099 12.8007 31.9931 9.87706 30.7821C6.95344 29.5711 4.45459 27.5203 2.69648 24.8891ZM12.0006 9.33281H9.33437V22.6665H12.0006V9.33281ZM22.6657 9.33281H14.6669V11.9991H22.6657V9.33281ZM22.6657 14.6654H14.6669V17.3316H22.6657V14.6654ZM22.6657 20.0003H14.6669V22.6665H22.6657V20.0003Z" + })); +}; +var _default = exports["default"] = ElementorLogo; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/icons/hierarchy-icon.js": +/*!*****************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/icons/hierarchy-icon.js ***! + \*****************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _extends2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/extends */ "../node_modules/@babel/runtime/helpers/extends.js")); +var React = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const UnstyledHierarchyIcon = React.forwardRef((props, ref) => { + return /*#__PURE__*/React.createElement(_ui.SvgIcon, (0, _extends2.default)({ + viewBox: "0 0 24 24" + }, props, { + ref: ref + }), /*#__PURE__*/React.createElement("path", { + fillRule: "evenodd", + clipRule: "evenodd", + d: "M11 3.75C10.3096 3.75 9.75 4.30964 9.75 5V7C9.75 7.69036 10.3096 8.25 11 8.25H13C13.6904 8.25 14.25 7.69036 14.25 7V5C14.25 4.30964 13.6904 3.75 13 3.75H11ZM12.75 9.75H13C14.5188 9.75 15.75 8.51878 15.75 7V5C15.75 3.48122 14.5188 2.25 13 2.25H11C9.48122 2.25 8.25 3.48122 8.25 5V7C8.25 8.51878 9.48122 9.75 11 9.75H11.25V11.25H8C7.27065 11.25 6.57118 11.5397 6.05546 12.0555C5.53973 12.5712 5.25 13.2707 5.25 14V14.25H5C3.48122 14.25 2.25 15.4812 2.25 17V19C2.25 20.5188 3.48122 21.75 5 21.75H7C8.51878 21.75 9.75 20.5188 9.75 19V17C9.75 15.4812 8.51878 14.25 7 14.25H6.75V14C6.75 13.6685 6.8817 13.3505 7.11612 13.1161C7.35054 12.8817 7.66848 12.75 8 12.75H16C16.3315 12.75 16.6495 12.8817 16.8839 13.1161C17.1183 13.3505 17.25 13.6685 17.25 14V14.25H17C15.4812 14.25 14.25 15.4812 14.25 17V19C14.25 20.5188 15.4812 21.75 17 21.75H19C20.5188 21.75 21.75 20.5188 21.75 19V17C21.75 15.4812 20.5188 14.25 19 14.25H18.75V14C18.75 13.2707 18.4603 12.5712 17.9445 12.0555C17.4288 11.5397 16.7293 11.25 16 11.25H12.75V9.75ZM17 15.75C16.3096 15.75 15.75 16.3096 15.75 17V19C15.75 19.6904 16.3096 20.25 17 20.25H19C19.6904 20.25 20.25 19.6904 20.25 19V17C20.25 16.3096 19.6904 15.75 19 15.75H17ZM5 15.75C4.30964 15.75 3.75 16.3096 3.75 17V19C3.75 19.6904 4.30964 20.25 5 20.25H7C7.69036 20.25 8.25 19.6904 8.25 19V17C8.25 16.3096 7.69036 15.75 7 15.75H5Z" + })); +}); +const HierarchyIcon = (0, _ui.styled)(UnstyledHierarchyIcon)(({ + theme +}) => ({ + '& path': { + fill: theme.palette.text.primary + } +})); +var _default = exports["default"] = HierarchyIcon; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/or-row-group.js": +/*!*********************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/or-row-group.js ***! + \*********************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; +/* provided dependency */ var PropTypes = __webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js"); + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _icons = __webpack_require__(/*! @elementor/icons */ "@elementor/icons"); +var _conditionsSelectors = _interopRequireDefault(__webpack_require__(/*! ./conditions-selectors */ "../modules/display-conditions/assets/js/editor/components/conditions-selectors.js")); +var _useConditions = _interopRequireDefault(__webpack_require__(/*! ../hooks/use-conditions */ "../modules/display-conditions/assets/js/editor/hooks/use-conditions.js")); +var _utils = __webpack_require__(/*! ../utils/utils */ "../modules/display-conditions/assets/js/editor/utils/utils.js"); +var _conditionsOrDivider = _interopRequireDefault(__webpack_require__(/*! ./ui/conditions-or-divider */ "../modules/display-conditions/assets/js/editor/components/ui/conditions-or-divider.js")); +var _constants = __webpack_require__(/*! ../utils/constants */ "../modules/display-conditions/assets/js/editor/utils/constants.js"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const OrRowGroup = ({ + showConditions, + setShowConditions +}) => { + const { + selectedConditions, + conditionsConfig, + dispatch + } = (0, _useConditions.default)(), + { + conditions: availableConditions, + conditionsByGroup + } = conditionsConfig, + addButtonText = selectedConditions.length ? __('Add condition group', 'elementor-pro') : __('Add Condition', 'elementor-pro'); + const addOrCondition = () => { + const conditionKey = (0, _utils.getDefaultActiveCondition)(conditionsByGroup), + defaultValues = (0, _utils.getConditionInitialState)(availableConditions, conditionKey), + andCondition = { + condition: conditionKey, + ...defaultValues + }; + dispatch({ + type: _constants.ACTION_TYPES.ADD_OR_CONDITION, + andCondition + }); + setShowConditions(true); + }; + return /*#__PURE__*/_react.default.createElement(_ui.Box, null, showConditions && selectedConditions.map((orCondition, orConditionIndex) => /*#__PURE__*/_react.default.createElement(_react.Fragment, { + key: orConditionIndex + }, orConditionIndex > 0 && /*#__PURE__*/_react.default.createElement(_conditionsOrDivider.default, null), /*#__PURE__*/_react.default.createElement(_conditionsSelectors.default, { + orConditionIndex: orConditionIndex + }))), /*#__PURE__*/_react.default.createElement(_ui.Button, { + variant: "contained", + className: "add-or-condition-button", + color: "secondary", + startIcon: /*#__PURE__*/_react.default.createElement(_icons.PlusIcon, null), + sx: { + mt: 1, + mb: 5 + }, + onClick: () => addOrCondition() + }, addButtonText)); +}; +OrRowGroup.propTypes = { + showConditions: PropTypes.bool.isRequired, + setShowConditions: PropTypes.func.isRequired +}; +var _default = exports["default"] = OrRowGroup; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/ui/conditions-or-divider.js": +/*!*********************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/ui/conditions-or-divider.js ***! + \*********************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +const OrDivider = () => { + return /*#__PURE__*/_react.default.createElement(_ui.Divider, { + sx: { + px: 3 + } + }, __('OR', 'elementor-pro')); +}; +var _default = exports["default"] = OrDivider; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/components/ui/row-controls.js": +/*!************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/components/ui/row-controls.js ***! + \************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var PropTypes = __webpack_require__(/*! prop-types */ "../node_modules/prop-types/index.js"); + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _ui = __webpack_require__(/*! @elementor/ui */ "@elementor/ui"); +var _i18n = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +var _icons = __webpack_require__(/*! @elementor/icons */ "@elementor/icons"); +var React = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _utils = __webpack_require__(/*! ../../utils/utils */ "../modules/display-conditions/assets/js/editor/utils/utils.js"); +var _useConditions = _interopRequireDefault(__webpack_require__(/*! ../../hooks/use-conditions */ "../modules/display-conditions/assets/js/editor/hooks/use-conditions.js")); +var _constants = __webpack_require__(/*! ../../utils/constants */ "../modules/display-conditions/assets/js/editor/utils/constants.js"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +const RowControls = ({ + orConditionIndex, + andConditionIndex +}) => { + const { + conditionsConfig, + dispatch + } = (0, _useConditions.default)(), + { + conditions: availableConditions, + conditionsByGroup + } = conditionsConfig; + const addRepeaterRow = () => { + const conditionKey = (0, _utils.getDefaultActiveCondition)(conditionsByGroup), + defaultValues = (0, _utils.getConditionInitialState)(availableConditions, conditionKey), + andCondition = { + condition: conditionKey, + ...defaultValues + }; + dispatch({ + type: _constants.ACTION_TYPES.ADD_AND_CONDITION, + andCondition, + andConditionIndex, + orConditionIndex + }); + }; + const removeRepeaterRow = () => { + dispatch({ + type: _constants.ACTION_TYPES.REMOVE_AND_CONDITION, + andConditionIndex, + orConditionIndex + }); + }; + return /*#__PURE__*/React.createElement(_ui.Stack, { + direction: "row", + alignItems: "center", + sx: { + left: '100%', + gap: .5, + ml: -1, + mt: '2.5px', + position: 'absolute' + } + }, /*#__PURE__*/React.createElement(_ui.Button, { + color: "secondary", + variant: "outlined", + sx: { + px: 1, + minWidth: 'unset' + }, + className: "add-single-condition-button", + onClick: addRepeaterRow + }, (0, _i18n.__)('AND', 'elementor-pro')), /*#__PURE__*/React.createElement(_ui.IconButton, { + color: "secondary", + "aria-label": (0, _i18n.__)('Delete', 'elementor-pro'), + className: "remove-single-condition-button", + onClick: removeRepeaterRow + }, /*#__PURE__*/React.createElement(_icons.XIcon, { + fontSize: "small" + }))); +}; +RowControls.propTypes = { + andConditionIndex: PropTypes.number.isRequired, + orConditionIndex: PropTypes.number.isRequired +}; +var _default = exports["default"] = RowControls; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/contexts/conditions-context.js": +/*!*************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/contexts/conditions-context.js ***! + \*************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var React = __webpack_require__(/*! react */ "react"); + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.ConditionsContext = void 0; +const ConditionsContext = exports.ConditionsContext = React.createContext(); + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/hooks/use-conditions.js": +/*!******************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/hooks/use-conditions.js ***! + \******************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = __webpack_require__(/*! react */ "react"); +var _conditionsContext = __webpack_require__(/*! ../contexts/conditions-context */ "../modules/display-conditions/assets/js/editor/contexts/conditions-context.js"); +function useConditions() { + return (0, _react.useContext)(_conditionsContext.ConditionsContext); +} +var _default = exports["default"] = useConditions; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/module.js": +/*!****************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/module.js ***! + \****************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +__webpack_require__(/*! core-js/modules/es.array.push.js */ "../node_modules/core-js/modules/es.array.push.js"); +var _behavior = _interopRequireDefault(__webpack_require__(/*! ./behavior */ "../modules/display-conditions/assets/js/editor/behavior.js")); +class Module extends elementorModules.editor.utils.Module { + onElementorInit() { + elementor.hooks.addFilter('controls/base/behaviors', this.registerControlBehavior); + elementor.channels.editor.on('section:activated', this.highlightIconIfFilled); + } + registerControlBehavior = (behaviors, view) => { + if ('e_display_conditions_trigger' !== view.options.model.get('name')) { + return behaviors; + } + if (!behaviors) { + behaviors = {}; + } + const conditionsByGroup = this._getGroupedConditionKeys(elementor.config.displayConditions || {}); + const flattenedConditionOptions = this._getFlattenedConditionOptions(conditionsByGroup); + behaviors.displayConditions = { + behaviorClass: _behavior.default, + getControlValue: () => { + const controlView = this.getEditorControlView('e_display_conditions'); + if (!controlView) { + return []; + } + return this._getStructuredConditions(JSON.parse(controlView.getControlValue() || '[]')); + }, + setControlValue: value => { + const displayConditionsInput = this.getEditorControlView('e_display_conditions'), + displayConditionsTemplate = this.getEditorControlView('e_display_conditions_trigger'); + if (displayConditionsInput) { + displayConditionsInput.setValue(value); + displayConditionsInput.applySavedValue(); + } + if (displayConditionsTemplate.$el) { + const icon = displayConditionsTemplate.$el.find('.eicon-flow.e-control-display-conditions'); + this.highlightIcon(icon, displayConditionsInput); + } + }, + fetchData: async (value, control) => { + const response = await this.doAjaxRequest('pro_panel_posts_control_filter_autocomplete', { + autocomplete: control.autocomplete, + q: value + }); + return response?.results ?? []; + }, + setCacheNoticeStatus: async () => { + const response = await this.doAjaxRequest('display_conditions_set_cache_notice_status'); + if (response) { + elementor.config.displayConditions.show_cache_notice = false; + } + return response; + }, + conditionsConfig: { + ...elementor.config.displayConditions, + conditionsByGroup, + flattenedConditionOptions + } + }; + return behaviors; + }; + highlightIconIfFilled = (sectionName, editor) => { + const advancedSections = ['section_advanced', + // Sections / Columns + '_section_style', + // Widgets + 'section_layout' // Containers + ]; + + if (!advancedSections.includes(sectionName)) { + return; + } + const controlView = this.getEditorControlView('e_display_conditions'); + if (!controlView) { + return; + } + const icon = editor.$childViewContainer.find('.eicon-flow.e-control-display-conditions'); + this.highlightIcon(icon, controlView); + }; + highlightIcon = (icon, controlView) => { + if (!icon[0]) { + return; + } + const conditionValue = controlView.getControlValue() || '[]', + conditionArray = '[]' !== conditionValue ? this._getStructuredConditions(JSON.parse(conditionValue)) : []; + if (!conditionArray.length) { + icon[0]?.classList?.remove('filled'); + } else { + icon[0]?.classList?.add('filled'); + } + }; + doAjaxRequest = (action, data) => { + try { + return new Promise((resolve, reject) => { + elementorCommon.ajax.addRequest(action, { + data, + error: () => reject(), + success: res => { + resolve(res); + } + }); + }); + } catch (error) { + return false; + } + }; + _getStructuredConditions = conditions => { + return this._shouldConvertConditionsStructure(conditions) ? [conditions] : conditions; + }; + _shouldConvertConditionsStructure = conditions => { + return conditions.length && !Array.isArray(conditions[0]); + }; + _getGroupedConditionKeys = conditionsConfig => { + return Object.keys(conditionsConfig?.groups || {}).reduce((group, groupName) => { + const conditions = this._getConditionKeyByGroup(conditionsConfig.conditions, groupName); + if (conditions.length) { + group[groupName] = conditions; + } + return group; + }, {}); + }; + _getConditionKeyByGroup = (conditions, groupName) => { + return Object.keys(conditions).filter(conditionKey => groupName === conditions[conditionKey].group); + }; + _getFlattenedConditionOptions = conditionsByGroup => { + const { + conditions = {}, + groups = {} + } = elementor.config.displayConditions || {}; + return Object.entries(conditionsByGroup).reduce((optionList, [groupName, conditionKeys]) => { + const relevantConditions = conditionKeys.map(key => ({ + key, + label: conditions[key].label, + isGroup: false + })); + optionList.push({ + key: groupName, + label: groups[groupName].label, + isGroup: true + }, ...relevantConditions); + return optionList; + }, []); + }; +} +exports["default"] = Module; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/reducers/conditions-reducer.js": +/*!*************************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/reducers/conditions-reducer.js ***! + \*************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.conditionsReducer = void 0; +__webpack_require__(/*! core-js/modules/es.array.push.js */ "../node_modules/core-js/modules/es.array.push.js"); +var _constants = __webpack_require__(/*! ../utils/constants */ "../modules/display-conditions/assets/js/editor/utils/constants.js"); +const conditionsReducer = (state, action) => { + switch (action.type) { + case _constants.ACTION_TYPES.CHANGE_CONDITION_TYPE: + return { + ...state, + selectedConditions: _changeConditionType({ + ...state, + ...action + }) + }; + case _constants.ACTION_TYPES.CHANGE_CONTROL_VALUE: + return { + ...state, + selectedConditions: _changeControlValue({ + ...state, + ...action + }) + }; + case _constants.ACTION_TYPES.ADD_AND_CONDITION: + return { + ...state, + selectedConditions: _addAndCondition({ + ...state, + ...action + }) + }; + case _constants.ACTION_TYPES.ADD_OR_CONDITION: + return { + ...state, + selectedConditions: [...state.selectedConditions, [action.andCondition]] + }; + case _constants.ACTION_TYPES.REMOVE_AND_CONDITION: + return { + ...state, + selectedConditions: _removeAndCondition({ + ...state, + ...action + }) + }; + case _constants.ACTION_TYPES.REMOVE_OR_CONDITION: + return { + ...state, + selectedConditions: state.selectedConditions.filter((_, index) => index !== action.orConditionIndex) + }; + case _constants.ACTION_TYPES.SET_ERRORS: + return { + ...state, + selectedConditions: _setErrors({ + ...state, + ...action + }) + }; + default: + return state; + } +}; +exports.conditionsReducer = conditionsReducer; +const _changeConditionType = ({ + selectedConditions, + conditionToChange, + orConditionIndex, + andConditionIndex +}) => { + const newOrCondition = selectedConditions[orConditionIndex].map((andCondition, index) => index === andConditionIndex ? conditionToChange : { + ...andCondition + }); + return selectedConditions.map((orCondition, index) => index === orConditionIndex ? newOrCondition : [...orCondition]); +}; +const _changeControlValue = ({ + selectedConditions, + orConditionIndex, + andConditionIndex, + controlKey, + value +}) => { + const existingOrCondition = [...selectedConditions[orConditionIndex]], + existingAndCondition = { + ...existingOrCondition[andConditionIndex] + }; + const newAndCondition = { + ...existingAndCondition, + [controlKey]: value + }, + newOrCondition = existingOrCondition.map((andCondition, index) => index === andConditionIndex ? newAndCondition : { + ...andCondition + }); + return selectedConditions.map((orCondition, index) => index === orConditionIndex ? newOrCondition : [...orCondition]); +}; +const _addAndCondition = ({ + selectedConditions, + orConditionIndex, + andConditionIndex, + andCondition +}) => { + const existingOrCondition = selectedConditions[orConditionIndex], + newOrCondition = existingOrCondition.reduce((newAndConditions, condition, index) => { + newAndConditions.push({ + ...condition + }); + if (index === andConditionIndex || existingOrCondition.length === andConditionIndex && existingOrCondition.length - 1 === index) { + newAndConditions.push(andCondition); + } + return newAndConditions; + }, []); + return selectedConditions.map((orCondition, index) => index === orConditionIndex ? newOrCondition : [...orCondition]); +}; +const _removeAndCondition = ({ + selectedConditions, + orConditionIndex, + andConditionIndex +}) => { + const newOrCondition = selectedConditions[orConditionIndex].reduce((newAndConditions, condition, index) => { + if (index !== andConditionIndex) { + newAndConditions.push({ + ...condition + }); + } + return newAndConditions; + }, []); + return selectedConditions.reduce((newOrConditions, orCondition, index) => { + if (index === orConditionIndex && newOrCondition.length) { + newOrConditions.push(newOrCondition); + } + if (index !== orConditionIndex) { + newOrConditions.push([...orCondition]); + } + return newOrConditions; + }, []); +}; +const _setErrors = ({ + selectedConditions, + orConditionIndex, + andConditionIndex, + errors +}) => { + const newOrCondition = [...selectedConditions[orConditionIndex]], + newAndCondition = { + ...newOrCondition[andConditionIndex] + }; + newAndCondition.errors = { + ...newAndCondition.errors, + ...errors + }; + newOrCondition[andConditionIndex] = newAndCondition; + return selectedConditions.map((orCondition, index) => index === orConditionIndex ? [...newOrCondition] : [...orCondition]); +}; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/utils/constants.js": +/*!*************************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/utils/constants.js ***! + \*************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.DISABLED_CONTROL_CONFIG = exports.DEFAULT_CONTROL_VALUES = exports.CONTROL_TYPES = exports.ACTION_TYPES = void 0; +// These values will match the Controls_Manager +const CONTROL_TYPES = exports.CONTROL_TYPES = { + MULTIPLE_SELECT: 'select2', + SELECT: 'select', + QUERY: 'query', + DATE_TIME: 'date_time', + TEXT_FIELD: 'text' +}; +const DEFAULT_CONTROL_VALUES = exports.DEFAULT_CONTROL_VALUES = { + select2: [], + query: [], + select: '', + text: '', + date_time: null +}; +const ACTION_TYPES = exports.ACTION_TYPES = { + CHANGE_CONTROL_VALUE: 'CHANGE_CONTROL_VALUE', + SET_ERRORS: 'SET_ERRORS', + ADD_OR_CONDITION: 'ADD_OR_CONDITION', + CHANGE_CONDITION_TYPE: 'CHANGE_CONDITION_TYPE', + ADD_AND_CONDITION: 'ADD_AND_CONDITION', + REMOVE_AND_CONDITION: 'REMOVE_AND_CONDITION', + REMOVE_OR_CONDITION: 'REMOVE_OR_CONDITION' +}; +const DISABLED_CONTROL_CONFIG = exports.DISABLED_CONTROL_CONFIG = { + CONDITION_NAME: 'dynamic_tags', + CONTROL_NAME: 'dynamic_tag_value', + COMPARATORS: ['is_empty', 'is_not_empty'] +}; + +/***/ }), + +/***/ "../modules/display-conditions/assets/js/editor/utils/utils.js": +/*!*********************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/utils/utils.js ***! + \*********************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.getControlDefaults = exports.getConditionInitialState = void 0; +exports.getControlValue = getControlValue; +exports.getControlValueMaxWidth = getControlValueMaxWidth; +exports.getDefaultActiveCondition = getDefaultActiveCondition; +exports.getInvalidInputFeedback = getInvalidInputFeedback; +exports.getSelectOptionMaxWidth = getSelectOptionMaxWidth; +exports.hasDecimalSeparator = hasDecimalSeparator; +exports.shouldCastToArray = shouldCastToArray; +exports.shouldDisableControl = shouldDisableControl; +exports.shouldEmptyValuePassValidation = shouldEmptyValuePassValidation; +var _constants = __webpack_require__(/*! ./constants */ "../modules/display-conditions/assets/js/editor/utils/constants.js"); +function shouldCastToArray(controlType) { + return _constants.CONTROL_TYPES.MULTIPLE_SELECT === controlType || _constants.CONTROL_TYPES.QUERY === controlType; +} +function getDefaultActiveCondition(conditionsByGroup) { + return Object.values(conditionsByGroup)[0][0]; +} +function getInvalidInputFeedback(type, variant, value, shouldShow = false) { + return !value?.length ? { + message: _getErrorMessage(type, variant), + shouldShow + } : {}; +} +const getControlDefaults = (controlKey, control) => { + const { + type, + variant = null, + options + } = control, + defaultValue = control?.default || (options && _constants.CONTROL_TYPES.MULTIPLE_SELECT !== type ? Object.keys(options)[0] : _constants.DEFAULT_CONTROL_VALUES[type]), + formattedDefaultValue = shouldCastToArray(type) && !Array.isArray(defaultValue) ? [defaultValue] : defaultValue, + error = getInvalidInputFeedback(type, variant, formattedDefaultValue); + return { + defaultValue: formattedDefaultValue, + error + }; +}; +exports.getControlDefaults = getControlDefaults; +const getConditionInitialState = (conditions, conditionKey) => { + const { + controls = {} + } = conditions?.[conditionKey] || {}; + return Object.keys(controls).reduce((defaults, controlKey) => { + if ('__settings' === controlKey) { + return defaults; + } + const { + defaultValue, + error + } = getControlDefaults(controlKey, controls[controlKey]); + defaults[controlKey] = defaultValue; + defaults.errors[controlKey] = error; + return defaults; + }, { + errors: {} + }); +}; +exports.getConditionInitialState = getConditionInitialState; +function hasDecimalSeparator(newValue) { + if (isNaN(parseFloat(newValue))) { + return false; + } + if (newValue.toString().indexOf('.') !== -1) { + return true; + } + if (newValue.toString().indexOf(',') !== -1) { + return true; + } +} +function getSelectOptionMaxWidth(controlCount) { + return 3 === controlCount ? 200 : 150; +} +function getControlValueMaxWidth(controlCount) { + return 3 === controlCount ? 190 : 135; +} +function getControlValue(value, altValue) { + return 'undefined' !== typeof value ? value : altValue; +} +function _getErrorMessage(controlType, variant = null) { + if (shouldCastToArray(controlType)) { + return __('Select an option', 'elementor-pro'); + } + if (_constants.CONTROL_TYPES.DATE_TIME === controlType) { + return 'time' === variant ? __('Select a time', 'elementor-pro') : __('Select a date', 'elementor-pro'); + } + return __('Enter a value', 'elementor-pro'); +} +function shouldDisableControl(control, comparator) { + return _constants.DISABLED_CONTROL_CONFIG.CONTROL_NAME === control && _constants.DISABLED_CONTROL_CONFIG.COMPARATORS.includes(comparator); +} +function shouldEmptyValuePassValidation(condition, comparator) { + return _constants.DISABLED_CONTROL_CONFIG.CONDITION_NAME === condition && _constants.DISABLED_CONTROL_CONFIG.COMPARATORS.includes(comparator); +} + +/***/ }), + +/***/ "../node_modules/dayjs/dayjs.min.js": +/*!******************************************!*\ + !*** ../node_modules/dayjs/dayjs.min.js ***! + \******************************************/ +/***/ (function(module) { + +!function(t,e){ true?module.exports=e():0}(this,(function(){"use strict";var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",c="month",f="quarter",h="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return"["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return!r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()1)return t(u[0])}else{var a=e.name;D[a]=e,i=a}return!r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,!0),this.parse(t),this.$x=this.$x||t.x||{},this[p]=!0}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init()},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},m.$utils=function(){return b},m.isValid=function(){return!(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t) { + +"use strict"; +/* +object-assign +(c) Sindre Sorhus +@license MIT +*/ + + +/* eslint-disable no-unused-vars */ +var getOwnPropertySymbols = Object.getOwnPropertySymbols; +var hasOwnProperty = Object.prototype.hasOwnProperty; +var propIsEnumerable = Object.prototype.propertyIsEnumerable; + +function toObject(val) { + if (val === null || val === undefined) { + throw new TypeError('Object.assign cannot be called with null or undefined'); + } + + return Object(val); +} + +function shouldUseNative() { + try { + if (!Object.assign) { + return false; + } + + // Detect buggy property enumeration order in older V8 versions. + + // https://bugs.chromium.org/p/v8/issues/detail?id=4118 + var test1 = new String('abc'); // eslint-disable-line no-new-wrappers + test1[5] = 'de'; + if (Object.getOwnPropertyNames(test1)[0] === '5') { + return false; + } + + // https://bugs.chromium.org/p/v8/issues/detail?id=3056 + var test2 = {}; + for (var i = 0; i < 10; i++) { + test2['_' + String.fromCharCode(i)] = i; + } + var order2 = Object.getOwnPropertyNames(test2).map(function (n) { + return test2[n]; + }); + if (order2.join('') !== '0123456789') { + return false; + } + + // https://bugs.chromium.org/p/v8/issues/detail?id=3056 + var test3 = {}; + 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { + test3[letter] = letter; + }); + if (Object.keys(Object.assign({}, test3)).join('') !== + 'abcdefghijklmnopqrst') { + return false; + } + + return true; + } catch (err) { + // We don't expect any of the above to throw, but better to be safe. + return false; + } +} + +module.exports = shouldUseNative() ? Object.assign : function (target, source) { + var from; + var to = toObject(target); + var symbols; + + for (var s = 1; s < arguments.length; s++) { + from = Object(arguments[s]); + + for (var key in from) { + if (hasOwnProperty.call(from, key)) { + to[key] = from[key]; + } + } + + if (getOwnPropertySymbols) { + symbols = getOwnPropertySymbols(from); + for (var i = 0; i < symbols.length; i++) { + if (propIsEnumerable.call(from, symbols[i])) { + to[symbols[i]] = from[symbols[i]]; + } + } + } + } + + return to; +}; + + +/***/ }), + +/***/ "../node_modules/prop-types/checkPropTypes.js": +/*!****************************************************!*\ + !*** ../node_modules/prop-types/checkPropTypes.js ***! + \****************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + + +var printWarning = function() {}; + +if (true) { + var ReactPropTypesSecret = __webpack_require__(/*! ./lib/ReactPropTypesSecret */ "../node_modules/prop-types/lib/ReactPropTypesSecret.js"); + var loggedTypeFailures = {}; + var has = __webpack_require__(/*! ./lib/has */ "../node_modules/prop-types/lib/has.js"); + + printWarning = function(text) { + var message = 'Warning: ' + text; + if (typeof console !== 'undefined') { + console.error(message); + } + try { + // --- Welcome to debugging React --- + // This error was thrown as a convenience so that you can use this stack + // to find the callsite that caused this warning to fire. + throw new Error(message); + } catch (x) { /**/ } + }; +} + +/** + * Assert that the values match with the type specs. + * Error messages are memorized and will only be shown once. + * + * @param {object} typeSpecs Map of name to a ReactPropType + * @param {object} values Runtime values that need to be type-checked + * @param {string} location e.g. "prop", "context", "child context" + * @param {string} componentName Name of the component for error messages. + * @param {?Function} getStack Returns the component stack. + * @private + */ +function checkPropTypes(typeSpecs, values, location, componentName, getStack) { + if (true) { + for (var typeSpecName in typeSpecs) { + if (has(typeSpecs, typeSpecName)) { + var error; + // Prop type validation may throw. In case they do, we don't want to + // fail the render phase where it didn't fail before. So we log it. + // After these have been cleaned up, we'll let them throw. + try { + // This is intentionally an invariant that gets caught. It's the same + // behavior as without this statement except with a better message. + if (typeof typeSpecs[typeSpecName] !== 'function') { + var err = Error( + (componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + + 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.' + ); + err.name = 'Invariant Violation'; + throw err; + } + error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret); + } catch (ex) { + error = ex; + } + if (error && !(error instanceof Error)) { + printWarning( + (componentName || 'React class') + ': type specification of ' + + location + ' `' + typeSpecName + '` is invalid; the type checker ' + + 'function must return `null` or an `Error` but returned a ' + typeof error + '. ' + + 'You may have forgotten to pass an argument to the type checker ' + + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + + 'shape all require an argument).' + ); + } + if (error instanceof Error && !(error.message in loggedTypeFailures)) { + // Only monitor this failure once because there tends to be a lot of the + // same error. + loggedTypeFailures[error.message] = true; + + var stack = getStack ? getStack() : ''; + + printWarning( + 'Failed ' + location + ' type: ' + error.message + (stack != null ? stack : '') + ); + } + } + } + } +} + +/** + * Resets warning cache when testing. + * + * @private + */ +checkPropTypes.resetWarningCache = function() { + if (true) { + loggedTypeFailures = {}; + } +} + +module.exports = checkPropTypes; + + +/***/ }), + +/***/ "../node_modules/prop-types/factoryWithTypeCheckers.js": +/*!*************************************************************!*\ + !*** ../node_modules/prop-types/factoryWithTypeCheckers.js ***! + \*************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + + +var ReactIs = __webpack_require__(/*! react-is */ "../node_modules/prop-types/node_modules/react-is/index.js"); +var assign = __webpack_require__(/*! object-assign */ "../node_modules/object-assign/index.js"); + +var ReactPropTypesSecret = __webpack_require__(/*! ./lib/ReactPropTypesSecret */ "../node_modules/prop-types/lib/ReactPropTypesSecret.js"); +var has = __webpack_require__(/*! ./lib/has */ "../node_modules/prop-types/lib/has.js"); +var checkPropTypes = __webpack_require__(/*! ./checkPropTypes */ "../node_modules/prop-types/checkPropTypes.js"); + +var printWarning = function() {}; + +if (true) { + printWarning = function(text) { + var message = 'Warning: ' + text; + if (typeof console !== 'undefined') { + console.error(message); + } + try { + // --- Welcome to debugging React --- + // This error was thrown as a convenience so that you can use this stack + // to find the callsite that caused this warning to fire. + throw new Error(message); + } catch (x) {} + }; +} + +function emptyFunctionThatReturnsNull() { + return null; +} + +module.exports = function(isValidElement, throwOnDirectAccess) { + /* global Symbol */ + var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator; + var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec. + + /** + * Returns the iterator method function contained on the iterable object. + * + * Be sure to invoke the function with the iterable as context: + * + * var iteratorFn = getIteratorFn(myIterable); + * if (iteratorFn) { + * var iterator = iteratorFn.call(myIterable); + * ... + * } + * + * @param {?object} maybeIterable + * @return {?function} + */ + function getIteratorFn(maybeIterable) { + var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]); + if (typeof iteratorFn === 'function') { + return iteratorFn; + } + } + + /** + * Collection of methods that allow declaration and validation of props that are + * supplied to React components. Example usage: + * + * var Props = require('ReactPropTypes'); + * var MyArticle = React.createClass({ + * propTypes: { + * // An optional string prop named "description". + * description: Props.string, + * + * // A required enum prop named "category". + * category: Props.oneOf(['News','Photos']).isRequired, + * + * // A prop named "dialog" that requires an instance of Dialog. + * dialog: Props.instanceOf(Dialog).isRequired + * }, + * render: function() { ... } + * }); + * + * A more formal specification of how these methods are used: + * + * type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...) + * decl := ReactPropTypes.{type}(.isRequired)? + * + * Each and every declaration produces a function with the same signature. This + * allows the creation of custom validation functions. For example: + * + * var MyLink = React.createClass({ + * propTypes: { + * // An optional string or URI prop named "href". + * href: function(props, propName, componentName) { + * var propValue = props[propName]; + * if (propValue != null && typeof propValue !== 'string' && + * !(propValue instanceof URI)) { + * return new Error( + * 'Expected a string or an URI for ' + propName + ' in ' + + * componentName + * ); + * } + * } + * }, + * render: function() {...} + * }); + * + * @internal + */ + + var ANONYMOUS = '<>'; + + // Important! + // Keep this list in sync with production version in `./factoryWithThrowingShims.js`. + var ReactPropTypes = { + array: createPrimitiveTypeChecker('array'), + bigint: createPrimitiveTypeChecker('bigint'), + bool: createPrimitiveTypeChecker('boolean'), + func: createPrimitiveTypeChecker('function'), + number: createPrimitiveTypeChecker('number'), + object: createPrimitiveTypeChecker('object'), + string: createPrimitiveTypeChecker('string'), + symbol: createPrimitiveTypeChecker('symbol'), + + any: createAnyTypeChecker(), + arrayOf: createArrayOfTypeChecker, + element: createElementTypeChecker(), + elementType: createElementTypeTypeChecker(), + instanceOf: createInstanceTypeChecker, + node: createNodeChecker(), + objectOf: createObjectOfTypeChecker, + oneOf: createEnumTypeChecker, + oneOfType: createUnionTypeChecker, + shape: createShapeTypeChecker, + exact: createStrictShapeTypeChecker, + }; + + /** + * inlined Object.is polyfill to avoid requiring consumers ship their own + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is + */ + /*eslint-disable no-self-compare*/ + function is(x, y) { + // SameValue algorithm + if (x === y) { + // Steps 1-5, 7-10 + // Steps 6.b-6.e: +0 != -0 + return x !== 0 || 1 / x === 1 / y; + } else { + // Step 6.a: NaN == NaN + return x !== x && y !== y; + } + } + /*eslint-enable no-self-compare*/ + + /** + * We use an Error-like object for backward compatibility as people may call + * PropTypes directly and inspect their output. However, we don't use real + * Errors anymore. We don't inspect their stack anyway, and creating them + * is prohibitively expensive if they are created too often, such as what + * happens in oneOfType() for any type before the one that matched. + */ + function PropTypeError(message, data) { + this.message = message; + this.data = data && typeof data === 'object' ? data: {}; + this.stack = ''; + } + // Make `instanceof Error` still work for returned errors. + PropTypeError.prototype = Error.prototype; + + function createChainableTypeChecker(validate) { + if (true) { + var manualPropTypeCallCache = {}; + var manualPropTypeWarningCount = 0; + } + function checkType(isRequired, props, propName, componentName, location, propFullName, secret) { + componentName = componentName || ANONYMOUS; + propFullName = propFullName || propName; + + if (secret !== ReactPropTypesSecret) { + if (throwOnDirectAccess) { + // New behavior only for users of `prop-types` package + var err = new Error( + 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' + + 'Use `PropTypes.checkPropTypes()` to call them. ' + + 'Read more at http://fb.me/use-check-prop-types' + ); + err.name = 'Invariant Violation'; + throw err; + } else if ( true && typeof console !== 'undefined') { + // Old behavior for people using React.PropTypes + var cacheKey = componentName + ':' + propName; + if ( + !manualPropTypeCallCache[cacheKey] && + // Avoid spamming the console because they are often not actionable except for lib authors + manualPropTypeWarningCount < 3 + ) { + printWarning( + 'You are manually calling a React.PropTypes validation ' + + 'function for the `' + propFullName + '` prop on `' + componentName + '`. This is deprecated ' + + 'and will throw in the standalone `prop-types` package. ' + + 'You may be seeing this warning due to a third-party PropTypes ' + + 'library. See https://fb.me/react-warning-dont-call-proptypes ' + 'for details.' + ); + manualPropTypeCallCache[cacheKey] = true; + manualPropTypeWarningCount++; + } + } + } + if (props[propName] == null) { + if (isRequired) { + if (props[propName] === null) { + return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.')); + } + return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.')); + } + return null; + } else { + return validate(props, propName, componentName, location, propFullName); + } + } + + var chainedCheckType = checkType.bind(null, false); + chainedCheckType.isRequired = checkType.bind(null, true); + + return chainedCheckType; + } + + function createPrimitiveTypeChecker(expectedType) { + function validate(props, propName, componentName, location, propFullName, secret) { + var propValue = props[propName]; + var propType = getPropType(propValue); + if (propType !== expectedType) { + // `propValue` being instance of, say, date/regexp, pass the 'object' + // check, but we can offer a more precise error message here rather than + // 'of type `object`'. + var preciseType = getPreciseType(propValue); + + return new PropTypeError( + 'Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + preciseType + '` supplied to `' + componentName + '`, expected ') + ('`' + expectedType + '`.'), + {expectedType: expectedType} + ); + } + return null; + } + return createChainableTypeChecker(validate); + } + + function createAnyTypeChecker() { + return createChainableTypeChecker(emptyFunctionThatReturnsNull); + } + + function createArrayOfTypeChecker(typeChecker) { + function validate(props, propName, componentName, location, propFullName) { + if (typeof typeChecker !== 'function') { + return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside arrayOf.'); + } + var propValue = props[propName]; + if (!Array.isArray(propValue)) { + var propType = getPropType(propValue); + return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an array.')); + } + for (var i = 0; i < propValue.length; i++) { + var error = typeChecker(propValue, i, componentName, location, propFullName + '[' + i + ']', ReactPropTypesSecret); + if (error instanceof Error) { + return error; + } + } + return null; + } + return createChainableTypeChecker(validate); + } + + function createElementTypeChecker() { + function validate(props, propName, componentName, location, propFullName) { + var propValue = props[propName]; + if (!isValidElement(propValue)) { + var propType = getPropType(propValue); + return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement.')); + } + return null; + } + return createChainableTypeChecker(validate); + } + + function createElementTypeTypeChecker() { + function validate(props, propName, componentName, location, propFullName) { + var propValue = props[propName]; + if (!ReactIs.isValidElementType(propValue)) { + var propType = getPropType(propValue); + return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement type.')); + } + return null; + } + return createChainableTypeChecker(validate); + } + + function createInstanceTypeChecker(expectedClass) { + function validate(props, propName, componentName, location, propFullName) { + if (!(props[propName] instanceof expectedClass)) { + var expectedClassName = expectedClass.name || ANONYMOUS; + var actualClassName = getClassName(props[propName]); + return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + actualClassName + '` supplied to `' + componentName + '`, expected ') + ('instance of `' + expectedClassName + '`.')); + } + return null; + } + return createChainableTypeChecker(validate); + } + + function createEnumTypeChecker(expectedValues) { + if (!Array.isArray(expectedValues)) { + if (true) { + if (arguments.length > 1) { + printWarning( + 'Invalid arguments supplied to oneOf, expected an array, got ' + arguments.length + ' arguments. ' + + 'A common mistake is to write oneOf(x, y, z) instead of oneOf([x, y, z]).' + ); + } else { + printWarning('Invalid argument supplied to oneOf, expected an array.'); + } + } + return emptyFunctionThatReturnsNull; + } + + function validate(props, propName, componentName, location, propFullName) { + var propValue = props[propName]; + for (var i = 0; i < expectedValues.length; i++) { + if (is(propValue, expectedValues[i])) { + return null; + } + } + + var valuesString = JSON.stringify(expectedValues, function replacer(key, value) { + var type = getPreciseType(value); + if (type === 'symbol') { + return String(value); + } + return value; + }); + return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of value `' + String(propValue) + '` ' + ('supplied to `' + componentName + '`, expected one of ' + valuesString + '.')); + } + return createChainableTypeChecker(validate); + } + + function createObjectOfTypeChecker(typeChecker) { + function validate(props, propName, componentName, location, propFullName) { + if (typeof typeChecker !== 'function') { + return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside objectOf.'); + } + var propValue = props[propName]; + var propType = getPropType(propValue); + if (propType !== 'object') { + return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an object.')); + } + for (var key in propValue) { + if (has(propValue, key)) { + var error = typeChecker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret); + if (error instanceof Error) { + return error; + } + } + } + return null; + } + return createChainableTypeChecker(validate); + } + + function createUnionTypeChecker(arrayOfTypeCheckers) { + if (!Array.isArray(arrayOfTypeCheckers)) { + true ? printWarning('Invalid argument supplied to oneOfType, expected an instance of array.') : 0; + return emptyFunctionThatReturnsNull; + } + + for (var i = 0; i < arrayOfTypeCheckers.length; i++) { + var checker = arrayOfTypeCheckers[i]; + if (typeof checker !== 'function') { + printWarning( + 'Invalid argument supplied to oneOfType. Expected an array of check functions, but ' + + 'received ' + getPostfixForTypeWarning(checker) + ' at index ' + i + '.' + ); + return emptyFunctionThatReturnsNull; + } + } + + function validate(props, propName, componentName, location, propFullName) { + var expectedTypes = []; + for (var i = 0; i < arrayOfTypeCheckers.length; i++) { + var checker = arrayOfTypeCheckers[i]; + var checkerResult = checker(props, propName, componentName, location, propFullName, ReactPropTypesSecret); + if (checkerResult == null) { + return null; + } + if (checkerResult.data && has(checkerResult.data, 'expectedType')) { + expectedTypes.push(checkerResult.data.expectedType); + } + } + var expectedTypesMessage = (expectedTypes.length > 0) ? ', expected one of type [' + expectedTypes.join(', ') + ']': ''; + return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`' + expectedTypesMessage + '.')); + } + return createChainableTypeChecker(validate); + } + + function createNodeChecker() { + function validate(props, propName, componentName, location, propFullName) { + if (!isNode(props[propName])) { + return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a ReactNode.')); + } + return null; + } + return createChainableTypeChecker(validate); + } + + function invalidValidatorError(componentName, location, propFullName, key, type) { + return new PropTypeError( + (componentName || 'React class') + ': ' + location + ' type `' + propFullName + '.' + key + '` is invalid; ' + + 'it must be a function, usually from the `prop-types` package, but received `' + type + '`.' + ); + } + + function createShapeTypeChecker(shapeTypes) { + function validate(props, propName, componentName, location, propFullName) { + var propValue = props[propName]; + var propType = getPropType(propValue); + if (propType !== 'object') { + return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.')); + } + for (var key in shapeTypes) { + var checker = shapeTypes[key]; + if (typeof checker !== 'function') { + return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker)); + } + var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret); + if (error) { + return error; + } + } + return null; + } + return createChainableTypeChecker(validate); + } + + function createStrictShapeTypeChecker(shapeTypes) { + function validate(props, propName, componentName, location, propFullName) { + var propValue = props[propName]; + var propType = getPropType(propValue); + if (propType !== 'object') { + return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.')); + } + // We need to check all keys in case some are required but missing from props. + var allKeys = assign({}, props[propName], shapeTypes); + for (var key in allKeys) { + var checker = shapeTypes[key]; + if (has(shapeTypes, key) && typeof checker !== 'function') { + return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker)); + } + if (!checker) { + return new PropTypeError( + 'Invalid ' + location + ' `' + propFullName + '` key `' + key + '` supplied to `' + componentName + '`.' + + '\nBad object: ' + JSON.stringify(props[propName], null, ' ') + + '\nValid keys: ' + JSON.stringify(Object.keys(shapeTypes), null, ' ') + ); + } + var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret); + if (error) { + return error; + } + } + return null; + } + + return createChainableTypeChecker(validate); + } + + function isNode(propValue) { + switch (typeof propValue) { + case 'number': + case 'string': + case 'undefined': + return true; + case 'boolean': + return !propValue; + case 'object': + if (Array.isArray(propValue)) { + return propValue.every(isNode); + } + if (propValue === null || isValidElement(propValue)) { + return true; + } + + var iteratorFn = getIteratorFn(propValue); + if (iteratorFn) { + var iterator = iteratorFn.call(propValue); + var step; + if (iteratorFn !== propValue.entries) { + while (!(step = iterator.next()).done) { + if (!isNode(step.value)) { + return false; + } + } + } else { + // Iterator will provide entry [k,v] tuples rather than values. + while (!(step = iterator.next()).done) { + var entry = step.value; + if (entry) { + if (!isNode(entry[1])) { + return false; + } + } + } + } + } else { + return false; + } + + return true; + default: + return false; + } + } + + function isSymbol(propType, propValue) { + // Native Symbol. + if (propType === 'symbol') { + return true; + } + + // falsy value can't be a Symbol + if (!propValue) { + return false; + } + + // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol' + if (propValue['@@toStringTag'] === 'Symbol') { + return true; + } + + // Fallback for non-spec compliant Symbols which are polyfilled. + if (typeof Symbol === 'function' && propValue instanceof Symbol) { + return true; + } + + return false; + } + + // Equivalent of `typeof` but with special handling for array and regexp. + function getPropType(propValue) { + var propType = typeof propValue; + if (Array.isArray(propValue)) { + return 'array'; + } + if (propValue instanceof RegExp) { + // Old webkits (at least until Android 4.0) return 'function' rather than + // 'object' for typeof a RegExp. We'll normalize this here so that /bla/ + // passes PropTypes.object. + return 'object'; + } + if (isSymbol(propType, propValue)) { + return 'symbol'; + } + return propType; + } + + // This handles more types than `getPropType`. Only used for error messages. + // See `createPrimitiveTypeChecker`. + function getPreciseType(propValue) { + if (typeof propValue === 'undefined' || propValue === null) { + return '' + propValue; + } + var propType = getPropType(propValue); + if (propType === 'object') { + if (propValue instanceof Date) { + return 'date'; + } else if (propValue instanceof RegExp) { + return 'regexp'; + } + } + return propType; + } + + // Returns a string that is postfixed to a warning about an invalid type. + // For example, "undefined" or "of type array" + function getPostfixForTypeWarning(value) { + var type = getPreciseType(value); + switch (type) { + case 'array': + case 'object': + return 'an ' + type; + case 'boolean': + case 'date': + case 'regexp': + return 'a ' + type; + default: + return type; + } + } + + // Returns class name of the object, if any. + function getClassName(propValue) { + if (!propValue.constructor || !propValue.constructor.name) { + return ANONYMOUS; + } + return propValue.constructor.name; + } + + ReactPropTypes.checkPropTypes = checkPropTypes; + ReactPropTypes.resetWarningCache = checkPropTypes.resetWarningCache; + ReactPropTypes.PropTypes = ReactPropTypes; + + return ReactPropTypes; +}; + + +/***/ }), + +/***/ "../node_modules/prop-types/index.js": +/*!*******************************************!*\ + !*** ../node_modules/prop-types/index.js ***! + \*******************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +if (true) { + var ReactIs = __webpack_require__(/*! react-is */ "../node_modules/prop-types/node_modules/react-is/index.js"); + + // By explicitly using `prop-types` you are opting into new development behavior. + // http://fb.me/prop-types-in-prod + var throwOnDirectAccess = true; + module.exports = __webpack_require__(/*! ./factoryWithTypeCheckers */ "../node_modules/prop-types/factoryWithTypeCheckers.js")(ReactIs.isElement, throwOnDirectAccess); +} else {} + + +/***/ }), + +/***/ "../node_modules/prop-types/lib/ReactPropTypesSecret.js": +/*!**************************************************************!*\ + !*** ../node_modules/prop-types/lib/ReactPropTypesSecret.js ***! + \**************************************************************/ +/***/ ((module) => { + +"use strict"; +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + + +var ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'; + +module.exports = ReactPropTypesSecret; + + +/***/ }), + +/***/ "../node_modules/prop-types/lib/has.js": +/*!*********************************************!*\ + !*** ../node_modules/prop-types/lib/has.js ***! + \*********************************************/ +/***/ ((module) => { + +module.exports = Function.call.bind(Object.prototype.hasOwnProperty); + + +/***/ }), + +/***/ "../node_modules/prop-types/node_modules/react-is/cjs/react-is.development.js": +/*!************************************************************************************!*\ + !*** ../node_modules/prop-types/node_modules/react-is/cjs/react-is.development.js ***! + \************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; +/** @license React v16.13.1 + * react-is.development.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + + + + +if (true) { + (function() { +'use strict'; + +// The Symbol used to tag the ReactElement-like types. If there is no native Symbol +// nor polyfill, then a plain number is used for performance. +var hasSymbol = typeof Symbol === 'function' && Symbol.for; +var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7; +var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca; +var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb; +var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc; +var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2; +var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd; +var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace; // TODO: We don't use AsyncMode or ConcurrentMode anymore. They were temporary +// (unstable) APIs that have been removed. Can we remove the symbols? + +var REACT_ASYNC_MODE_TYPE = hasSymbol ? Symbol.for('react.async_mode') : 0xeacf; +var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf; +var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0; +var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1; +var REACT_SUSPENSE_LIST_TYPE = hasSymbol ? Symbol.for('react.suspense_list') : 0xead8; +var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3; +var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4; +var REACT_BLOCK_TYPE = hasSymbol ? Symbol.for('react.block') : 0xead9; +var REACT_FUNDAMENTAL_TYPE = hasSymbol ? Symbol.for('react.fundamental') : 0xead5; +var REACT_RESPONDER_TYPE = hasSymbol ? Symbol.for('react.responder') : 0xead6; +var REACT_SCOPE_TYPE = hasSymbol ? Symbol.for('react.scope') : 0xead7; + +function isValidElementType(type) { + return typeof type === 'string' || typeof type === 'function' || // Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill. + type === REACT_FRAGMENT_TYPE || type === REACT_CONCURRENT_MODE_TYPE || type === REACT_PROFILER_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || typeof type === 'object' && type !== null && (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || type.$$typeof === REACT_FUNDAMENTAL_TYPE || type.$$typeof === REACT_RESPONDER_TYPE || type.$$typeof === REACT_SCOPE_TYPE || type.$$typeof === REACT_BLOCK_TYPE); +} + +function typeOf(object) { + if (typeof object === 'object' && object !== null) { + var $$typeof = object.$$typeof; + + switch ($$typeof) { + case REACT_ELEMENT_TYPE: + var type = object.type; + + switch (type) { + case REACT_ASYNC_MODE_TYPE: + case REACT_CONCURRENT_MODE_TYPE: + case REACT_FRAGMENT_TYPE: + case REACT_PROFILER_TYPE: + case REACT_STRICT_MODE_TYPE: + case REACT_SUSPENSE_TYPE: + return type; + + default: + var $$typeofType = type && type.$$typeof; + + switch ($$typeofType) { + case REACT_CONTEXT_TYPE: + case REACT_FORWARD_REF_TYPE: + case REACT_LAZY_TYPE: + case REACT_MEMO_TYPE: + case REACT_PROVIDER_TYPE: + return $$typeofType; + + default: + return $$typeof; + } + + } + + case REACT_PORTAL_TYPE: + return $$typeof; + } + } + + return undefined; +} // AsyncMode is deprecated along with isAsyncMode + +var AsyncMode = REACT_ASYNC_MODE_TYPE; +var ConcurrentMode = REACT_CONCURRENT_MODE_TYPE; +var ContextConsumer = REACT_CONTEXT_TYPE; +var ContextProvider = REACT_PROVIDER_TYPE; +var Element = REACT_ELEMENT_TYPE; +var ForwardRef = REACT_FORWARD_REF_TYPE; +var Fragment = REACT_FRAGMENT_TYPE; +var Lazy = REACT_LAZY_TYPE; +var Memo = REACT_MEMO_TYPE; +var Portal = REACT_PORTAL_TYPE; +var Profiler = REACT_PROFILER_TYPE; +var StrictMode = REACT_STRICT_MODE_TYPE; +var Suspense = REACT_SUSPENSE_TYPE; +var hasWarnedAboutDeprecatedIsAsyncMode = false; // AsyncMode should be deprecated + +function isAsyncMode(object) { + { + if (!hasWarnedAboutDeprecatedIsAsyncMode) { + hasWarnedAboutDeprecatedIsAsyncMode = true; // Using console['warn'] to evade Babel and ESLint + + console['warn']('The ReactIs.isAsyncMode() alias has been deprecated, ' + 'and will be removed in React 17+. Update your code to use ' + 'ReactIs.isConcurrentMode() instead. It has the exact same API.'); + } + } + + return isConcurrentMode(object) || typeOf(object) === REACT_ASYNC_MODE_TYPE; +} +function isConcurrentMode(object) { + return typeOf(object) === REACT_CONCURRENT_MODE_TYPE; +} +function isContextConsumer(object) { + return typeOf(object) === REACT_CONTEXT_TYPE; +} +function isContextProvider(object) { + return typeOf(object) === REACT_PROVIDER_TYPE; +} +function isElement(object) { + return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE; +} +function isForwardRef(object) { + return typeOf(object) === REACT_FORWARD_REF_TYPE; +} +function isFragment(object) { + return typeOf(object) === REACT_FRAGMENT_TYPE; +} +function isLazy(object) { + return typeOf(object) === REACT_LAZY_TYPE; +} +function isMemo(object) { + return typeOf(object) === REACT_MEMO_TYPE; +} +function isPortal(object) { + return typeOf(object) === REACT_PORTAL_TYPE; +} +function isProfiler(object) { + return typeOf(object) === REACT_PROFILER_TYPE; +} +function isStrictMode(object) { + return typeOf(object) === REACT_STRICT_MODE_TYPE; +} +function isSuspense(object) { + return typeOf(object) === REACT_SUSPENSE_TYPE; +} + +exports.AsyncMode = AsyncMode; +exports.ConcurrentMode = ConcurrentMode; +exports.ContextConsumer = ContextConsumer; +exports.ContextProvider = ContextProvider; +exports.Element = Element; +exports.ForwardRef = ForwardRef; +exports.Fragment = Fragment; +exports.Lazy = Lazy; +exports.Memo = Memo; +exports.Portal = Portal; +exports.Profiler = Profiler; +exports.StrictMode = StrictMode; +exports.Suspense = Suspense; +exports.isAsyncMode = isAsyncMode; +exports.isConcurrentMode = isConcurrentMode; +exports.isContextConsumer = isContextConsumer; +exports.isContextProvider = isContextProvider; +exports.isElement = isElement; +exports.isForwardRef = isForwardRef; +exports.isFragment = isFragment; +exports.isLazy = isLazy; +exports.isMemo = isMemo; +exports.isPortal = isPortal; +exports.isProfiler = isProfiler; +exports.isStrictMode = isStrictMode; +exports.isSuspense = isSuspense; +exports.isValidElementType = isValidElementType; +exports.typeOf = typeOf; + })(); +} + + +/***/ }), + +/***/ "../node_modules/prop-types/node_modules/react-is/index.js": +/*!*****************************************************************!*\ + !*** ../node_modules/prop-types/node_modules/react-is/index.js ***! + \*****************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +if (false) {} else { + module.exports = __webpack_require__(/*! ./cjs/react-is.development.js */ "../node_modules/prop-types/node_modules/react-is/cjs/react-is.development.js"); +} + + +/***/ }), + +/***/ "react": +/*!************************!*\ + !*** external "React" ***! + \************************/ +/***/ ((module) => { + +"use strict"; +module.exports = React; + +/***/ }), + +/***/ "@elementor/icons": +/*!************************************!*\ + !*** external "elementorV2.icons" ***! + \************************************/ +/***/ ((module) => { + +"use strict"; +module.exports = elementorV2.icons; + +/***/ }), + +/***/ "@elementor/ui": +/*!*********************************!*\ + !*** external "elementorV2.ui" ***! + \*********************************/ +/***/ ((module) => { + +"use strict"; +module.exports = elementorV2.ui; + +/***/ }), + +/***/ "@wordpress/i18n": +/*!**************************!*\ + !*** external "wp.i18n" ***! + \**************************/ +/***/ ((module) => { + +"use strict"; +module.exports = wp.i18n; + +/***/ }), + +/***/ "../node_modules/@babel/runtime/helpers/extends.js": +/*!*********************************************************!*\ + !*** ../node_modules/@babel/runtime/helpers/extends.js ***! + \*********************************************************/ +/***/ ((module) => { + +function _extends() { + module.exports = _extends = Object.assign ? Object.assign.bind() : function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + return target; + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + return _extends.apply(this, arguments); +} +module.exports = _extends, module.exports.__esModule = true, module.exports["default"] = module.exports; + +/***/ }), + +/***/ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js": +/*!***********************************************************************!*\ + !*** ../node_modules/@babel/runtime/helpers/interopRequireDefault.js ***! + \***********************************************************************/ +/***/ ((module) => { + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + "default": obj + }; +} +module.exports = _interopRequireDefault, module.exports.__esModule = true, module.exports["default"] = module.exports; + +/***/ }), + +/***/ "../node_modules/core-js/internals/a-callable.js": +/*!*******************************************************!*\ + !*** ../node_modules/core-js/internals/a-callable.js ***! + \*******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var tryToString = __webpack_require__(/*! ../internals/try-to-string */ "../node_modules/core-js/internals/try-to-string.js"); + +var $TypeError = TypeError; + +// `Assert: IsCallable(argument) is true` +module.exports = function (argument) { + if (isCallable(argument)) return argument; + throw new $TypeError(tryToString(argument) + ' is not a function'); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/an-object.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/an-object.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var isObject = __webpack_require__(/*! ../internals/is-object */ "../node_modules/core-js/internals/is-object.js"); + +var $String = String; +var $TypeError = TypeError; + +// `Assert: Type(argument) is Object` +module.exports = function (argument) { + if (isObject(argument)) return argument; + throw new $TypeError($String(argument) + ' is not an object'); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/array-includes.js": +/*!***********************************************************!*\ + !*** ../node_modules/core-js/internals/array-includes.js ***! + \***********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var toIndexedObject = __webpack_require__(/*! ../internals/to-indexed-object */ "../node_modules/core-js/internals/to-indexed-object.js"); +var toAbsoluteIndex = __webpack_require__(/*! ../internals/to-absolute-index */ "../node_modules/core-js/internals/to-absolute-index.js"); +var lengthOfArrayLike = __webpack_require__(/*! ../internals/length-of-array-like */ "../node_modules/core-js/internals/length-of-array-like.js"); + +// `Array.prototype.{ indexOf, includes }` methods implementation +var createMethod = function (IS_INCLUDES) { + return function ($this, el, fromIndex) { + var O = toIndexedObject($this); + var length = lengthOfArrayLike(O); + var index = toAbsoluteIndex(fromIndex, length); + var value; + // Array#includes uses SameValueZero equality algorithm + // eslint-disable-next-line no-self-compare -- NaN check + if (IS_INCLUDES && el !== el) while (length > index) { + value = O[index++]; + // eslint-disable-next-line no-self-compare -- NaN check + if (value !== value) return true; + // Array#indexOf ignores holes, Array#includes - not + } else for (;length > index; index++) { + if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0; + } return !IS_INCLUDES && -1; + }; +}; + +module.exports = { + // `Array.prototype.includes` method + // https://tc39.es/ecma262/#sec-array.prototype.includes + includes: createMethod(true), + // `Array.prototype.indexOf` method + // https://tc39.es/ecma262/#sec-array.prototype.indexof + indexOf: createMethod(false) +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/array-set-length.js": +/*!*************************************************************!*\ + !*** ../node_modules/core-js/internals/array-set-length.js ***! + \*************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var isArray = __webpack_require__(/*! ../internals/is-array */ "../node_modules/core-js/internals/is-array.js"); + +var $TypeError = TypeError; +// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe +var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + +// Safari < 13 does not throw an error in this case +var SILENT_ON_NON_WRITABLE_LENGTH_SET = DESCRIPTORS && !function () { + // makes no sense without proper strict mode support + if (this !== undefined) return true; + try { + // eslint-disable-next-line es/no-object-defineproperty -- safe + Object.defineProperty([], 'length', { writable: false }).length = 1; + } catch (error) { + return error instanceof TypeError; + } +}(); + +module.exports = SILENT_ON_NON_WRITABLE_LENGTH_SET ? function (O, length) { + if (isArray(O) && !getOwnPropertyDescriptor(O, 'length').writable) { + throw new $TypeError('Cannot set read only .length'); + } return O.length = length; +} : function (O, length) { + return O.length = length; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/classof-raw.js": +/*!********************************************************!*\ + !*** ../node_modules/core-js/internals/classof-raw.js ***! + \********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); + +var toString = uncurryThis({}.toString); +var stringSlice = uncurryThis(''.slice); + +module.exports = function (it) { + return stringSlice(toString(it), 8, -1); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/copy-constructor-properties.js": +/*!************************************************************************!*\ + !*** ../node_modules/core-js/internals/copy-constructor-properties.js ***! + \************************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var ownKeys = __webpack_require__(/*! ../internals/own-keys */ "../node_modules/core-js/internals/own-keys.js"); +var getOwnPropertyDescriptorModule = __webpack_require__(/*! ../internals/object-get-own-property-descriptor */ "../node_modules/core-js/internals/object-get-own-property-descriptor.js"); +var definePropertyModule = __webpack_require__(/*! ../internals/object-define-property */ "../node_modules/core-js/internals/object-define-property.js"); + +module.exports = function (target, source, exceptions) { + var keys = ownKeys(source); + var defineProperty = definePropertyModule.f; + var getOwnPropertyDescriptor = getOwnPropertyDescriptorModule.f; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (!hasOwn(target, key) && !(exceptions && hasOwn(exceptions, key))) { + defineProperty(target, key, getOwnPropertyDescriptor(source, key)); + } + } +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/create-non-enumerable-property.js": +/*!***************************************************************************!*\ + !*** ../node_modules/core-js/internals/create-non-enumerable-property.js ***! + \***************************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var definePropertyModule = __webpack_require__(/*! ../internals/object-define-property */ "../node_modules/core-js/internals/object-define-property.js"); +var createPropertyDescriptor = __webpack_require__(/*! ../internals/create-property-descriptor */ "../node_modules/core-js/internals/create-property-descriptor.js"); + +module.exports = DESCRIPTORS ? function (object, key, value) { + return definePropertyModule.f(object, key, createPropertyDescriptor(1, value)); +} : function (object, key, value) { + object[key] = value; + return object; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/create-property-descriptor.js": +/*!***********************************************************************!*\ + !*** ../node_modules/core-js/internals/create-property-descriptor.js ***! + \***********************************************************************/ +/***/ ((module) => { + +"use strict"; + +module.exports = function (bitmap, value) { + return { + enumerable: !(bitmap & 1), + configurable: !(bitmap & 2), + writable: !(bitmap & 4), + value: value + }; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/define-built-in.js": +/*!************************************************************!*\ + !*** ../node_modules/core-js/internals/define-built-in.js ***! + \************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var definePropertyModule = __webpack_require__(/*! ../internals/object-define-property */ "../node_modules/core-js/internals/object-define-property.js"); +var makeBuiltIn = __webpack_require__(/*! ../internals/make-built-in */ "../node_modules/core-js/internals/make-built-in.js"); +var defineGlobalProperty = __webpack_require__(/*! ../internals/define-global-property */ "../node_modules/core-js/internals/define-global-property.js"); + +module.exports = function (O, key, value, options) { + if (!options) options = {}; + var simple = options.enumerable; + var name = options.name !== undefined ? options.name : key; + if (isCallable(value)) makeBuiltIn(value, name, options); + if (options.global) { + if (simple) O[key] = value; + else defineGlobalProperty(key, value); + } else { + try { + if (!options.unsafe) delete O[key]; + else if (O[key]) simple = true; + } catch (error) { /* empty */ } + if (simple) O[key] = value; + else definePropertyModule.f(O, key, { + value: value, + enumerable: false, + configurable: !options.nonConfigurable, + writable: !options.nonWritable + }); + } return O; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/define-global-property.js": +/*!*******************************************************************!*\ + !*** ../node_modules/core-js/internals/define-global-property.js ***! + \*******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); + +// eslint-disable-next-line es/no-object-defineproperty -- safe +var defineProperty = Object.defineProperty; + +module.exports = function (key, value) { + try { + defineProperty(global, key, { value: value, configurable: true, writable: true }); + } catch (error) { + global[key] = value; + } return value; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/descriptors.js": +/*!********************************************************!*\ + !*** ../node_modules/core-js/internals/descriptors.js ***! + \********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); + +// Detect IE8's incomplete defineProperty implementation +module.exports = !fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] !== 7; +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/document-all.js": +/*!*********************************************************!*\ + !*** ../node_modules/core-js/internals/document-all.js ***! + \*********************************************************/ +/***/ ((module) => { + +"use strict"; + +var documentAll = typeof document == 'object' && document.all; + +// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot +// eslint-disable-next-line unicorn/no-typeof-undefined -- required for testing +var IS_HTMLDDA = typeof documentAll == 'undefined' && documentAll !== undefined; + +module.exports = { + all: documentAll, + IS_HTMLDDA: IS_HTMLDDA +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/document-create-element.js": +/*!********************************************************************!*\ + !*** ../node_modules/core-js/internals/document-create-element.js ***! + \********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var isObject = __webpack_require__(/*! ../internals/is-object */ "../node_modules/core-js/internals/is-object.js"); + +var document = global.document; +// typeof document.createElement is 'object' in old IE +var EXISTS = isObject(document) && isObject(document.createElement); + +module.exports = function (it) { + return EXISTS ? document.createElement(it) : {}; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/does-not-exceed-safe-integer.js": +/*!*************************************************************************!*\ + !*** ../node_modules/core-js/internals/does-not-exceed-safe-integer.js ***! + \*************************************************************************/ +/***/ ((module) => { + +"use strict"; + +var $TypeError = TypeError; +var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF; // 2 ** 53 - 1 == 9007199254740991 + +module.exports = function (it) { + if (it > MAX_SAFE_INTEGER) throw $TypeError('Maximum allowed index exceeded'); + return it; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/engine-user-agent.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/engine-user-agent.js ***! + \**************************************************************/ +/***/ ((module) => { + +"use strict"; + +module.exports = typeof navigator != 'undefined' && String(navigator.userAgent) || ''; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/engine-v8-version.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/engine-v8-version.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var userAgent = __webpack_require__(/*! ../internals/engine-user-agent */ "../node_modules/core-js/internals/engine-user-agent.js"); + +var process = global.process; +var Deno = global.Deno; +var versions = process && process.versions || Deno && Deno.version; +var v8 = versions && versions.v8; +var match, version; + +if (v8) { + match = v8.split('.'); + // in old Chrome, versions of V8 isn't V8 = Chrome / 10 + // but their correct versions are not interesting for us + version = match[0] > 0 && match[0] < 4 ? 1 : +(match[0] + match[1]); +} + +// BrowserFS NodeJS `process` polyfill incorrectly set `.v8` to `0.0` +// so check `userAgent` even if `.v8` exists, but 0 +if (!version && userAgent) { + match = userAgent.match(/Edge\/(\d+)/); + if (!match || match[1] >= 74) { + match = userAgent.match(/Chrome\/(\d+)/); + if (match) version = +match[1]; + } +} + +module.exports = version; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/enum-bug-keys.js": +/*!**********************************************************!*\ + !*** ../node_modules/core-js/internals/enum-bug-keys.js ***! + \**********************************************************/ +/***/ ((module) => { + +"use strict"; + +// IE8- don't enum bug keys +module.exports = [ + 'constructor', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'toLocaleString', + 'toString', + 'valueOf' +]; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/export.js": +/*!***************************************************!*\ + !*** ../node_modules/core-js/internals/export.js ***! + \***************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var getOwnPropertyDescriptor = (__webpack_require__(/*! ../internals/object-get-own-property-descriptor */ "../node_modules/core-js/internals/object-get-own-property-descriptor.js").f); +var createNonEnumerableProperty = __webpack_require__(/*! ../internals/create-non-enumerable-property */ "../node_modules/core-js/internals/create-non-enumerable-property.js"); +var defineBuiltIn = __webpack_require__(/*! ../internals/define-built-in */ "../node_modules/core-js/internals/define-built-in.js"); +var defineGlobalProperty = __webpack_require__(/*! ../internals/define-global-property */ "../node_modules/core-js/internals/define-global-property.js"); +var copyConstructorProperties = __webpack_require__(/*! ../internals/copy-constructor-properties */ "../node_modules/core-js/internals/copy-constructor-properties.js"); +var isForced = __webpack_require__(/*! ../internals/is-forced */ "../node_modules/core-js/internals/is-forced.js"); + +/* + options.target - name of the target object + options.global - target is the global object + options.stat - export as static methods of target + options.proto - export as prototype methods of target + options.real - real prototype method for the `pure` version + options.forced - export even if the native feature is available + options.bind - bind methods to the target, required for the `pure` version + options.wrap - wrap constructors to preventing global pollution, required for the `pure` version + options.unsafe - use the simple assignment of property instead of delete + defineProperty + options.sham - add a flag to not completely full polyfills + options.enumerable - export as enumerable property + options.dontCallGetSet - prevent calling a getter on target + options.name - the .name of the function if it does not match the key +*/ +module.exports = function (options, source) { + var TARGET = options.target; + var GLOBAL = options.global; + var STATIC = options.stat; + var FORCED, target, key, targetProperty, sourceProperty, descriptor; + if (GLOBAL) { + target = global; + } else if (STATIC) { + target = global[TARGET] || defineGlobalProperty(TARGET, {}); + } else { + target = (global[TARGET] || {}).prototype; + } + if (target) for (key in source) { + sourceProperty = source[key]; + if (options.dontCallGetSet) { + descriptor = getOwnPropertyDescriptor(target, key); + targetProperty = descriptor && descriptor.value; + } else targetProperty = target[key]; + FORCED = isForced(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced); + // contained in target + if (!FORCED && targetProperty !== undefined) { + if (typeof sourceProperty == typeof targetProperty) continue; + copyConstructorProperties(sourceProperty, targetProperty); + } + // add a flag to not completely full polyfills + if (options.sham || (targetProperty && targetProperty.sham)) { + createNonEnumerableProperty(sourceProperty, 'sham', true); + } + defineBuiltIn(target, key, sourceProperty, options); + } +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/fails.js": +/*!**************************************************!*\ + !*** ../node_modules/core-js/internals/fails.js ***! + \**************************************************/ +/***/ ((module) => { + +"use strict"; + +module.exports = function (exec) { + try { + return !!exec(); + } catch (error) { + return true; + } +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/function-bind-native.js": +/*!*****************************************************************!*\ + !*** ../node_modules/core-js/internals/function-bind-native.js ***! + \*****************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); + +module.exports = !fails(function () { + // eslint-disable-next-line es/no-function-prototype-bind -- safe + var test = (function () { /* empty */ }).bind(); + // eslint-disable-next-line no-prototype-builtins -- safe + return typeof test != 'function' || test.hasOwnProperty('prototype'); +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/function-call.js": +/*!**********************************************************!*\ + !*** ../node_modules/core-js/internals/function-call.js ***! + \**********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var NATIVE_BIND = __webpack_require__(/*! ../internals/function-bind-native */ "../node_modules/core-js/internals/function-bind-native.js"); + +var call = Function.prototype.call; + +module.exports = NATIVE_BIND ? call.bind(call) : function () { + return call.apply(call, arguments); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/function-name.js": +/*!**********************************************************!*\ + !*** ../node_modules/core-js/internals/function-name.js ***! + \**********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); + +var FunctionPrototype = Function.prototype; +// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe +var getDescriptor = DESCRIPTORS && Object.getOwnPropertyDescriptor; + +var EXISTS = hasOwn(FunctionPrototype, 'name'); +// additional protection from minified / mangled / dropped function names +var PROPER = EXISTS && (function something() { /* empty */ }).name === 'something'; +var CONFIGURABLE = EXISTS && (!DESCRIPTORS || (DESCRIPTORS && getDescriptor(FunctionPrototype, 'name').configurable)); + +module.exports = { + EXISTS: EXISTS, + PROPER: PROPER, + CONFIGURABLE: CONFIGURABLE +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/function-uncurry-this.js": +/*!******************************************************************!*\ + !*** ../node_modules/core-js/internals/function-uncurry-this.js ***! + \******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var NATIVE_BIND = __webpack_require__(/*! ../internals/function-bind-native */ "../node_modules/core-js/internals/function-bind-native.js"); + +var FunctionPrototype = Function.prototype; +var call = FunctionPrototype.call; +var uncurryThisWithBind = NATIVE_BIND && FunctionPrototype.bind.bind(call, call); + +module.exports = NATIVE_BIND ? uncurryThisWithBind : function (fn) { + return function () { + return call.apply(fn, arguments); + }; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/get-built-in.js": +/*!*********************************************************!*\ + !*** ../node_modules/core-js/internals/get-built-in.js ***! + \*********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); + +var aFunction = function (argument) { + return isCallable(argument) ? argument : undefined; +}; + +module.exports = function (namespace, method) { + return arguments.length < 2 ? aFunction(global[namespace]) : global[namespace] && global[namespace][method]; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/get-method.js": +/*!*******************************************************!*\ + !*** ../node_modules/core-js/internals/get-method.js ***! + \*******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var aCallable = __webpack_require__(/*! ../internals/a-callable */ "../node_modules/core-js/internals/a-callable.js"); +var isNullOrUndefined = __webpack_require__(/*! ../internals/is-null-or-undefined */ "../node_modules/core-js/internals/is-null-or-undefined.js"); + +// `GetMethod` abstract operation +// https://tc39.es/ecma262/#sec-getmethod +module.exports = function (V, P) { + var func = V[P]; + return isNullOrUndefined(func) ? undefined : aCallable(func); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/global.js": +/*!***************************************************!*\ + !*** ../node_modules/core-js/internals/global.js ***! + \***************************************************/ +/***/ (function(module, __unused_webpack_exports, __webpack_require__) { + +"use strict"; + +var check = function (it) { + return it && it.Math === Math && it; +}; + +// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 +module.exports = + // eslint-disable-next-line es/no-global-this -- safe + check(typeof globalThis == 'object' && globalThis) || + check(typeof window == 'object' && window) || + // eslint-disable-next-line no-restricted-globals -- safe + check(typeof self == 'object' && self) || + check(typeof __webpack_require__.g == 'object' && __webpack_require__.g) || + // eslint-disable-next-line no-new-func -- fallback + (function () { return this; })() || this || Function('return this')(); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/has-own-property.js": +/*!*************************************************************!*\ + !*** ../node_modules/core-js/internals/has-own-property.js ***! + \*************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var toObject = __webpack_require__(/*! ../internals/to-object */ "../node_modules/core-js/internals/to-object.js"); + +var hasOwnProperty = uncurryThis({}.hasOwnProperty); + +// `HasOwnProperty` abstract operation +// https://tc39.es/ecma262/#sec-hasownproperty +// eslint-disable-next-line es/no-object-hasown -- safe +module.exports = Object.hasOwn || function hasOwn(it, key) { + return hasOwnProperty(toObject(it), key); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/hidden-keys.js": +/*!********************************************************!*\ + !*** ../node_modules/core-js/internals/hidden-keys.js ***! + \********************************************************/ +/***/ ((module) => { + +"use strict"; + +module.exports = {}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/ie8-dom-define.js": +/*!***********************************************************!*\ + !*** ../node_modules/core-js/internals/ie8-dom-define.js ***! + \***********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); +var createElement = __webpack_require__(/*! ../internals/document-create-element */ "../node_modules/core-js/internals/document-create-element.js"); + +// Thanks to IE8 for its funny defineProperty +module.exports = !DESCRIPTORS && !fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty(createElement('div'), 'a', { + get: function () { return 7; } + }).a !== 7; +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/indexed-object.js": +/*!***********************************************************!*\ + !*** ../node_modules/core-js/internals/indexed-object.js ***! + \***********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); +var classof = __webpack_require__(/*! ../internals/classof-raw */ "../node_modules/core-js/internals/classof-raw.js"); + +var $Object = Object; +var split = uncurryThis(''.split); + +// fallback for non-array-like ES3 and non-enumerable old V8 strings +module.exports = fails(function () { + // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346 + // eslint-disable-next-line no-prototype-builtins -- safe + return !$Object('z').propertyIsEnumerable(0); +}) ? function (it) { + return classof(it) === 'String' ? split(it, '') : $Object(it); +} : $Object; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/inspect-source.js": +/*!***********************************************************!*\ + !*** ../node_modules/core-js/internals/inspect-source.js ***! + \***********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var store = __webpack_require__(/*! ../internals/shared-store */ "../node_modules/core-js/internals/shared-store.js"); + +var functionToString = uncurryThis(Function.toString); + +// this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper +if (!isCallable(store.inspectSource)) { + store.inspectSource = function (it) { + return functionToString(it); + }; +} + +module.exports = store.inspectSource; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/internal-state.js": +/*!***********************************************************!*\ + !*** ../node_modules/core-js/internals/internal-state.js ***! + \***********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var NATIVE_WEAK_MAP = __webpack_require__(/*! ../internals/weak-map-basic-detection */ "../node_modules/core-js/internals/weak-map-basic-detection.js"); +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var isObject = __webpack_require__(/*! ../internals/is-object */ "../node_modules/core-js/internals/is-object.js"); +var createNonEnumerableProperty = __webpack_require__(/*! ../internals/create-non-enumerable-property */ "../node_modules/core-js/internals/create-non-enumerable-property.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var shared = __webpack_require__(/*! ../internals/shared-store */ "../node_modules/core-js/internals/shared-store.js"); +var sharedKey = __webpack_require__(/*! ../internals/shared-key */ "../node_modules/core-js/internals/shared-key.js"); +var hiddenKeys = __webpack_require__(/*! ../internals/hidden-keys */ "../node_modules/core-js/internals/hidden-keys.js"); + +var OBJECT_ALREADY_INITIALIZED = 'Object already initialized'; +var TypeError = global.TypeError; +var WeakMap = global.WeakMap; +var set, get, has; + +var enforce = function (it) { + return has(it) ? get(it) : set(it, {}); +}; + +var getterFor = function (TYPE) { + return function (it) { + var state; + if (!isObject(it) || (state = get(it)).type !== TYPE) { + throw new TypeError('Incompatible receiver, ' + TYPE + ' required'); + } return state; + }; +}; + +if (NATIVE_WEAK_MAP || shared.state) { + var store = shared.state || (shared.state = new WeakMap()); + /* eslint-disable no-self-assign -- prototype methods protection */ + store.get = store.get; + store.has = store.has; + store.set = store.set; + /* eslint-enable no-self-assign -- prototype methods protection */ + set = function (it, metadata) { + if (store.has(it)) throw new TypeError(OBJECT_ALREADY_INITIALIZED); + metadata.facade = it; + store.set(it, metadata); + return metadata; + }; + get = function (it) { + return store.get(it) || {}; + }; + has = function (it) { + return store.has(it); + }; +} else { + var STATE = sharedKey('state'); + hiddenKeys[STATE] = true; + set = function (it, metadata) { + if (hasOwn(it, STATE)) throw new TypeError(OBJECT_ALREADY_INITIALIZED); + metadata.facade = it; + createNonEnumerableProperty(it, STATE, metadata); + return metadata; + }; + get = function (it) { + return hasOwn(it, STATE) ? it[STATE] : {}; + }; + has = function (it) { + return hasOwn(it, STATE); + }; +} + +module.exports = { + set: set, + get: get, + has: has, + enforce: enforce, + getterFor: getterFor +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-array.js": +/*!*****************************************************!*\ + !*** ../node_modules/core-js/internals/is-array.js ***! + \*****************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var classof = __webpack_require__(/*! ../internals/classof-raw */ "../node_modules/core-js/internals/classof-raw.js"); + +// `IsArray` abstract operation +// https://tc39.es/ecma262/#sec-isarray +// eslint-disable-next-line es/no-array-isarray -- safe +module.exports = Array.isArray || function isArray(argument) { + return classof(argument) === 'Array'; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-callable.js": +/*!********************************************************!*\ + !*** ../node_modules/core-js/internals/is-callable.js ***! + \********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var $documentAll = __webpack_require__(/*! ../internals/document-all */ "../node_modules/core-js/internals/document-all.js"); + +var documentAll = $documentAll.all; + +// `IsCallable` abstract operation +// https://tc39.es/ecma262/#sec-iscallable +module.exports = $documentAll.IS_HTMLDDA ? function (argument) { + return typeof argument == 'function' || argument === documentAll; +} : function (argument) { + return typeof argument == 'function'; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-forced.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/is-forced.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); + +var replacement = /#|\.prototype\./; + +var isForced = function (feature, detection) { + var value = data[normalize(feature)]; + return value === POLYFILL ? true + : value === NATIVE ? false + : isCallable(detection) ? fails(detection) + : !!detection; +}; + +var normalize = isForced.normalize = function (string) { + return String(string).replace(replacement, '.').toLowerCase(); +}; + +var data = isForced.data = {}; +var NATIVE = isForced.NATIVE = 'N'; +var POLYFILL = isForced.POLYFILL = 'P'; + +module.exports = isForced; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-null-or-undefined.js": +/*!*****************************************************************!*\ + !*** ../node_modules/core-js/internals/is-null-or-undefined.js ***! + \*****************************************************************/ +/***/ ((module) => { + +"use strict"; + +// we can't use just `it == null` since of `document.all` special case +// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot-aec +module.exports = function (it) { + return it === null || it === undefined; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-object.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/is-object.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var $documentAll = __webpack_require__(/*! ../internals/document-all */ "../node_modules/core-js/internals/document-all.js"); + +var documentAll = $documentAll.all; + +module.exports = $documentAll.IS_HTMLDDA ? function (it) { + return typeof it == 'object' ? it !== null : isCallable(it) || it === documentAll; +} : function (it) { + return typeof it == 'object' ? it !== null : isCallable(it); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-pure.js": +/*!****************************************************!*\ + !*** ../node_modules/core-js/internals/is-pure.js ***! + \****************************************************/ +/***/ ((module) => { + +"use strict"; + +module.exports = false; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-symbol.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/is-symbol.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var getBuiltIn = __webpack_require__(/*! ../internals/get-built-in */ "../node_modules/core-js/internals/get-built-in.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var isPrototypeOf = __webpack_require__(/*! ../internals/object-is-prototype-of */ "../node_modules/core-js/internals/object-is-prototype-of.js"); +var USE_SYMBOL_AS_UID = __webpack_require__(/*! ../internals/use-symbol-as-uid */ "../node_modules/core-js/internals/use-symbol-as-uid.js"); + +var $Object = Object; + +module.exports = USE_SYMBOL_AS_UID ? function (it) { + return typeof it == 'symbol'; +} : function (it) { + var $Symbol = getBuiltIn('Symbol'); + return isCallable($Symbol) && isPrototypeOf($Symbol.prototype, $Object(it)); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/length-of-array-like.js": +/*!*****************************************************************!*\ + !*** ../node_modules/core-js/internals/length-of-array-like.js ***! + \*****************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var toLength = __webpack_require__(/*! ../internals/to-length */ "../node_modules/core-js/internals/to-length.js"); + +// `LengthOfArrayLike` abstract operation +// https://tc39.es/ecma262/#sec-lengthofarraylike +module.exports = function (obj) { + return toLength(obj.length); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/make-built-in.js": +/*!**********************************************************!*\ + !*** ../node_modules/core-js/internals/make-built-in.js ***! + \**********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var CONFIGURABLE_FUNCTION_NAME = (__webpack_require__(/*! ../internals/function-name */ "../node_modules/core-js/internals/function-name.js").CONFIGURABLE); +var inspectSource = __webpack_require__(/*! ../internals/inspect-source */ "../node_modules/core-js/internals/inspect-source.js"); +var InternalStateModule = __webpack_require__(/*! ../internals/internal-state */ "../node_modules/core-js/internals/internal-state.js"); + +var enforceInternalState = InternalStateModule.enforce; +var getInternalState = InternalStateModule.get; +var $String = String; +// eslint-disable-next-line es/no-object-defineproperty -- safe +var defineProperty = Object.defineProperty; +var stringSlice = uncurryThis(''.slice); +var replace = uncurryThis(''.replace); +var join = uncurryThis([].join); + +var CONFIGURABLE_LENGTH = DESCRIPTORS && !fails(function () { + return defineProperty(function () { /* empty */ }, 'length', { value: 8 }).length !== 8; +}); + +var TEMPLATE = String(String).split('String'); + +var makeBuiltIn = module.exports = function (value, name, options) { + if (stringSlice($String(name), 0, 7) === 'Symbol(') { + name = '[' + replace($String(name), /^Symbol\(([^)]*)\)/, '$1') + ']'; + } + if (options && options.getter) name = 'get ' + name; + if (options && options.setter) name = 'set ' + name; + if (!hasOwn(value, 'name') || (CONFIGURABLE_FUNCTION_NAME && value.name !== name)) { + if (DESCRIPTORS) defineProperty(value, 'name', { value: name, configurable: true }); + else value.name = name; + } + if (CONFIGURABLE_LENGTH && options && hasOwn(options, 'arity') && value.length !== options.arity) { + defineProperty(value, 'length', { value: options.arity }); + } + try { + if (options && hasOwn(options, 'constructor') && options.constructor) { + if (DESCRIPTORS) defineProperty(value, 'prototype', { writable: false }); + // in V8 ~ Chrome 53, prototypes of some methods, like `Array.prototype.values`, are non-writable + } else if (value.prototype) value.prototype = undefined; + } catch (error) { /* empty */ } + var state = enforceInternalState(value); + if (!hasOwn(state, 'source')) { + state.source = join(TEMPLATE, typeof name == 'string' ? name : ''); + } return value; +}; + +// add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative +// eslint-disable-next-line no-extend-native -- required +Function.prototype.toString = makeBuiltIn(function toString() { + return isCallable(this) && getInternalState(this).source || inspectSource(this); +}, 'toString'); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/math-trunc.js": +/*!*******************************************************!*\ + !*** ../node_modules/core-js/internals/math-trunc.js ***! + \*******************************************************/ +/***/ ((module) => { + +"use strict"; + +var ceil = Math.ceil; +var floor = Math.floor; + +// `Math.trunc` method +// https://tc39.es/ecma262/#sec-math.trunc +// eslint-disable-next-line es/no-math-trunc -- safe +module.exports = Math.trunc || function trunc(x) { + var n = +x; + return (n > 0 ? floor : ceil)(n); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-define-property.js": +/*!*******************************************************************!*\ + !*** ../node_modules/core-js/internals/object-define-property.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var IE8_DOM_DEFINE = __webpack_require__(/*! ../internals/ie8-dom-define */ "../node_modules/core-js/internals/ie8-dom-define.js"); +var V8_PROTOTYPE_DEFINE_BUG = __webpack_require__(/*! ../internals/v8-prototype-define-bug */ "../node_modules/core-js/internals/v8-prototype-define-bug.js"); +var anObject = __webpack_require__(/*! ../internals/an-object */ "../node_modules/core-js/internals/an-object.js"); +var toPropertyKey = __webpack_require__(/*! ../internals/to-property-key */ "../node_modules/core-js/internals/to-property-key.js"); + +var $TypeError = TypeError; +// eslint-disable-next-line es/no-object-defineproperty -- safe +var $defineProperty = Object.defineProperty; +// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe +var $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; +var ENUMERABLE = 'enumerable'; +var CONFIGURABLE = 'configurable'; +var WRITABLE = 'writable'; + +// `Object.defineProperty` method +// https://tc39.es/ecma262/#sec-object.defineproperty +exports.f = DESCRIPTORS ? V8_PROTOTYPE_DEFINE_BUG ? function defineProperty(O, P, Attributes) { + anObject(O); + P = toPropertyKey(P); + anObject(Attributes); + if (typeof O === 'function' && P === 'prototype' && 'value' in Attributes && WRITABLE in Attributes && !Attributes[WRITABLE]) { + var current = $getOwnPropertyDescriptor(O, P); + if (current && current[WRITABLE]) { + O[P] = Attributes.value; + Attributes = { + configurable: CONFIGURABLE in Attributes ? Attributes[CONFIGURABLE] : current[CONFIGURABLE], + enumerable: ENUMERABLE in Attributes ? Attributes[ENUMERABLE] : current[ENUMERABLE], + writable: false + }; + } + } return $defineProperty(O, P, Attributes); +} : $defineProperty : function defineProperty(O, P, Attributes) { + anObject(O); + P = toPropertyKey(P); + anObject(Attributes); + if (IE8_DOM_DEFINE) try { + return $defineProperty(O, P, Attributes); + } catch (error) { /* empty */ } + if ('get' in Attributes || 'set' in Attributes) throw new $TypeError('Accessors not supported'); + if ('value' in Attributes) O[P] = Attributes.value; + return O; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-get-own-property-descriptor.js": +/*!*******************************************************************************!*\ + !*** ../node_modules/core-js/internals/object-get-own-property-descriptor.js ***! + \*******************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var call = __webpack_require__(/*! ../internals/function-call */ "../node_modules/core-js/internals/function-call.js"); +var propertyIsEnumerableModule = __webpack_require__(/*! ../internals/object-property-is-enumerable */ "../node_modules/core-js/internals/object-property-is-enumerable.js"); +var createPropertyDescriptor = __webpack_require__(/*! ../internals/create-property-descriptor */ "../node_modules/core-js/internals/create-property-descriptor.js"); +var toIndexedObject = __webpack_require__(/*! ../internals/to-indexed-object */ "../node_modules/core-js/internals/to-indexed-object.js"); +var toPropertyKey = __webpack_require__(/*! ../internals/to-property-key */ "../node_modules/core-js/internals/to-property-key.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var IE8_DOM_DEFINE = __webpack_require__(/*! ../internals/ie8-dom-define */ "../node_modules/core-js/internals/ie8-dom-define.js"); + +// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe +var $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + +// `Object.getOwnPropertyDescriptor` method +// https://tc39.es/ecma262/#sec-object.getownpropertydescriptor +exports.f = DESCRIPTORS ? $getOwnPropertyDescriptor : function getOwnPropertyDescriptor(O, P) { + O = toIndexedObject(O); + P = toPropertyKey(P); + if (IE8_DOM_DEFINE) try { + return $getOwnPropertyDescriptor(O, P); + } catch (error) { /* empty */ } + if (hasOwn(O, P)) return createPropertyDescriptor(!call(propertyIsEnumerableModule.f, O, P), O[P]); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-get-own-property-names.js": +/*!**************************************************************************!*\ + !*** ../node_modules/core-js/internals/object-get-own-property-names.js ***! + \**************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + +var internalObjectKeys = __webpack_require__(/*! ../internals/object-keys-internal */ "../node_modules/core-js/internals/object-keys-internal.js"); +var enumBugKeys = __webpack_require__(/*! ../internals/enum-bug-keys */ "../node_modules/core-js/internals/enum-bug-keys.js"); + +var hiddenKeys = enumBugKeys.concat('length', 'prototype'); + +// `Object.getOwnPropertyNames` method +// https://tc39.es/ecma262/#sec-object.getownpropertynames +// eslint-disable-next-line es/no-object-getownpropertynames -- safe +exports.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) { + return internalObjectKeys(O, hiddenKeys); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-get-own-property-symbols.js": +/*!****************************************************************************!*\ + !*** ../node_modules/core-js/internals/object-get-own-property-symbols.js ***! + \****************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +// eslint-disable-next-line es/no-object-getownpropertysymbols -- safe +exports.f = Object.getOwnPropertySymbols; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-is-prototype-of.js": +/*!*******************************************************************!*\ + !*** ../node_modules/core-js/internals/object-is-prototype-of.js ***! + \*******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); + +module.exports = uncurryThis({}.isPrototypeOf); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-keys-internal.js": +/*!*****************************************************************!*\ + !*** ../node_modules/core-js/internals/object-keys-internal.js ***! + \*****************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var toIndexedObject = __webpack_require__(/*! ../internals/to-indexed-object */ "../node_modules/core-js/internals/to-indexed-object.js"); +var indexOf = (__webpack_require__(/*! ../internals/array-includes */ "../node_modules/core-js/internals/array-includes.js").indexOf); +var hiddenKeys = __webpack_require__(/*! ../internals/hidden-keys */ "../node_modules/core-js/internals/hidden-keys.js"); + +var push = uncurryThis([].push); + +module.exports = function (object, names) { + var O = toIndexedObject(object); + var i = 0; + var result = []; + var key; + for (key in O) !hasOwn(hiddenKeys, key) && hasOwn(O, key) && push(result, key); + // Don't enum bug & hidden keys + while (names.length > i) if (hasOwn(O, key = names[i++])) { + ~indexOf(result, key) || push(result, key); + } + return result; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-property-is-enumerable.js": +/*!**************************************************************************!*\ + !*** ../node_modules/core-js/internals/object-property-is-enumerable.js ***! + \**************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +var $propertyIsEnumerable = {}.propertyIsEnumerable; +// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe +var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + +// Nashorn ~ JDK8 bug +var NASHORN_BUG = getOwnPropertyDescriptor && !$propertyIsEnumerable.call({ 1: 2 }, 1); + +// `Object.prototype.propertyIsEnumerable` method implementation +// https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable +exports.f = NASHORN_BUG ? function propertyIsEnumerable(V) { + var descriptor = getOwnPropertyDescriptor(this, V); + return !!descriptor && descriptor.enumerable; +} : $propertyIsEnumerable; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/ordinary-to-primitive.js": +/*!******************************************************************!*\ + !*** ../node_modules/core-js/internals/ordinary-to-primitive.js ***! + \******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var call = __webpack_require__(/*! ../internals/function-call */ "../node_modules/core-js/internals/function-call.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var isObject = __webpack_require__(/*! ../internals/is-object */ "../node_modules/core-js/internals/is-object.js"); + +var $TypeError = TypeError; + +// `OrdinaryToPrimitive` abstract operation +// https://tc39.es/ecma262/#sec-ordinarytoprimitive +module.exports = function (input, pref) { + var fn, val; + if (pref === 'string' && isCallable(fn = input.toString) && !isObject(val = call(fn, input))) return val; + if (isCallable(fn = input.valueOf) && !isObject(val = call(fn, input))) return val; + if (pref !== 'string' && isCallable(fn = input.toString) && !isObject(val = call(fn, input))) return val; + throw new $TypeError("Can't convert object to primitive value"); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/own-keys.js": +/*!*****************************************************!*\ + !*** ../node_modules/core-js/internals/own-keys.js ***! + \*****************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var getBuiltIn = __webpack_require__(/*! ../internals/get-built-in */ "../node_modules/core-js/internals/get-built-in.js"); +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var getOwnPropertyNamesModule = __webpack_require__(/*! ../internals/object-get-own-property-names */ "../node_modules/core-js/internals/object-get-own-property-names.js"); +var getOwnPropertySymbolsModule = __webpack_require__(/*! ../internals/object-get-own-property-symbols */ "../node_modules/core-js/internals/object-get-own-property-symbols.js"); +var anObject = __webpack_require__(/*! ../internals/an-object */ "../node_modules/core-js/internals/an-object.js"); + +var concat = uncurryThis([].concat); + +// all object keys, includes non-enumerable and symbols +module.exports = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) { + var keys = getOwnPropertyNamesModule.f(anObject(it)); + var getOwnPropertySymbols = getOwnPropertySymbolsModule.f; + return getOwnPropertySymbols ? concat(keys, getOwnPropertySymbols(it)) : keys; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/require-object-coercible.js": +/*!*********************************************************************!*\ + !*** ../node_modules/core-js/internals/require-object-coercible.js ***! + \*********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var isNullOrUndefined = __webpack_require__(/*! ../internals/is-null-or-undefined */ "../node_modules/core-js/internals/is-null-or-undefined.js"); + +var $TypeError = TypeError; + +// `RequireObjectCoercible` abstract operation +// https://tc39.es/ecma262/#sec-requireobjectcoercible +module.exports = function (it) { + if (isNullOrUndefined(it)) throw new $TypeError("Can't call method on " + it); + return it; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/shared-key.js": +/*!*******************************************************!*\ + !*** ../node_modules/core-js/internals/shared-key.js ***! + \*******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var shared = __webpack_require__(/*! ../internals/shared */ "../node_modules/core-js/internals/shared.js"); +var uid = __webpack_require__(/*! ../internals/uid */ "../node_modules/core-js/internals/uid.js"); + +var keys = shared('keys'); + +module.exports = function (key) { + return keys[key] || (keys[key] = uid(key)); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/shared-store.js": +/*!*********************************************************!*\ + !*** ../node_modules/core-js/internals/shared-store.js ***! + \*********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var defineGlobalProperty = __webpack_require__(/*! ../internals/define-global-property */ "../node_modules/core-js/internals/define-global-property.js"); + +var SHARED = '__core-js_shared__'; +var store = global[SHARED] || defineGlobalProperty(SHARED, {}); + +module.exports = store; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/shared.js": +/*!***************************************************!*\ + !*** ../node_modules/core-js/internals/shared.js ***! + \***************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var IS_PURE = __webpack_require__(/*! ../internals/is-pure */ "../node_modules/core-js/internals/is-pure.js"); +var store = __webpack_require__(/*! ../internals/shared-store */ "../node_modules/core-js/internals/shared-store.js"); + +(module.exports = function (key, value) { + return store[key] || (store[key] = value !== undefined ? value : {}); +})('versions', []).push({ + version: '3.33.2', + mode: IS_PURE ? 'pure' : 'global', + copyright: '© 2014-2023 Denis Pushkarev (zloirock.ru)', + license: 'https://github.com/zloirock/core-js/blob/v3.33.2/LICENSE', + source: 'https://github.com/zloirock/core-js' +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/symbol-constructor-detection.js": +/*!*************************************************************************!*\ + !*** ../node_modules/core-js/internals/symbol-constructor-detection.js ***! + \*************************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +/* eslint-disable es/no-symbol -- required for testing */ +var V8_VERSION = __webpack_require__(/*! ../internals/engine-v8-version */ "../node_modules/core-js/internals/engine-v8-version.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); + +var $String = global.String; + +// eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing +module.exports = !!Object.getOwnPropertySymbols && !fails(function () { + var symbol = Symbol('symbol detection'); + // Chrome 38 Symbol has incorrect toString conversion + // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances + // nb: Do not call `String` directly to avoid this being optimized out to `symbol+''` which will, + // of course, fail. + return !$String(symbol) || !(Object(symbol) instanceof Symbol) || + // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances + !Symbol.sham && V8_VERSION && V8_VERSION < 41; +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-absolute-index.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/to-absolute-index.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var toIntegerOrInfinity = __webpack_require__(/*! ../internals/to-integer-or-infinity */ "../node_modules/core-js/internals/to-integer-or-infinity.js"); + +var max = Math.max; +var min = Math.min; + +// Helper for a popular repeating case of the spec: +// Let integer be ? ToInteger(index). +// If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length). +module.exports = function (index, length) { + var integer = toIntegerOrInfinity(index); + return integer < 0 ? max(integer + length, 0) : min(integer, length); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-indexed-object.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/to-indexed-object.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +// toObject with fallback for non-array-like ES3 strings +var IndexedObject = __webpack_require__(/*! ../internals/indexed-object */ "../node_modules/core-js/internals/indexed-object.js"); +var requireObjectCoercible = __webpack_require__(/*! ../internals/require-object-coercible */ "../node_modules/core-js/internals/require-object-coercible.js"); + +module.exports = function (it) { + return IndexedObject(requireObjectCoercible(it)); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-integer-or-infinity.js": +/*!*******************************************************************!*\ + !*** ../node_modules/core-js/internals/to-integer-or-infinity.js ***! + \*******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var trunc = __webpack_require__(/*! ../internals/math-trunc */ "../node_modules/core-js/internals/math-trunc.js"); + +// `ToIntegerOrInfinity` abstract operation +// https://tc39.es/ecma262/#sec-tointegerorinfinity +module.exports = function (argument) { + var number = +argument; + // eslint-disable-next-line no-self-compare -- NaN check + return number !== number || number === 0 ? 0 : trunc(number); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-length.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/to-length.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var toIntegerOrInfinity = __webpack_require__(/*! ../internals/to-integer-or-infinity */ "../node_modules/core-js/internals/to-integer-or-infinity.js"); + +var min = Math.min; + +// `ToLength` abstract operation +// https://tc39.es/ecma262/#sec-tolength +module.exports = function (argument) { + return argument > 0 ? min(toIntegerOrInfinity(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991 +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-object.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/to-object.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var requireObjectCoercible = __webpack_require__(/*! ../internals/require-object-coercible */ "../node_modules/core-js/internals/require-object-coercible.js"); + +var $Object = Object; + +// `ToObject` abstract operation +// https://tc39.es/ecma262/#sec-toobject +module.exports = function (argument) { + return $Object(requireObjectCoercible(argument)); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-primitive.js": +/*!*********************************************************!*\ + !*** ../node_modules/core-js/internals/to-primitive.js ***! + \*********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var call = __webpack_require__(/*! ../internals/function-call */ "../node_modules/core-js/internals/function-call.js"); +var isObject = __webpack_require__(/*! ../internals/is-object */ "../node_modules/core-js/internals/is-object.js"); +var isSymbol = __webpack_require__(/*! ../internals/is-symbol */ "../node_modules/core-js/internals/is-symbol.js"); +var getMethod = __webpack_require__(/*! ../internals/get-method */ "../node_modules/core-js/internals/get-method.js"); +var ordinaryToPrimitive = __webpack_require__(/*! ../internals/ordinary-to-primitive */ "../node_modules/core-js/internals/ordinary-to-primitive.js"); +var wellKnownSymbol = __webpack_require__(/*! ../internals/well-known-symbol */ "../node_modules/core-js/internals/well-known-symbol.js"); + +var $TypeError = TypeError; +var TO_PRIMITIVE = wellKnownSymbol('toPrimitive'); + +// `ToPrimitive` abstract operation +// https://tc39.es/ecma262/#sec-toprimitive +module.exports = function (input, pref) { + if (!isObject(input) || isSymbol(input)) return input; + var exoticToPrim = getMethod(input, TO_PRIMITIVE); + var result; + if (exoticToPrim) { + if (pref === undefined) pref = 'default'; + result = call(exoticToPrim, input, pref); + if (!isObject(result) || isSymbol(result)) return result; + throw new $TypeError("Can't convert object to primitive value"); + } + if (pref === undefined) pref = 'number'; + return ordinaryToPrimitive(input, pref); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-property-key.js": +/*!************************************************************!*\ + !*** ../node_modules/core-js/internals/to-property-key.js ***! + \************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var toPrimitive = __webpack_require__(/*! ../internals/to-primitive */ "../node_modules/core-js/internals/to-primitive.js"); +var isSymbol = __webpack_require__(/*! ../internals/is-symbol */ "../node_modules/core-js/internals/is-symbol.js"); + +// `ToPropertyKey` abstract operation +// https://tc39.es/ecma262/#sec-topropertykey +module.exports = function (argument) { + var key = toPrimitive(argument, 'string'); + return isSymbol(key) ? key : key + ''; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/try-to-string.js": +/*!**********************************************************!*\ + !*** ../node_modules/core-js/internals/try-to-string.js ***! + \**********************************************************/ +/***/ ((module) => { + +"use strict"; + +var $String = String; + +module.exports = function (argument) { + try { + return $String(argument); + } catch (error) { + return 'Object'; + } +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/uid.js": +/*!************************************************!*\ + !*** ../node_modules/core-js/internals/uid.js ***! + \************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); + +var id = 0; +var postfix = Math.random(); +var toString = uncurryThis(1.0.toString); + +module.exports = function (key) { + return 'Symbol(' + (key === undefined ? '' : key) + ')_' + toString(++id + postfix, 36); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/use-symbol-as-uid.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/use-symbol-as-uid.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +/* eslint-disable es/no-symbol -- required for testing */ +var NATIVE_SYMBOL = __webpack_require__(/*! ../internals/symbol-constructor-detection */ "../node_modules/core-js/internals/symbol-constructor-detection.js"); + +module.exports = NATIVE_SYMBOL + && !Symbol.sham + && typeof Symbol.iterator == 'symbol'; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/v8-prototype-define-bug.js": +/*!********************************************************************!*\ + !*** ../node_modules/core-js/internals/v8-prototype-define-bug.js ***! + \********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); + +// V8 ~ Chrome 36- +// https://bugs.chromium.org/p/v8/issues/detail?id=3334 +module.exports = DESCRIPTORS && fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty(function () { /* empty */ }, 'prototype', { + value: 42, + writable: false + }).prototype !== 42; +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/weak-map-basic-detection.js": +/*!*********************************************************************!*\ + !*** ../node_modules/core-js/internals/weak-map-basic-detection.js ***! + \*********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); + +var WeakMap = global.WeakMap; + +module.exports = isCallable(WeakMap) && /native code/.test(String(WeakMap)); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/well-known-symbol.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/well-known-symbol.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var shared = __webpack_require__(/*! ../internals/shared */ "../node_modules/core-js/internals/shared.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var uid = __webpack_require__(/*! ../internals/uid */ "../node_modules/core-js/internals/uid.js"); +var NATIVE_SYMBOL = __webpack_require__(/*! ../internals/symbol-constructor-detection */ "../node_modules/core-js/internals/symbol-constructor-detection.js"); +var USE_SYMBOL_AS_UID = __webpack_require__(/*! ../internals/use-symbol-as-uid */ "../node_modules/core-js/internals/use-symbol-as-uid.js"); + +var Symbol = global.Symbol; +var WellKnownSymbolsStore = shared('wks'); +var createWellKnownSymbol = USE_SYMBOL_AS_UID ? Symbol['for'] || Symbol : Symbol && Symbol.withoutSetter || uid; + +module.exports = function (name) { + if (!hasOwn(WellKnownSymbolsStore, name)) { + WellKnownSymbolsStore[name] = NATIVE_SYMBOL && hasOwn(Symbol, name) + ? Symbol[name] + : createWellKnownSymbol('Symbol.' + name); + } return WellKnownSymbolsStore[name]; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/modules/es.array.push.js": +/*!********************************************************!*\ + !*** ../node_modules/core-js/modules/es.array.push.js ***! + \********************************************************/ +/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var $ = __webpack_require__(/*! ../internals/export */ "../node_modules/core-js/internals/export.js"); +var toObject = __webpack_require__(/*! ../internals/to-object */ "../node_modules/core-js/internals/to-object.js"); +var lengthOfArrayLike = __webpack_require__(/*! ../internals/length-of-array-like */ "../node_modules/core-js/internals/length-of-array-like.js"); +var setArrayLength = __webpack_require__(/*! ../internals/array-set-length */ "../node_modules/core-js/internals/array-set-length.js"); +var doesNotExceedSafeInteger = __webpack_require__(/*! ../internals/does-not-exceed-safe-integer */ "../node_modules/core-js/internals/does-not-exceed-safe-integer.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); + +var INCORRECT_TO_LENGTH = fails(function () { + return [].push.call({ length: 0x100000000 }, 1) !== 4294967297; +}); + +// V8 and Safari <= 15.4, FF < 23 throws InternalError +// https://bugs.chromium.org/p/v8/issues/detail?id=12681 +var properErrorOnNonWritableLength = function () { + try { + // eslint-disable-next-line es/no-object-defineproperty -- safe + Object.defineProperty([], 'length', { writable: false }).push(); + } catch (error) { + return error instanceof TypeError; + } +}; + +var FORCED = INCORRECT_TO_LENGTH || !properErrorOnNonWritableLength(); + +// `Array.prototype.push` method +// https://tc39.es/ecma262/#sec-array.prototype.push +$({ target: 'Array', proto: true, arity: 1, forced: FORCED }, { + // eslint-disable-next-line no-unused-vars -- required for `.length` + push: function push(item) { + var O = toObject(this); + var len = lengthOfArrayLike(O); + var argCount = arguments.length; + doesNotExceedSafeInteger(len + argCount); + for (var i = 0; i < argCount; i++) { + O[len] = arguments[i]; + len++; + } + setArrayLength(O, len); + return len; + } +}); + + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/global */ +/******/ (() => { +/******/ __webpack_require__.g = (function() { +/******/ if (typeof globalThis === 'object') return globalThis; +/******/ try { +/******/ return this || new Function('return this')(); +/******/ } catch (e) { +/******/ if (typeof window === 'object') return window; +/******/ } +/******/ })(); +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be in strict mode. +(() => { +"use strict"; +/*!***************************************************************!*\ + !*** ../modules/display-conditions/assets/js/editor/index.js ***! + \***************************************************************/ + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +var _module = _interopRequireDefault(__webpack_require__(/*! ./module.js */ "../modules/display-conditions/assets/js/editor/module.js")); +new _module.default(); +})(); + +/******/ })() +; +//# sourceMappingURL=display-conditions.js.map \ No newline at end of file diff --git a/assets/js/display-conditions.min.js b/assets/js/display-conditions.min.js new file mode 100644 index 0000000..f63430b --- /dev/null +++ b/assets/js/display-conditions.min.js @@ -0,0 +1,2 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +(()=>{var e={1420:(e,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.replaceUtmPlaceholders=r.htmlDecodeTextContent=r.arrayToClassName=void 0;r.arrayToClassName=(e,r)=>e.filter((e=>"object"==typeof e?Object.entries(e)[0][1]:e)).map((e=>{const n="object"==typeof e?Object.entries(e)[0][0]:e;return r?r(n):n})).join(" ");r.htmlDecodeTextContent=e=>(new DOMParser).parseFromString(e,"text/html").documentElement.textContent;r.replaceUtmPlaceholders=(e="",r={})=>e&&r?(Object.keys(r).forEach((n=>{const o=new RegExp(`%%${n}%%`,"g");e=e.replace(o,r[n])})),e):e},7824:(e,r,n)=>{"use strict";var o=n(3615),i=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var a=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(7363)),u=n(6626),s=i(n(8564));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}const App=e=>{const[r,n]=(0,a.useState)(!0);(0,a.useEffect)((()=>{if(!r){const r=setTimeout((()=>{e.onClose()}),500);return()=>clearTimeout(r)}}),[r]);return a.default.createElement(u.DirectionProvider,{rtl:e.isRTL},a.default.createElement(u.LocalizationProvider,null,a.default.createElement(u.ThemeProvider,{colorScheme:e.colorScheme},a.default.createElement(u.Dialog,{open:r,fullWidth:!0,maxWidth:"lg",TransitionComponent:u.Fade,transitionDuration:{enter:500,exit:500},sx:{"& .MuiDialog-paper":{height:"calc(100vh - 4rem)",maxHeight:775}}},a.default.createElement(s.default,{getControlValue:e.getControlValue,setControlValue:e.setControlValue,fetchData:e.fetchData,onClose:()=>{n(!1)},conditionsConfig:e.conditionsConfig,setCacheNoticeStatus:e.setCacheNoticeStatus})))))};App.propTypes={colorScheme:o.oneOf(["auto","light","dark"]),isRTL:o.bool,getControlValue:o.func.isRequired,setControlValue:o.func.isRequired,fetchData:o.func.isRequired,onClose:o.func.isRequired,conditionsConfig:o.object.isRequired,setCacheNoticeStatus:o.func.isRequired};r.default=App},6231:(e,r,n)=>{"use strict";var o=n(8003).__,i=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var a=i(n(7363)),u=i(n(7824));class DisplayConditionsBehavior extends Marionette.Behavior{ui(){const e=".eicon-flow.e-control-display-conditions";return{displayConditionsButton:e,displayConditionsPromoButton:`${e}-promo`}}events(){return{"click @ui.displayConditionsButton":"onClickControlButtonDisplayConditions","mouseenter @ui.displayConditionsPromoButton":"onHoverControlButtonDisplayConditions"}}onClickControlButtonDisplayConditions(e){e.stopPropagation(),this.mount()}onHoverControlButtonDisplayConditions(e){e.stopPropagation(),elementor.promotion.showDialog({title:o("Display Conditions","elementor-pro"),content:o("Upgrade to Elementor Pro Advanced to get the Display Conditions feature as well as additional professional and ecommerce widgets","elementor-pro"),targetElement:this.el,actionButton:{url:"https://go.elementor.com/go-pro-advanced-display-conditions/",text:o("Upgrade Now","elementor-pro"),classes:["elementor-button","go-pro"]}})}getRootElement(){let e=window.parent.document.getElementById("elementor-conditions__modal");return e||(e=document.createElement("div"),e.setAttribute("id","elementor-conditions__modal"),e)}mount(){const e=elementor?.getPreferences?.("ui_theme")||"auto",r=elementorCommon.config.isRTL,n=this.getRootElement();window.parent.document.body.appendChild(n),ReactDOM.render(a.default.createElement(u.default,{colorScheme:e,isRTL:r,getControlValue:this.getOption("getControlValue"),setControlValue:this.getOption("setControlValue"),fetchData:this.getOption("fetchData"),onClose:()=>this.unmount(n),conditionsConfig:this.getOption("conditionsConfig"),setCacheNoticeStatus:this.getOption("setCacheNoticeStatus")}),n)}unmount(e){ReactDOM.unmountComponentAtNode(e),e.remove()}}r.default=DisplayConditionsBehavior},3007:(e,r,n)=>{"use strict";var o=n(3615);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(7363)),a=n(8003),u=n(6626);function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}const CacheNotice=({setCacheNoticeStatus:e})=>{const[r,n]=(0,i.useState)(!0);return i.default.createElement(u.Box,null,i.default.createElement(u.Collapse,{in:r,sx:{px:3}},i.default.createElement(u.Alert,{color:"info",severity:"error",variant:"standard",onClose:async()=>{await e()&&n(!1)},sx:{mt:3}},(0,a.__)("Keep in mind: Certain cache plugins can conflict with your display conditions. ","elementor-pro"),i.default.createElement(u.Link,{href:"https://go.elementor.com/app-display-conditions-cache-notice/",underline:"hover",color:"info.main",target:"_blank",sx:{"&:hover":{color:e=>e.palette.info.main}}},(0,a.__)("Learn more","elementor-pro")))))};CacheNotice.propTypes={setCacheNoticeStatus:o.func.isRequired};r.default=CacheNotice},9262:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=_interopRequireWildcard(n(7363)),a=_interopRequireWildcard(n(3615)),u=o(n(4411)),s=n(6626),c=o(n(6506)),f=o(n(3894)),p=o(n(9119)),_=o(n(3921)),g=n(3815),v=n(2381);function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}const ConditionsRepeaterRow=({andConditionIndex:e,orConditionIndex:r})=>{const{selectedConditions:n,conditionsConfig:o,dispatch:a}=(0,u.default)(),{conditions:C,flattenedConditionOptions:b}=o,x=n[r][e],R=x?.condition,E=C[R]?.controls||{},w=Object.keys(E).length;return i.createElement(s.Container,{maxWidth:"md",sx:{display:"flex",gap:.5,mb:1,position:"relative"},className:`and-condition-repeater-row and-condition-${e}`},i.createElement(f.default,{id:"condition-select",value:x.condition||"",onChange:n=>(n=>{const o=n.target.value,i={condition:o,...(0,g.getConditionInitialState)(C,o)};a({type:v.ACTION_TYPES.CHANGE_CONDITION_TYPE,orConditionIndex:r,andConditionIndex:e,conditionToChange:i})})(n),controlCount:w},b.map((({key:e,label:r,isGroup:n})=>n?i.createElement(s.ListSubheader,{key:e},i.createElement(p.default,{variant:"inherit",controlCount:w},r)):i.createElement(s.MenuItem,{key:e,value:e},i.createElement(p.default,{controlCount:w},r))))),Object.keys(E).map((n=>i.createElement(c.default,{key:n,controlKey:n,andConditionIndex:e,orConditionIndex:r,controlCount:w}))),i.createElement(_.default,{orConditionIndex:r,andConditionIndex:e}))};ConditionsRepeaterRow.propTypes={andConditionIndex:a.number.isRequired,orConditionIndex:a.number.isRequired};r.default=ConditionsRepeaterRow},7594:(e,r,n)=>{"use strict";var o=n(3615),i=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var a=i(n(7363)),u=i(n(4411)),s=n(6626),c=i(n(9262));const ConditionsSelectors=({orConditionIndex:e})=>{const{selectedConditions:r}=(0,u.default)(),n=r[e];return a.default.createElement(s.Box,{sx:{my:2,gap:1},className:`or-condition-repeater or-condition-${e}`},n.map(((r,n)=>a.default.createElement(c.default,{key:"or-condition-row-"+n,andConditionIndex:n,orConditionIndex:e}))))};ConditionsSelectors.propTypes={orConditionIndex:o.number.isRequired};r.default=ConditionsSelectors},717:(e,r,n)=>{"use strict";var o=n(8003).__,i=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var a=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(7363)),u=n(6626),s=i(n(4338)),c=i(n(7657));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}r.default=e=>a.createElement(u.Box,{display:"flex",justifyContent:"center",alignItems:"flex-start",sx:{flex:1,overflow:"auto"}},a.createElement(u.Stack,{maxWidth:"md",width:"100%",justifyContent:"center",textAlign:"center",sx:{pt:5,pb:10,px:6}},a.createElement(s.default,{fontSize:"large",sx:{mb:1,mx:"auto"}}),a.createElement(u.Typography,{component:"h5",variant:"h5",color:"text.primary"},o("Set one or more conditions for this element","elementor-pro")),a.createElement(u.Typography,{variant:"subtitle1",color:"text.tertiary",sx:{mb:4}},o("It will only appear on your website when all the conditions are met.","elementor-pro")," ",a.createElement(u.Link,{href:"https://go.elementor.com/app-display-conditions/",target:"_blank",rel:"noreferrer",color:"info.main",underline:"hover",sx:{"&:hover":{color:e=>e.palette.info.main}}},o("Learn more","elementor-pro"))),a.createElement(c.default,e)))},8564:(e,r,n)=>{"use strict";var o=n(3615),i=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var a=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(7363)),u=n(9347),s=n(6836),c=n(6626),f=n(3815),p=i(n(4975)),_=i(n(7173)),g=i(n(717)),v=i(n(3007)),C=n(2381);function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}const Content=({getControlValue:e,setControlValue:r,conditionsConfig:n,onClose:o,fetchData:i,setCacheNoticeStatus:b})=>{const x=e(),R={conditionsConfig:n,selectedConditions:x||[],fetchData:i},[E,w]=a.default.useState(!0),[j,P]=(0,a.useReducer)(u.conditionsReducer,R),[T,I]=(0,a.useState)(!1),{selectedConditions:W}=j;(0,a.useEffect)((()=>{T||I(!0)}),[W]),(0,a.useEffect)((()=>{I(!1)}),[]);const handleEmptyFieldsPerConditionSet=(e,r)=>{let n=!1,o=null;return e.forEach(((e,i)=>{const{condition:a}=e,u=getRequiredControlKeys(a);handleInvalidRequiredKeysPerCondition({requiredKeys:u,andCondition:e,orConditionIndex:r,andConditionIndex:i})&&!n&&(o=i,n=!0)})),{hasFoundInvalidConditionInConditionSet:n,invalidAndConditionIndex:o}},handleInvalidRequiredKeysPerCondition=({requiredKeys:e,andCondition:r,orConditionIndex:o,andConditionIndex:i})=>{const{condition:a}=r;let u=!1;return e.forEach((e=>{const s=r[e],{type:c,variant:p=null}=n.conditions[a].controls[e];s?.length||(0,f.shouldEmptyValuePassValidation)(r.condition,r.comparator)||(u||(u=!0),P({type:C.ACTION_TYPES.SET_ERRORS,andConditionIndex:i,orConditionIndex:o,errors:{[e]:(0,f.getInvalidInputFeedback)(c,p,s,!0)}}))})),u},handleSave=()=>{const{hasFoundInvalidCondition:e,invalidOrConditionIndex:n,invalidAndConditionIndex:i}=(()=>{let e=!1,r=null,n=null;return W.forEach(((o,i)=>{const{hasFoundInvalidConditionInConditionSet:a,invalidAndConditionIndex:u}=handleEmptyFieldsPerConditionSet(o,i);a&&!e&&(e=!0,n=u,r=i)})),{hasFoundInvalidCondition:e,invalidOrConditionIndex:r,invalidAndConditionIndex:n}})();if(e){const e=`.or-condition-repeater.or-condition-${n} .and-condition-repeater-row.and-condition-${i}`,r=document.querySelector(e);setTimeout((()=>r?.scrollIntoView({behavior:"smooth"})),100)}else r([JSON.stringify(getSanitizedConditions())]),o()},getRequiredControlKeys=e=>{const{controls:r}=n.conditions[e];return Object.keys(r).filter((e=>r[e].required))},getSanitizedConditions=()=>W.map((e=>e.map((e=>{const r={...e};return delete r.errors,r}))));return a.default.createElement(a.default.Fragment,null,a.default.createElement(p.default,{onClose:o}),a.default.createElement(c.Divider,{orientation:"horizontal"}),n.show_cache_notice&&a.default.createElement(v.default,{setCacheNoticeStatus:b}),a.default.createElement(s.ConditionsContext.Provider,{value:{dispatch:P,...j}},a.default.createElement(g.default,{showConditions:E,setShowConditions:w})),a.default.createElement(c.Divider,{orientation:"horizontal"}),a.default.createElement(_.default,{onClickSaveButton:()=>handleSave(),showConditions:E,setShowConditions:w,isButtonDisabled:T}))};Content.propTypes={getControlValue:o.func.isRequired,setControlValue:o.func.isRequired,fetchData:o.func.isRequired,onClose:o.func.isRequired,conditionsConfig:o.object.isRequired,setCacheNoticeStatus:o.func.isRequired};r.default=Content},6506:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=_interopRequireWildcard(n(7363)),a=_interopRequireWildcard(n(3615)),u=o(n(4411)),s=o(n(984)),c=o(n(3809)),f=o(n(9295)),p=o(n(7731)),_=o(n(5747)),g=o(n(5083)),v=n(2381),C=n(3815);function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}const ControlRenderer=({controlKey:e,andConditionIndex:r,orConditionIndex:n,controlCount:o})=>{const{conditionsConfig:a,selectedConditions:b,dispatch:x}=(0,u.default)(),{conditions:R}=a,E=b[n][r],w=E.condition,{controls:j={}}=R[w],P=({}=j[e]),{options:T={}}=P;if("__settings"===e)return null;const extractControlPropsFromGlobals=n=>{const i=getControlValueRelatedProps(n),a=getControlInvalidInputRelatedProps(),u={controlKey:e,control:P,conditionIndex:r,condition:E,conditions:R,options:T,onChangeOption:handleChangeOption,controlCount:o};return(0,C.shouldDisableControl)(e,u.condition.comparator)&&(u.disabled=!0),{...i,...a,...u}},getControlValueRelatedProps=r=>{r=(0,C.getControlValue)(r,v.DEFAULT_CONTROL_VALUES[P.type]);const n=(0,C.getControlValue)(P?.default,Object.keys(T)[0]||r);return{defaultValue:n,value:(0,C.getControlValue)(E[e],n),placeholder:P?.placeholder||"",isMultiple:P?.multiple||!1}},getControlInvalidInputRelatedProps=()=>{const r=(E.errors||{})[e]||{},n=r.shouldShow&&r.message||"";return{errorMessage:n,shouldShowError:Boolean(n)}},handleChangeOption=o=>{const{type:i,variant:a}=j[e],u=(0,C.getInvalidInputFeedback)(i,a,o);x({type:v.ACTION_TYPES.CHANGE_CONTROL_VALUE,orConditionIndex:n,andConditionIndex:r,controlKey:e,value:o}),x({type:v.ACTION_TYPES.SET_ERRORS,andConditionIndex:r,orConditionIndex:n,errors:{[e]:u}})};switch(P.type){case v.CONTROL_TYPES.SELECT:return i.createElement(s.default,extractControlPropsFromGlobals());case v.CONTROL_TYPES.MULTIPLE_SELECT:return i.createElement(c.default,extractControlPropsFromGlobals());case v.CONTROL_TYPES.DATE_TIME:return(e=>{switch(e){case"date":return i.createElement(_.default,extractControlPropsFromGlobals());case"time":return i.createElement(g.default,extractControlPropsFromGlobals())}})(P?.variant);case v.CONTROL_TYPES.QUERY:return i.createElement(f.default,extractControlPropsFromGlobals())}return i.createElement(p.default,extractControlPropsFromGlobals())};ControlRenderer.propTypes={controlKey:a.string.isRequired,andConditionIndex:a.number.isRequired,orConditionIndex:a.number.isRequired,controlCount:a.number.isRequired};r.default=ControlRenderer},3809:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=o(n(3119)),a=_interopRequireWildcard(n(7363)),u=a,s=_interopRequireWildcard(n(3615)),c=n(6626),f=o(n(9119));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}const formatValue=e=>Array.isArray(e)?e:[e],AutocompleteControl=({conditions:e,condition:r,controlKey:n,onChangeOption:o,options:s,value:p,shouldShowError:_,errorMessage:g,isMultiple:v,controlCount:C})=>{const[b,x]=(0,a.useState)(formatValue(p)),R=b?.length?"":e[r.condition].label||"";(0,a.useEffect)((()=>{x(formatValue(p))}),[r]);return u.createElement(c.Autocomplete,{multiple:v,id:`select-${n}`,value:b,options:Object.keys(s),getOptionLabel:e=>s[e],sx:{flex:1},ChipProps:{sx:{"&.MuiAutocomplete-tag":{maxWidth:"100px"}}},renderInput:e=>u.createElement(c.TextField,(0,i.default)({error:_,helperText:g},e,{placeholder:R,color:"secondary"})),ListboxProps:{sx:{maxHeight:280}},size:"small",onChange:(e,r)=>{return n=formatValue(r),o(n),void x(n);var n},renderOption:(e,r)=>u.createElement(c.Typography,(0,i.default)({component:"li"},e),u.createElement(f.default,{component:"span",variant:"inherit",noWrap:!0,controlCount:C},s[r])),forcePopupIcon:!Object.keys(s).length<=1})};AutocompleteControl.propTypes={conditions:s.object.isRequired,condition:s.object.isRequired,controlKey:s.string.isRequired,onChangeOption:s.func.isRequired,value:s.array.isRequired,options:s.object.isRequired,errorMessage:s.string.isRequired,shouldShowError:s.bool.isRequired,isMultiple:s.bool.isRequired,optionsStyles:s.object.isRequired,menuStyles:s.object.isRequired,controlCount:s.number.isRequired};r.default=AutocompleteControl},5747:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=_interopRequireWildcard(n(7363)),a=i,u=_interopRequireWildcard(n(3615)),s=n(6626),c=o(n(7693));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}const f="MM-DD-YYYY",formattedValue=e=>(0,c.default)(e,f,!0).isValid()?(0,c.default)(e,f):null,DatePickerControl=({condition:e,onChangeOption:r,controlKey:n,value:o,shouldShowError:u,errorMessage:p})=>{const[_,g]=(0,i.useState)(formattedValue(o));(0,i.useEffect)((()=>{g(formattedValue(o))}),[e]);return a.createElement(s.DatePicker,{value:_,sx:{flex:1},id:`select-${n}`,slotProps:{openPickerButton:{size:"small"},textField:{size:"small",color:"secondary",error:u,helperText:p}},onChange:e=>(e=>{(0,c.default)(e,f,!0).isValid()?(r(e.format(f)),g(formattedValue(e))):r("")})(e)})};DatePickerControl.propTypes={condition:u.object.isRequired,controlKey:u.string.isRequired,onChangeOption:u.func.isRequired,value:u.string,errorMessage:u.string.isRequired,shouldShowError:u.bool.isRequired};r.default=DatePickerControl},9295:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=_interopRequireWildcard(n(7363)),a=o(n(3119)),u=_interopRequireWildcard(n(3615)),s=n(8003),c=n(6836),f=n(6626),p=n(1420),_=o(n(9119));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}const formatValue=e=>Array.isArray(e)?e:[e],QueryControl=({conditions:e,condition:r,control:n,controlKey:o,onChangeOption:u,value:g,shouldShowError:v,errorMessage:C,isMultiple:b,controlCount:x})=>{const{fetchData:R}=(0,i.useContext)(c.ConditionsContext),[E,w]=(0,i.useState)(formatValue(g)),[j,P]=(0,i.useState)([]),[T,I]=(0,i.useState)(!1),W=E?.length?"":e[r.condition].label||"";(0,i.useEffect)((()=>{w(formatValue(g))}),[r]);return i.default.createElement(f.Autocomplete,{multiple:b,id:`select-${o}`,value:E,options:j,getOptionLabel:e=>e?e.text:"",isOptionEqualToValue:(e,r)=>e.id===r.id,filterOptions:e=>e,noOptionsText:(0,s.__)("No results","elementor-pro"),loading:T,loadingText:(0,s.__)("Searching...","elementor-pro"),size:"small",sx:{flex:1},ChipProps:{sx:{"&.MuiAutocomplete-tag":{maxWidth:"100px"}}},renderInput:e=>i.default.createElement(f.TextField,(0,a.default)({},e,{placeholder:W,color:"secondary",error:v,helperText:C,InputProps:{...e.InputProps,endAdornment:i.default.createElement(i.default.Fragment,null,T?i.default.createElement(f.CircularProgress,{color:"inherit",size:20}):null,e.InputProps.endAdornment)}})),ListboxProps:{sx:{maxHeight:280}},onChange:(e,r)=>{return u(n=r),void w(n);var n},onInputChange:(e,r)=>(async(e,r,o)=>{if(""===r)return void P([]);I(!0);const i=(await R(r,n)).filter((e=>(e.text=(0,p.htmlDecodeTextContent)(e.text),!o.some((r=>r?.id===e?.id)))));P(i),I(!1)})(0,r,E),renderOption:(e,r)=>i.default.createElement(f.Typography,(0,a.default)({component:"li"},e),i.default.createElement(_.default,{component:"span",variant:"inherit",noWrap:!0,controlCount:x},r.text))})};QueryControl.propTypes={conditions:u.object.isRequired,condition:u.object.isRequired,onChangeOption:u.func.isRequired,controlKey:u.string.isRequired,control:u.object.isRequired,value:u.array.isRequired,errorMessage:u.string.isRequired,shouldShowError:u.bool.isRequired,isMultiple:u.bool.isRequired,controlCount:u.number.isRequired};r.default=QueryControl},984:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=_interopRequireWildcard(n(7363)),a=i,u=_interopRequireWildcard(n(3615)),s=n(6626),c=o(n(3894)),f=o(n(9119));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}const SelectControl=({condition:e,control:r,controlKey:n,onChangeOption:o,options:u,value:p,controlCount:_})=>{const[g,v]=(0,i.useState)(p);(0,i.useEffect)((()=>{v(p)}),[e]);return a.createElement(c.default,{id:`select-${n}`,value:g,onChange:e=>{return r=e.target.value,o(r),void v(r);var r},disabled:Object.keys(u).length<=1,controlCount:_},Object.entries(u).map((([e,n])=>{if("group"===n.type)return a.createElement(s.ListSubheader,{key:e},a.createElement(f.default,{variant:"inherit",controlCount:_},n.label));const o=r?.disabled_options?.includes(e);return a.createElement(s.MenuItem,{key:e,value:e,disabled:o,className:o&&"hidden"===r?.disabled_type?"elementor-hidden":""},a.createElement(f.default,{controlCount:_},n))})))};SelectControl.propTypes={condition:u.object.isRequired,control:u.object.isRequired,controlKey:u.string.isRequired,onChangeOption:u.func.isRequired,options:u.object.isRequired,value:u.string.isRequired,controlCount:u.number.isRequired};r.default=SelectControl},7731:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=o(n(3119)),a=_interopRequireWildcard(n(7363)),u=a,s=_interopRequireWildcard(n(3615)),c=n(6626),f=n(3815);function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}const TextFieldControl=({condition:e,controlKey:r,control:n,onChangeOption:o,value:s,errorMessage:p,shouldShowError:_,placeholder:g,disabled:v})=>{const[C,b]=(0,a.useState)(s),{step:x=1,min:R=0,variant:E=null}=n,w="number"===E?{type:"number",inputProps:{step:x,min:R}}:{};(0,a.useEffect)((()=>{b(s)}),[e]);return u.createElement(c.TextField,(0,i.default)({},w,{sx:{flex:1},error:_,helperText:p,value:C,id:`text-${r}`,variant:"outlined",onChange:e=>((e,r)=>{let n=null;"number"===r&&(0,f.hasDecimalSeparator)(e)&&(n=Math.floor(parseFloat(e))),o(n??e.trim()),b(n??e)})(e.target.value,E),size:"small",color:"secondary",placeholder:g,disabled:v??!1}))};TextFieldControl.propTypes={condition:s.object.isRequired,controlKey:s.string.isRequired,control:s.object.isRequired,onChangeOption:s.func.isRequired,value:s.oneOfType([s.string,s.number]).isRequired,errorMessage:s.string.isRequired,shouldShowError:s.bool.isRequired,placeholder:s.string.isRequired,disabled:s.bool};r.default=TextFieldControl},5083:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=_interopRequireWildcard(n(7363)),a=i,u=_interopRequireWildcard(n(3615)),s=n(6626),c=o(n(7693));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}const f="HH:mm",p="MM-DD-YYYY "+f,TimePickerControl=({condition:e,controlKey:r,onChangeOption:n,value:o,shouldShowError:u,errorMessage:_})=>{const g=(0,i.useRef)((b=o,(0,c.default)(b,f,!0).isValid()?(0,c.default)(b,f):null)),[v,C]=(0,i.useState)(g.current);var b;(0,i.useEffect)((()=>{C(g.current)}),[e]);return a.createElement(s.TimePicker,{sx:{flex:1},id:`select-${r}`,value:v,slotProps:{textField:{size:"small",error:u,helperText:_}},onChange:e=>(e=>{const r=(0,c.default)(e,p,!0).isValid()?e.format(p):"";n(r),g.current=e,C(e)})(e)})};TimePickerControl.propTypes={condition:u.object.isRequired,control:u.object.isRequired,controlKey:u.string.isRequired,onChangeOption:u.func.isRequired,value:u.string,errorMessage:u.string.isRequired,shouldShowError:u.bool.isRequired};r.default=TimePickerControl},3894:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=o(n(7363)),a=o(n(3119)),u=n(6626),s=n(3815),c=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(3615));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}const ConditionSelect=({controlCount:e,...r})=>i.default.createElement(u.Select,(0,a.default)({},r,{size:"small",sx:{flex:1,textAlign:"start",alignSelf:"flex-start",".MuiSelect-select .MuiTypography-root":{maxWidth:(0,s.getControlValueMaxWidth)(e)}},color:"secondary",MenuProps:{PaperProps:{sx:{maxHeight:280,"& .MuiListSubheader-root":{position:"initial"}}},classes:{paper:"e-conditions-select-menu"}}}));ConditionSelect.propTypes={controlCount:c.number.isRequired};r.default=ConditionSelect},9119:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=o(n(7363)),a=o(n(3119)),u=n(6626),s=n(3815),c=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(3615));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}const ConditionSelectOption=({controlCount:e,sx:r={},...n})=>i.default.createElement(u.Typography,(0,a.default)({variant:"inherit",noWrap:!0},n,{sx:{maxWidth:(0,s.getSelectOptionMaxWidth)(e),...r}}));ConditionSelectOption.propTypes={sx:c.object,isDropdownItem:c.bool,controlCount:c.number.isRequired};r.default=ConditionSelectOption},7173:(e,r,n)=>{"use strict";var o=n(8003).__,i=n(3615);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var a=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(7363)),u=n(6626);function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}const Footer=({onClickSaveButton:e,isButtonDisabled:r})=>a.createElement(u.Stack,{direction:"row",justifyContent:"flex-end",sx:{py:1,px:3}},a.createElement(u.Button,{variant:"contained",className:"save-and-close-button",disabled:!r,onClick:e},o("Save & Close","elementor-pro")));Footer.propTypes={onClickSaveButton:i.func,isButtonDisabled:i.bool.isRequired};r.default=Footer},4975:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=o(n(7363)),a=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(3615)),u=n(8003),s=n(6626),c=o(n(2943)),f=n(4029);function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}const Header=({onClose:e})=>i.default.createElement(s.AppBar,{sx:{fontWeight:"normal"},color:"transparent",position:"relative"},i.default.createElement(s.Toolbar,{variant:"dense"},i.default.createElement(c.default,{sx:{mr:1}}),i.default.createElement(s.Typography,{component:"span",variant:"subtitle2",sx:{fontWeight:"bold",textTransform:"uppercase"}},(0,u.__)("Display Conditions","elementor-pro")),i.default.createElement(s.Stack,{direction:"row",spacing:1,alignItems:"center",sx:{ml:"auto"}},i.default.createElement(s.IconButton,{size:"small","aria-label":(0,u.__)("Close","elementor-pro"),onClick:e,sx:{"&.MuiButtonBase-root":{mr:-1}}},i.default.createElement(f.XIcon,null)))));Header.propTypes={onClose:a.func.isRequired};r.default=Header},2943:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=o(n(3119)),a=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(7363)),u=n(6626);function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}r.default=e=>a.createElement(u.SvgIcon,(0,i.default)({viewBox:"0 0 32 32"},e),a.createElement("path",{fillRule:"evenodd",clipRule:"evenodd",d:"M2.69648 24.8891C0.938383 22.2579 0 19.1645 0 16C0 11.7566 1.68571 7.68687 4.68629 4.68629C7.68687 1.68571 11.7566 0 16 0C19.1645 0 22.2579 0.938383 24.8891 2.69648C27.5203 4.45459 29.5711 6.95344 30.7821 9.87706C31.9931 12.8007 32.3099 16.0177 31.6926 19.1214C31.0752 22.2251 29.5514 25.0761 27.3137 27.3137C25.0761 29.5514 22.2251 31.0752 19.1214 31.6926C16.0177 32.3099 12.8007 31.9931 9.87706 30.7821C6.95344 29.5711 4.45459 27.5203 2.69648 24.8891ZM12.0006 9.33281H9.33437V22.6665H12.0006V9.33281ZM22.6657 9.33281H14.6669V11.9991H22.6657V9.33281ZM22.6657 14.6654H14.6669V17.3316H22.6657V14.6654ZM22.6657 20.0003H14.6669V22.6665H22.6657V20.0003Z"}))},4338:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var i=o(n(3119)),a=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(7363)),u=n(6626);function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}const s=a.forwardRef(((e,r)=>a.createElement(u.SvgIcon,(0,i.default)({viewBox:"0 0 24 24"},e,{ref:r}),a.createElement("path",{fillRule:"evenodd",clipRule:"evenodd",d:"M11 3.75C10.3096 3.75 9.75 4.30964 9.75 5V7C9.75 7.69036 10.3096 8.25 11 8.25H13C13.6904 8.25 14.25 7.69036 14.25 7V5C14.25 4.30964 13.6904 3.75 13 3.75H11ZM12.75 9.75H13C14.5188 9.75 15.75 8.51878 15.75 7V5C15.75 3.48122 14.5188 2.25 13 2.25H11C9.48122 2.25 8.25 3.48122 8.25 5V7C8.25 8.51878 9.48122 9.75 11 9.75H11.25V11.25H8C7.27065 11.25 6.57118 11.5397 6.05546 12.0555C5.53973 12.5712 5.25 13.2707 5.25 14V14.25H5C3.48122 14.25 2.25 15.4812 2.25 17V19C2.25 20.5188 3.48122 21.75 5 21.75H7C8.51878 21.75 9.75 20.5188 9.75 19V17C9.75 15.4812 8.51878 14.25 7 14.25H6.75V14C6.75 13.6685 6.8817 13.3505 7.11612 13.1161C7.35054 12.8817 7.66848 12.75 8 12.75H16C16.3315 12.75 16.6495 12.8817 16.8839 13.1161C17.1183 13.3505 17.25 13.6685 17.25 14V14.25H17C15.4812 14.25 14.25 15.4812 14.25 17V19C14.25 20.5188 15.4812 21.75 17 21.75H19C20.5188 21.75 21.75 20.5188 21.75 19V17C21.75 15.4812 20.5188 14.25 19 14.25H18.75V14C18.75 13.2707 18.4603 12.5712 17.9445 12.0555C17.4288 11.5397 16.7293 11.25 16 11.25H12.75V9.75ZM17 15.75C16.3096 15.75 15.75 16.3096 15.75 17V19C15.75 19.6904 16.3096 20.25 17 20.25H19C19.6904 20.25 20.25 19.6904 20.25 19V17C20.25 16.3096 19.6904 15.75 19 15.75H17ZM5 15.75C4.30964 15.75 3.75 16.3096 3.75 17V19C3.75 19.6904 4.30964 20.25 5 20.25H7C7.69036 20.25 8.25 19.6904 8.25 19V17C8.25 16.3096 7.69036 15.75 7 15.75H5Z"})))),c=(0,u.styled)(s)((({theme:e})=>({"& path":{fill:e.palette.text.primary}})));r.default=c},7657:(e,r,n)=>{"use strict";var o=n(8003).__,i=n(3615),a=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var u=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(7363)),s=n(6626),c=n(4029),f=a(n(7594)),p=a(n(4411)),_=n(3815),g=a(n(712)),v=n(2381);function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}const OrRowGroup=({showConditions:e,setShowConditions:r})=>{const{selectedConditions:n,conditionsConfig:i,dispatch:a}=(0,p.default)(),{conditions:C,conditionsByGroup:b}=i,x=n.length?o("Add condition group","elementor-pro"):o("Add Condition","elementor-pro");return u.default.createElement(s.Box,null,e&&n.map(((e,r)=>u.default.createElement(u.Fragment,{key:r},r>0&&u.default.createElement(g.default,null),u.default.createElement(f.default,{orConditionIndex:r})))),u.default.createElement(s.Button,{variant:"contained",className:"add-or-condition-button",color:"secondary",startIcon:u.default.createElement(c.PlusIcon,null),sx:{mt:1,mb:5},onClick:()=>(()=>{const e=(0,_.getDefaultActiveCondition)(b),n={condition:e,...(0,_.getConditionInitialState)(C,e)};a({type:v.ACTION_TYPES.ADD_OR_CONDITION,andCondition:n}),r(!0)})()},x))};OrRowGroup.propTypes={showConditions:i.bool.isRequired,setShowConditions:i.func.isRequired};r.default=OrRowGroup},712:(e,r,n)=>{"use strict";var o=n(8003).__,i=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var a=i(n(7363)),u=n(6626);r.default=()=>a.default.createElement(u.Divider,{sx:{px:3}},o("OR","elementor-pro"))},3921:(e,r,n)=>{"use strict";var o=n(3615),i=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var a=n(6626),u=n(8003),s=n(4029),c=function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_getRequireWildcardCache(r);if(n&&n.has(e))return n.get(e);var o={__proto__:null},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&Object.prototype.hasOwnProperty.call(e,a)){var u=i?Object.getOwnPropertyDescriptor(e,a):null;u&&(u.get||u.set)?Object.defineProperty(o,a,u):o[a]=e[a]}return o.default=e,n&&n.set(e,o),o}(n(7363)),f=n(3815),p=i(n(4411)),_=n(2381);function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var r=new WeakMap,n=new WeakMap;return(_getRequireWildcardCache=function(e){return e?n:r})(e)}const RowControls=({orConditionIndex:e,andConditionIndex:r})=>{const{conditionsConfig:n,dispatch:o}=(0,p.default)(),{conditions:i,conditionsByGroup:g}=n;return c.createElement(a.Stack,{direction:"row",alignItems:"center",sx:{left:"100%",gap:.5,ml:-1,mt:"2.5px",position:"absolute"}},c.createElement(a.Button,{color:"secondary",variant:"outlined",sx:{px:1,minWidth:"unset"},className:"add-single-condition-button",onClick:()=>{const n=(0,f.getDefaultActiveCondition)(g),a={condition:n,...(0,f.getConditionInitialState)(i,n)};o({type:_.ACTION_TYPES.ADD_AND_CONDITION,andCondition:a,andConditionIndex:r,orConditionIndex:e})}},(0,u.__)("AND","elementor-pro")),c.createElement(a.IconButton,{color:"secondary","aria-label":(0,u.__)("Delete","elementor-pro"),className:"remove-single-condition-button",onClick:()=>{o({type:_.ACTION_TYPES.REMOVE_AND_CONDITION,andConditionIndex:r,orConditionIndex:e})}},c.createElement(s.XIcon,{fontSize:"small"})))};RowControls.propTypes={andConditionIndex:o.number.isRequired,orConditionIndex:o.number.isRequired};r.default=RowControls},6836:(e,r,n)=>{"use strict";var o=n(7363);Object.defineProperty(r,"__esModule",{value:!0}),r.ConditionsContext=void 0;r.ConditionsContext=o.createContext()},4411:(e,r,n)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0;var o=n(7363),i=n(6836);r.default=function useConditions(){return(0,o.useContext)(i.ConditionsContext)}},9013:(e,r,n)=>{"use strict";var o=n(3203);Object.defineProperty(r,"__esModule",{value:!0}),r.default=void 0,n(3517);var i=o(n(6231));class Module extends elementorModules.editor.utils.Module{onElementorInit(){elementor.hooks.addFilter("controls/base/behaviors",this.registerControlBehavior),elementor.channels.editor.on("section:activated",this.highlightIconIfFilled)}registerControlBehavior=(e,r)=>{if("e_display_conditions_trigger"!==r.options.model.get("name"))return e;e||(e={});const n=this._getGroupedConditionKeys(elementor.config.displayConditions||{}),o=this._getFlattenedConditionOptions(n);return e.displayConditions={behaviorClass:i.default,getControlValue:()=>{const e=this.getEditorControlView("e_display_conditions");return e?this._getStructuredConditions(JSON.parse(e.getControlValue()||"[]")):[]},setControlValue:e=>{const r=this.getEditorControlView("e_display_conditions"),n=this.getEditorControlView("e_display_conditions_trigger");if(r&&(r.setValue(e),r.applySavedValue()),n.$el){const e=n.$el.find(".eicon-flow.e-control-display-conditions");this.highlightIcon(e,r)}},fetchData:async(e,r)=>{const n=await this.doAjaxRequest("pro_panel_posts_control_filter_autocomplete",{autocomplete:r.autocomplete,q:e});return n?.results??[]},setCacheNoticeStatus:async()=>{const e=await this.doAjaxRequest("display_conditions_set_cache_notice_status");return e&&(elementor.config.displayConditions.show_cache_notice=!1),e},conditionsConfig:{...elementor.config.displayConditions,conditionsByGroup:n,flattenedConditionOptions:o}},e};highlightIconIfFilled=(e,r)=>{if(!["section_advanced","_section_style","section_layout"].includes(e))return;const n=this.getEditorControlView("e_display_conditions");if(!n)return;const o=r.$childViewContainer.find(".eicon-flow.e-control-display-conditions");this.highlightIcon(o,n)};highlightIcon=(e,r)=>{if(!e[0])return;const n=r.getControlValue()||"[]";("[]"!==n?this._getStructuredConditions(JSON.parse(n)):[]).length?e[0]?.classList?.add("filled"):e[0]?.classList?.remove("filled")};doAjaxRequest=(e,r)=>{try{return new Promise(((n,o)=>{elementorCommon.ajax.addRequest(e,{data:r,error:()=>o(),success:e=>{n(e)}})}))}catch(e){return!1}};_getStructuredConditions=e=>this._shouldConvertConditionsStructure(e)?[e]:e;_shouldConvertConditionsStructure=e=>e.length&&!Array.isArray(e[0]);_getGroupedConditionKeys=e=>Object.keys(e?.groups||{}).reduce(((r,n)=>{const o=this._getConditionKeyByGroup(e.conditions,n);return o.length&&(r[n]=o),r}),{});_getConditionKeyByGroup=(e,r)=>Object.keys(e).filter((n=>r===e[n].group));_getFlattenedConditionOptions=e=>{const{conditions:r={},groups:n={}}=elementor.config.displayConditions||{};return Object.entries(e).reduce(((e,[o,i])=>{const a=i.map((e=>({key:e,label:r[e].label,isGroup:!1})));return e.push({key:o,label:n[o].label,isGroup:!0},...a),e}),[])}}r.default=Module},9347:(e,r,n)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.conditionsReducer=void 0,n(3517);var o=n(2381);r.conditionsReducer=(e,r)=>{switch(r.type){case o.ACTION_TYPES.CHANGE_CONDITION_TYPE:return{...e,selectedConditions:_changeConditionType({...e,...r})};case o.ACTION_TYPES.CHANGE_CONTROL_VALUE:return{...e,selectedConditions:_changeControlValue({...e,...r})};case o.ACTION_TYPES.ADD_AND_CONDITION:return{...e,selectedConditions:_addAndCondition({...e,...r})};case o.ACTION_TYPES.ADD_OR_CONDITION:return{...e,selectedConditions:[...e.selectedConditions,[r.andCondition]]};case o.ACTION_TYPES.REMOVE_AND_CONDITION:return{...e,selectedConditions:_removeAndCondition({...e,...r})};case o.ACTION_TYPES.REMOVE_OR_CONDITION:return{...e,selectedConditions:e.selectedConditions.filter(((e,n)=>n!==r.orConditionIndex))};case o.ACTION_TYPES.SET_ERRORS:return{...e,selectedConditions:_setErrors({...e,...r})};default:return e}};const _changeConditionType=({selectedConditions:e,conditionToChange:r,orConditionIndex:n,andConditionIndex:o})=>{const i=e[n].map(((e,n)=>n===o?r:{...e}));return e.map(((e,r)=>r===n?i:[...e]))},_changeControlValue=({selectedConditions:e,orConditionIndex:r,andConditionIndex:n,controlKey:o,value:i})=>{const a=[...e[r]],u={...{...a[n]},[o]:i},s=a.map(((e,r)=>r===n?u:{...e}));return e.map(((e,n)=>n===r?s:[...e]))},_addAndCondition=({selectedConditions:e,orConditionIndex:r,andConditionIndex:n,andCondition:o})=>{const i=e[r],a=i.reduce(((e,r,a)=>(e.push({...r}),(a===n||i.length===n&&i.length-1===a)&&e.push(o),e)),[]);return e.map(((e,n)=>n===r?a:[...e]))},_removeAndCondition=({selectedConditions:e,orConditionIndex:r,andConditionIndex:n})=>{const o=e[r].reduce(((e,r,o)=>(o!==n&&e.push({...r}),e)),[]);return e.reduce(((e,n,i)=>(i===r&&o.length&&e.push(o),i!==r&&e.push([...n]),e)),[])},_setErrors=({selectedConditions:e,orConditionIndex:r,andConditionIndex:n,errors:o})=>{const i=[...e[r]],a={...i[n]};return a.errors={...a.errors,...o},i[n]=a,e.map(((e,n)=>n===r?[...i]:[...e]))}},2381:(e,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.DISABLED_CONTROL_CONFIG=r.DEFAULT_CONTROL_VALUES=r.CONTROL_TYPES=r.ACTION_TYPES=void 0;r.CONTROL_TYPES={MULTIPLE_SELECT:"select2",SELECT:"select",QUERY:"query",DATE_TIME:"date_time",TEXT_FIELD:"text"},r.DEFAULT_CONTROL_VALUES={select2:[],query:[],select:"",text:"",date_time:null},r.ACTION_TYPES={CHANGE_CONTROL_VALUE:"CHANGE_CONTROL_VALUE",SET_ERRORS:"SET_ERRORS",ADD_OR_CONDITION:"ADD_OR_CONDITION",CHANGE_CONDITION_TYPE:"CHANGE_CONDITION_TYPE",ADD_AND_CONDITION:"ADD_AND_CONDITION",REMOVE_AND_CONDITION:"REMOVE_AND_CONDITION",REMOVE_OR_CONDITION:"REMOVE_OR_CONDITION"},r.DISABLED_CONTROL_CONFIG={CONDITION_NAME:"dynamic_tags",CONTROL_NAME:"dynamic_tag_value",COMPARATORS:["is_empty","is_not_empty"]}},3815:(e,r,n)=>{"use strict";var o=n(8003).__;Object.defineProperty(r,"__esModule",{value:!0}),r.getControlDefaults=r.getConditionInitialState=void 0,r.getControlValue=function getControlValue(e,r){return void 0!==e?e:r},r.getControlValueMaxWidth=function getControlValueMaxWidth(e){return 3===e?190:135},r.getDefaultActiveCondition=function getDefaultActiveCondition(e){return Object.values(e)[0][0]},r.getInvalidInputFeedback=getInvalidInputFeedback,r.getSelectOptionMaxWidth=function getSelectOptionMaxWidth(e){return 3===e?200:150},r.hasDecimalSeparator=function hasDecimalSeparator(e){if(isNaN(parseFloat(e)))return!1;if(-1!==e.toString().indexOf("."))return!0;if(-1!==e.toString().indexOf(","))return!0},r.shouldCastToArray=shouldCastToArray,r.shouldDisableControl=function shouldDisableControl(e,r){return i.DISABLED_CONTROL_CONFIG.CONTROL_NAME===e&&i.DISABLED_CONTROL_CONFIG.COMPARATORS.includes(r)},r.shouldEmptyValuePassValidation=function shouldEmptyValuePassValidation(e,r){return i.DISABLED_CONTROL_CONFIG.CONDITION_NAME===e&&i.DISABLED_CONTROL_CONFIG.COMPARATORS.includes(r)};var i=n(2381);function shouldCastToArray(e){return i.CONTROL_TYPES.MULTIPLE_SELECT===e||i.CONTROL_TYPES.QUERY===e}function getInvalidInputFeedback(e,r,n,o=!1){return n?.length?{}:{message:_getErrorMessage(e,r),shouldShow:o}}const getControlDefaults=(e,r)=>{const{type:n,variant:o=null,options:a}=r,u=r?.default||(a&&i.CONTROL_TYPES.MULTIPLE_SELECT!==n?Object.keys(a)[0]:i.DEFAULT_CONTROL_VALUES[n]),s=shouldCastToArray(n)&&!Array.isArray(u)?[u]:u;return{defaultValue:s,error:getInvalidInputFeedback(n,o,s)}};r.getControlDefaults=getControlDefaults;function _getErrorMessage(e,r=null){return shouldCastToArray(e)?o("Select an option","elementor-pro"):i.CONTROL_TYPES.DATE_TIME===e?o("time"===r?"Select a time":"Select a date","elementor-pro"):o("Enter a value","elementor-pro")}r.getConditionInitialState=(e,r)=>{const{controls:n={}}=e?.[r]||{};return Object.keys(n).reduce(((e,r)=>{if("__settings"===r)return e;const{defaultValue:o,error:i}=getControlDefaults(0,n[r]);return e[r]=o,e.errors[r]=i,e}),{errors:{}})}},7693:function(e){e.exports=function(){"use strict";var e=1e3,r=6e4,n=36e5,o="millisecond",i="second",a="minute",u="hour",s="day",c="week",f="month",p="quarter",_="year",g="date",v="Invalid Date",C=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,b=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,x={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(e){var r=["th","st","nd","rd"],n=e%100;return"["+e+(r[(n-20)%10]||r[n]||r[0])+"]"}},m=function(e,r,n){var o=String(e);return!o||o.length>=r?e:""+Array(r+1-o.length).join(n)+e},R={s:m,z:function(e){var r=-e.utcOffset(),n=Math.abs(r),o=Math.floor(n/60),i=n%60;return(r<=0?"+":"-")+m(o,2,"0")+":"+m(i,2,"0")},m:function t(e,r){if(e.date()1)return t(a[0])}else{var u=e.name;w[u]=e,o=u}return!n&&o&&(E=o),o||!n&&E},O=function(e,r){if(S(e))return e.clone();var n="object"==typeof r?r:{};return n.date=e,n.args=arguments,new I(n)},T=R;T.l=P,T.i=S,T.w=function(e,r){return O(e,{locale:r.$L,utc:r.$u,x:r.$x,$offset:r.$offset})};var I=function(){function M(e){this.$L=P(e.locale,null,!0),this.parse(e),this.$x=this.$x||e.x||{},this[j]=!0}var x=M.prototype;return x.parse=function(e){this.$d=function(e){var r=e.date,n=e.utc;if(null===r)return new Date(NaN);if(T.u(r))return new Date;if(r instanceof Date)return new Date(r);if("string"==typeof r&&!/Z$/i.test(r)){var o=r.match(C);if(o){var i=o[2]-1||0,a=(o[7]||"0").substring(0,3);return n?new Date(Date.UTC(o[1],i,o[3]||1,o[4]||0,o[5]||0,o[6]||0,a)):new Date(o[1],i,o[3]||1,o[4]||0,o[5]||0,o[6]||0,a)}}return new Date(r)}(e),this.init()},x.init=function(){var e=this.$d;this.$y=e.getFullYear(),this.$M=e.getMonth(),this.$D=e.getDate(),this.$W=e.getDay(),this.$H=e.getHours(),this.$m=e.getMinutes(),this.$s=e.getSeconds(),this.$ms=e.getMilliseconds()},x.$utils=function(){return T},x.isValid=function(){return!(this.$d.toString()===v)},x.isSame=function(e,r){var n=O(e);return this.startOf(r)<=n&&n<=this.endOf(r)},x.isAfter=function(e,r){return O(e){"use strict";var o=n(331);function emptyFunction(){}function emptyFunctionWithReset(){}emptyFunctionWithReset.resetWarningCache=emptyFunction,e.exports=function(){function shim(e,r,n,i,a,u){if(u!==o){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function getShim(){return shim}shim.isRequired=shim;var e={array:shim,bigint:shim,bool:shim,func:shim,number:shim,object:shim,string:shim,symbol:shim,any:shim,arrayOf:getShim,element:shim,elementType:shim,instanceOf:getShim,node:shim,objectOf:getShim,oneOf:getShim,oneOfType:getShim,shape:getShim,exact:getShim,checkPropTypes:emptyFunctionWithReset,resetWarningCache:emptyFunction};return e.PropTypes=e,e}},3615:(e,r,n)=>{e.exports=n(8772)()},331:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},7363:e=>{"use strict";e.exports=React},4029:e=>{"use strict";e.exports=elementorV2.icons},6626:e=>{"use strict";e.exports=elementorV2.ui},8003:e=>{"use strict";e.exports=wp.i18n},3119:e=>{function _extends(){return e.exports=_extends=Object.assign?Object.assign.bind():function(e){for(var r=1;r{e.exports=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}},e.exports.__esModule=!0,e.exports.default=e.exports},1575:(e,r,n)=>{"use strict";var o=n(5893),i=n(5545),a=TypeError;e.exports=function(e){if(o(e))return e;throw new a(i(e)+" is not a function")}},9972:(e,r,n)=>{"use strict";var o=n(5287),i=String,a=TypeError;e.exports=function(e){if(o(e))return e;throw new a(i(e)+" is not an object")}},2971:(e,r,n)=>{"use strict";var o=n(9405),i=n(9961),a=n(9969),createMethod=function(e){return function(r,n,u){var s,c=o(r),f=a(c),p=i(u,f);if(e&&n!=n){for(;f>p;)if((s=c[p++])!=s)return!0}else for(;f>p;p++)if((e||p in c)&&c[p]===n)return e||p||0;return!e&&-1}};e.exports={includes:createMethod(!0),indexOf:createMethod(!1)}},8576:(e,r,n)=>{"use strict";var o=n(3877),i=n(5289),a=TypeError,u=Object.getOwnPropertyDescriptor,s=o&&!function(){if(void 0!==this)return!0;try{Object.defineProperty([],"length",{writable:!1}).length=1}catch(e){return e instanceof TypeError}}();e.exports=s?function(e,r){if(i(e)&&!u(e,"length").writable)throw new a("Cannot set read only .length");return e.length=r}:function(e,r){return e.length=r}},3048:(e,r,n)=>{"use strict";var o=n(6406),i=o({}.toString),a=o("".slice);e.exports=function(e){return a(i(e),8,-1)}},779:(e,r,n)=>{"use strict";var o=n(4130),i=n(6627),a=n(10),u=n(7144);e.exports=function(e,r,n){for(var s=i(r),c=u.f,f=a.f,p=0;p{"use strict";var o=n(3877),i=n(7144),a=n(9637);e.exports=o?function(e,r,n){return i.f(e,r,a(1,n))}:function(e,r,n){return e[r]=n,e}},9637:e=>{"use strict";e.exports=function(e,r){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:r}}},7205:(e,r,n)=>{"use strict";var o=n(5893),i=n(7144),a=n(3911),u=n(3630);e.exports=function(e,r,n,s){s||(s={});var c=s.enumerable,f=void 0!==s.name?s.name:r;if(o(n)&&a(n,f,s),s.global)c?e[r]=n:u(r,n);else{try{s.unsafe?e[r]&&(c=!0):delete e[r]}catch(e){}c?e[r]=n:i.f(e,r,{value:n,enumerable:!1,configurable:!s.nonConfigurable,writable:!s.nonWritable})}return e}},3630:(e,r,n)=>{"use strict";var o=n(7802),i=Object.defineProperty;e.exports=function(e,r){try{i(o,e,{value:r,configurable:!0,writable:!0})}catch(n){o[e]=r}return r}},3877:(e,r,n)=>{"use strict";var o=n(5306);e.exports=!o((function(){return 7!==Object.defineProperty({},1,{get:function(){return 7}})[1]}))},811:e=>{"use strict";var r="object"==typeof document&&document.all,n=void 0===r&&void 0!==r;e.exports={all:r,IS_HTMLDDA:n}},9800:(e,r,n)=>{"use strict";var o=n(7802),i=n(5287),a=o.document,u=i(a)&&i(a.createElement);e.exports=function(e){return u?a.createElement(e):{}}},9060:e=>{"use strict";var r=TypeError;e.exports=function(e){if(e>9007199254740991)throw r("Maximum allowed index exceeded");return e}},2626:e=>{"use strict";e.exports="undefined"!=typeof navigator&&String(navigator.userAgent)||""},7245:(e,r,n)=>{"use strict";var o,i,a=n(7802),u=n(2626),s=a.process,c=a.Deno,f=s&&s.versions||c&&c.version,p=f&&f.v8;p&&(i=(o=p.split("."))[0]>0&&o[0]<4?1:+(o[0]+o[1])),!i&&u&&(!(o=u.match(/Edge\/(\d+)/))||o[1]>=74)&&(o=u.match(/Chrome\/(\d+)/))&&(i=+o[1]),e.exports=i},4286:e=>{"use strict";e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2390:(e,r,n)=>{"use strict";var o=n(7802),i=n(10).f,a=n(9251),u=n(7205),s=n(3630),c=n(779),f=n(5031);e.exports=function(e,r){var n,p,_,g,v,C=e.target,b=e.global,x=e.stat;if(n=b?o:x?o[C]||s(C,{}):(o[C]||{}).prototype)for(p in r){if(g=r[p],_=e.dontCallGetSet?(v=i(n,p))&&v.value:n[p],!f(b?p:C+(x?".":"#")+p,e.forced)&&void 0!==_){if(typeof g==typeof _)continue;c(g,_)}(e.sham||_&&_.sham)&&a(g,"sham",!0),u(n,p,g,e)}}},5306:e=>{"use strict";e.exports=function(e){try{return!!e()}catch(e){return!0}}},7219:(e,r,n)=>{"use strict";var o=n(5306);e.exports=!o((function(){var e=function(){}.bind();return"function"!=typeof e||e.hasOwnProperty("prototype")}))},1550:(e,r,n)=>{"use strict";var o=n(7219),i=Function.prototype.call;e.exports=o?i.bind(i):function(){return i.apply(i,arguments)}},9656:(e,r,n)=>{"use strict";var o=n(3877),i=n(4130),a=Function.prototype,u=o&&Object.getOwnPropertyDescriptor,s=i(a,"name"),c=s&&"something"===function something(){}.name,f=s&&(!o||o&&u(a,"name").configurable);e.exports={EXISTS:s,PROPER:c,CONFIGURABLE:f}},6406:(e,r,n)=>{"use strict";var o=n(7219),i=Function.prototype,a=i.call,u=o&&i.bind.bind(a,a);e.exports=o?u:function(e){return function(){return a.apply(e,arguments)}}},1570:(e,r,n)=>{"use strict";var o=n(7802),i=n(5893);e.exports=function(e,r){return arguments.length<2?(n=o[e],i(n)?n:void 0):o[e]&&o[e][r];var n}},6628:(e,r,n)=>{"use strict";var o=n(1575),i=n(7707);e.exports=function(e,r){var n=e[r];return i(n)?void 0:o(n)}},7802:function(e,r,n){"use strict";var check=function(e){return e&&e.Math===Math&&e};e.exports=check("object"==typeof globalThis&&globalThis)||check("object"==typeof window&&window)||check("object"==typeof self&&self)||check("object"==typeof n.g&&n.g)||function(){return this}()||this||Function("return this")()},4130:(e,r,n)=>{"use strict";var o=n(6406),i=n(5864),a=o({}.hasOwnProperty);e.exports=Object.hasOwn||function hasOwn(e,r){return a(i(e),r)}},3421:e=>{"use strict";e.exports={}},3075:(e,r,n)=>{"use strict";var o=n(3877),i=n(5306),a=n(9800);e.exports=!o&&!i((function(){return 7!==Object.defineProperty(a("div"),"a",{get:function(){return 7}}).a}))},5366:(e,r,n)=>{"use strict";var o=n(6406),i=n(5306),a=n(3048),u=Object,s=o("".split);e.exports=i((function(){return!u("z").propertyIsEnumerable(0)}))?function(e){return"String"===a(e)?s(e,""):u(e)}:u},5088:(e,r,n)=>{"use strict";var o=n(6406),i=n(5893),a=n(4830),u=o(Function.toString);i(a.inspectSource)||(a.inspectSource=function(e){return u(e)}),e.exports=a.inspectSource},9930:(e,r,n)=>{"use strict";var o,i,a,u=n(5585),s=n(7802),c=n(5287),f=n(9251),p=n(4130),_=n(4830),g=n(139),v=n(3421),C="Object already initialized",b=s.TypeError,x=s.WeakMap;if(u||_.state){var R=_.state||(_.state=new x);R.get=R.get,R.has=R.has,R.set=R.set,o=function(e,r){if(R.has(e))throw new b(C);return r.facade=e,R.set(e,r),r},i=function(e){return R.get(e)||{}},a=function(e){return R.has(e)}}else{var E=g("state");v[E]=!0,o=function(e,r){if(p(e,E))throw new b(C);return r.facade=e,f(e,E,r),r},i=function(e){return p(e,E)?e[E]:{}},a=function(e){return p(e,E)}}e.exports={set:o,get:i,has:a,enforce:function(e){return a(e)?i(e):o(e,{})},getterFor:function(e){return function(r){var n;if(!c(r)||(n=i(r)).type!==e)throw new b("Incompatible receiver, "+e+" required");return n}}}},5289:(e,r,n)=>{"use strict";var o=n(3048);e.exports=Array.isArray||function isArray(e){return"Array"===o(e)}},5893:(e,r,n)=>{"use strict";var o=n(811),i=o.all;e.exports=o.IS_HTMLDDA?function(e){return"function"==typeof e||e===i}:function(e){return"function"==typeof e}},5031:(e,r,n)=>{"use strict";var o=n(5306),i=n(5893),a=/#|\.prototype\./,isForced=function(e,r){var n=s[u(e)];return n===f||n!==c&&(i(r)?o(r):!!r)},u=isForced.normalize=function(e){return String(e).replace(a,".").toLowerCase()},s=isForced.data={},c=isForced.NATIVE="N",f=isForced.POLYFILL="P";e.exports=isForced},7707:e=>{"use strict";e.exports=function(e){return null==e}},5287:(e,r,n)=>{"use strict";var o=n(5893),i=n(811),a=i.all;e.exports=i.IS_HTMLDDA?function(e){return"object"==typeof e?null!==e:o(e)||e===a}:function(e){return"object"==typeof e?null!==e:o(e)}},99:e=>{"use strict";e.exports=!1},103:(e,r,n)=>{"use strict";var o=n(1570),i=n(5893),a=n(2075),u=n(345),s=Object;e.exports=u?function(e){return"symbol"==typeof e}:function(e){var r=o("Symbol");return i(r)&&a(r.prototype,s(e))}},9969:(e,r,n)=>{"use strict";var o=n(9099);e.exports=function(e){return o(e.length)}},3911:(e,r,n)=>{"use strict";var o=n(6406),i=n(5306),a=n(5893),u=n(4130),s=n(3877),c=n(9656).CONFIGURABLE,f=n(5088),p=n(9930),_=p.enforce,g=p.get,v=String,C=Object.defineProperty,b=o("".slice),x=o("".replace),R=o([].join),E=s&&!i((function(){return 8!==C((function(){}),"length",{value:8}).length})),w=String(String).split("String"),j=e.exports=function(e,r,n){"Symbol("===b(v(r),0,7)&&(r="["+x(v(r),/^Symbol\(([^)]*)\)/,"$1")+"]"),n&&n.getter&&(r="get "+r),n&&n.setter&&(r="set "+r),(!u(e,"name")||c&&e.name!==r)&&(s?C(e,"name",{value:r,configurable:!0}):e.name=r),E&&n&&u(n,"arity")&&e.length!==n.arity&&C(e,"length",{value:n.arity});try{n&&u(n,"constructor")&&n.constructor?s&&C(e,"prototype",{writable:!1}):e.prototype&&(e.prototype=void 0)}catch(e){}var o=_(e);return u(o,"source")||(o.source=R(w,"string"==typeof r?r:"")),e};Function.prototype.toString=j((function toString(){return a(this)&&g(this).source||f(this)}),"toString")},1402:e=>{"use strict";var r=Math.ceil,n=Math.floor;e.exports=Math.trunc||function trunc(e){var o=+e;return(o>0?n:r)(o)}},7144:(e,r,n)=>{"use strict";var o=n(3877),i=n(3075),a=n(7475),u=n(9972),s=n(3662),c=TypeError,f=Object.defineProperty,p=Object.getOwnPropertyDescriptor,_="enumerable",g="configurable",v="writable";r.f=o?a?function defineProperty(e,r,n){if(u(e),r=s(r),u(n),"function"==typeof e&&"prototype"===r&&"value"in n&&v in n&&!n[v]){var o=p(e,r);o&&o[v]&&(e[r]=n.value,n={configurable:g in n?n[g]:o[g],enumerable:_ in n?n[_]:o[_],writable:!1})}return f(e,r,n)}:f:function defineProperty(e,r,n){if(u(e),r=s(r),u(n),i)try{return f(e,r,n)}catch(e){}if("get"in n||"set"in n)throw new c("Accessors not supported");return"value"in n&&(e[r]=n.value),e}},10:(e,r,n)=>{"use strict";var o=n(3877),i=n(1550),a=n(1940),u=n(9637),s=n(9405),c=n(3662),f=n(4130),p=n(3075),_=Object.getOwnPropertyDescriptor;r.f=o?_:function getOwnPropertyDescriptor(e,r){if(e=s(e),r=c(r),p)try{return _(e,r)}catch(e){}if(f(e,r))return u(!i(a.f,e,r),e[r])}},7397:(e,r,n)=>{"use strict";var o=n(5079),i=n(4286).concat("length","prototype");r.f=Object.getOwnPropertyNames||function getOwnPropertyNames(e){return o(e,i)}},6855:(e,r)=>{"use strict";r.f=Object.getOwnPropertySymbols},2075:(e,r,n)=>{"use strict";var o=n(6406);e.exports=o({}.isPrototypeOf)},5079:(e,r,n)=>{"use strict";var o=n(6406),i=n(4130),a=n(9405),u=n(2971).indexOf,s=n(3421),c=o([].push);e.exports=function(e,r){var n,o=a(e),f=0,p=[];for(n in o)!i(s,n)&&i(o,n)&&c(p,n);for(;r.length>f;)i(o,n=r[f++])&&(~u(p,n)||c(p,n));return p}},1940:(e,r)=>{"use strict";var n={}.propertyIsEnumerable,o=Object.getOwnPropertyDescriptor,i=o&&!n.call({1:2},1);r.f=i?function propertyIsEnumerable(e){var r=o(this,e);return!!r&&r.enumerable}:n},1253:(e,r,n)=>{"use strict";var o=n(1550),i=n(5893),a=n(5287),u=TypeError;e.exports=function(e,r){var n,s;if("string"===r&&i(n=e.toString)&&!a(s=o(n,e)))return s;if(i(n=e.valueOf)&&!a(s=o(n,e)))return s;if("string"!==r&&i(n=e.toString)&&!a(s=o(n,e)))return s;throw new u("Can't convert object to primitive value")}},6627:(e,r,n)=>{"use strict";var o=n(1570),i=n(6406),a=n(7397),u=n(6855),s=n(9972),c=i([].concat);e.exports=o("Reflect","ownKeys")||function ownKeys(e){var r=a.f(s(e)),n=u.f;return n?c(r,n(e)):r}},6762:(e,r,n)=>{"use strict";var o=n(7707),i=TypeError;e.exports=function(e){if(o(e))throw new i("Can't call method on "+e);return e}},139:(e,r,n)=>{"use strict";var o=n(9231),i=n(6350),a=o("keys");e.exports=function(e){return a[e]||(a[e]=i(e))}},4830:(e,r,n)=>{"use strict";var o=n(7802),i=n(3630),a="__core-js_shared__",u=o[a]||i(a,{});e.exports=u},9231:(e,r,n)=>{"use strict";var o=n(99),i=n(4830);(e.exports=function(e,r){return i[e]||(i[e]=void 0!==r?r:{})})("versions",[]).push({version:"3.33.2",mode:o?"pure":"global",copyright:"© 2014-2023 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.33.2/LICENSE",source:"https://github.com/zloirock/core-js"})},4053:(e,r,n)=>{"use strict";var o=n(7245),i=n(5306),a=n(7802).String;e.exports=!!Object.getOwnPropertySymbols&&!i((function(){var e=Symbol("symbol detection");return!a(e)||!(Object(e)instanceof Symbol)||!Symbol.sham&&o&&o<41}))},9961:(e,r,n)=>{"use strict";var o=n(5930),i=Math.max,a=Math.min;e.exports=function(e,r){var n=o(e);return n<0?i(n+r,0):a(n,r)}},9405:(e,r,n)=>{"use strict";var o=n(5366),i=n(6762);e.exports=function(e){return o(i(e))}},5930:(e,r,n)=>{"use strict";var o=n(1402);e.exports=function(e){var r=+e;return r!=r||0===r?0:o(r)}},9099:(e,r,n)=>{"use strict";var o=n(5930),i=Math.min;e.exports=function(e){return e>0?i(o(e),9007199254740991):0}},5864:(e,r,n)=>{"use strict";var o=n(6762),i=Object;e.exports=function(e){return i(o(e))}},6090:(e,r,n)=>{"use strict";var o=n(1550),i=n(5287),a=n(103),u=n(6628),s=n(1253),c=n(7936),f=TypeError,p=c("toPrimitive");e.exports=function(e,r){if(!i(e)||a(e))return e;var n,c=u(e,p);if(c){if(void 0===r&&(r="default"),n=o(c,e,r),!i(n)||a(n))return n;throw new f("Can't convert object to primitive value")}return void 0===r&&(r="number"),s(e,r)}},3662:(e,r,n)=>{"use strict";var o=n(6090),i=n(103);e.exports=function(e){var r=o(e,"string");return i(r)?r:r+""}},5545:e=>{"use strict";var r=String;e.exports=function(e){try{return r(e)}catch(e){return"Object"}}},6350:(e,r,n)=>{"use strict";var o=n(6406),i=0,a=Math.random(),u=o(1..toString);e.exports=function(e){return"Symbol("+(void 0===e?"":e)+")_"+u(++i+a,36)}},345:(e,r,n)=>{"use strict";var o=n(4053);e.exports=o&&!Symbol.sham&&"symbol"==typeof Symbol.iterator},7475:(e,r,n)=>{"use strict";var o=n(3877),i=n(5306);e.exports=o&&i((function(){return 42!==Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype}))},5585:(e,r,n)=>{"use strict";var o=n(7802),i=n(5893),a=o.WeakMap;e.exports=i(a)&&/native code/.test(String(a))},7936:(e,r,n)=>{"use strict";var o=n(7802),i=n(9231),a=n(4130),u=n(6350),s=n(4053),c=n(345),f=o.Symbol,p=i("wks"),_=c?f.for||f:f&&f.withoutSetter||u;e.exports=function(e){return a(p,e)||(p[e]=s&&a(f,e)?f[e]:_("Symbol."+e)),p[e]}},3517:(e,r,n)=>{"use strict";var o=n(2390),i=n(5864),a=n(9969),u=n(8576),s=n(9060);o({target:"Array",proto:!0,arity:1,forced:n(5306)((function(){return 4294967297!==[].push.call({length:4294967296},1)}))||!function(){try{Object.defineProperty([],"length",{writable:!1}).push()}catch(e){return e instanceof TypeError}}()},{push:function push(e){var r=i(this),n=a(r),o=arguments.length;s(n+o);for(var c=0;c{"use strict";new(__webpack_require__(3203)(__webpack_require__(9013)).default)})()})(); \ No newline at end of file diff --git a/assets/js/editor.js b/assets/js/editor.js new file mode 100644 index 0000000..db86220 --- /dev/null +++ b/assets/js/editor.js @@ -0,0 +1,9744 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ "../assets/dev/js/editor/element-editor-module.js": +/*!********************************************************!*\ + !*** ../assets/dev/js/editor/element-editor-module.js ***! + \********************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + elementType: null, + __construct(elementType) { + this.elementType = elementType; + this.addEditorListener(); + }, + updateOptions(name, options) { + const controlView = this.getEditorControlView(name); + if (controlView) { + this.getEditorControlModel(name).set('options', options); + controlView.render(); + } + }, + addEditorListener() { + var self = this; + if (self.onElementChange) { + var eventName = 'change'; + if ('global' !== self.elementType) { + eventName += ':' + self.elementType; + } + elementor.channels.editor.on(eventName, function (controlView, elementView) { + self.onElementChange(controlView.model.get('name'), controlView, elementView); + }); + } + }, + /** + * Add a spinner to a control inside its control title. + * + * @param {string} controlName - The control name to add the spinner to. + * + * @return {void} + */ + addControlSpinner(controlName) { + const $el = this.getEditorControlView(controlName).$el; + + // Exit if there is a spinner already. + if ($el.find('.elementor-control-spinner').length) { + return; + } + const $input = $el.find(':input'); + $input.attr('disabled', true); + $el.find('.elementor-control-title').after(' '); + }, + /** + * Remove a spinner from a control. + * + * @param {string} controlName - The control name to remove the spinner from. + * + * @return {void} + */ + removeControlSpinner(controlName) { + const $controlEl = this.getEditorControlView(controlName).$el; + $controlEl.find(':input').attr('disabled', false); + $controlEl.find('.elementor-control-spinner').remove(); + }, + /** + * Add an error message under the control. + * + * @param {string} controlName - The control name to add the error to. + * @param {string} error - Set an error message. + * @param {string} location - A CSS selector to the element which the error will be appended to. + * + * @return {void} + */ + addControlError(controlName, error, location = '.elementor-control-content') { + const $el = this.getEditorControlView(controlName).$el; + + // Remove any existing error in order to override it. + if ($el.find('.e-control-error').length) { + $el.find('.e-control-error').remove(); + } + + // Select only the first elements to prevent cases where the error is added to many elements. + // (usually in repeater controls). + $el.find(location).first().after(`${error}`); + }, + /** + * Remove the control error message. + * + * @param {string} controlName - The control name to add the error to. + * + * @return {void} + */ + removeControlError(controlName) { + const $el = this.getEditorControlView(controlName).$el; + $el.find('.e-control-error').remove(); + }, + /** + * Remove any indicators that are related to the control. (e.g. spinner, error, etc.) + * + * @param {string} controlName - The control name to reset. + * + * @return {void} + */ + resetControlIndicators(controlName) { + this.removeControlSpinner(controlName); + this.removeControlError(controlName); + }, + addSectionListener(section, callback) { + const self = this; + elementor.channels.editor.on('section:activated', function (sectionName, editor) { + var model = editor.getOption('editedElementView').getEditModel(), + currentElementType = model.get('elType'), + _arguments = arguments; + if ('widget' === currentElementType) { + currentElementType = model.get('widgetType'); + } + if (self.elementType === currentElementType && section === sectionName) { + setTimeout(function () { + callback.apply(self, _arguments); + }, 10); + } + }); + } +}); + +/***/ }), + +/***/ "../assets/dev/js/editor/inline-controls-stack.js": +/*!********************************************************!*\ + !*** ../assets/dev/js/editor/inline-controls-stack.js ***! + \********************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = elementorModules.editor.views.ControlsStack.extend({ + activeTab: 'content', + activeSection: 'settings', + initialize() { + this.collection = new Backbone.Collection(_.values(this.options.controls)); + }, + filter(model) { + if ('section' === model.get('type')) { + return true; + } + var section = model.get('section'); + return !section || section === this.activeSection; + }, + childViewOptions() { + return { + elementSettingsModel: this.model + }; + } +}); + +/***/ }), + +/***/ "../assets/dev/js/editor/tiers.js": +/*!****************************************!*\ + !*** ../assets/dev/js/editor/tiers.js ***! + \****************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.isTierAtLeast = exports.TIERS_PRIORITY = void 0; +// TODO: Read from Core. +const TIERS_PRIORITY = exports.TIERS_PRIORITY = Object.freeze(['free', 'essential', 'essential-oct2023', 'advanced', 'expert', 'agency']); +const isTierAtLeast = (currentTier, expectedTier) => { + const currentTierIndex = TIERS_PRIORITY.indexOf(currentTier); + const expectedTierIndex = TIERS_PRIORITY.indexOf(expectedTier); + if (-1 === currentTierIndex || -1 === expectedTierIndex) { + return false; + } + return currentTierIndex >= expectedTierIndex; +}; +exports.isTierAtLeast = isTierAtLeast; + +/***/ }), + +/***/ "../assets/dev/js/preview/utils/document-handle.js": +/*!*********************************************************!*\ + !*** ../assets/dev/js/preview/utils/document-handle.js ***! + \*********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.SAVE_CONTEXT = exports.EDIT_CONTEXT = void 0; +exports.createElement = createElement; +exports["default"] = addDocumentHandle; +__webpack_require__(/*! core-js/modules/es.array.push.js */ "../node_modules/core-js/modules/es.array.push.js"); +const EDIT_HANDLE_CLASS_NAME = 'elementor-document-handle'; +const EDIT_MODE_CLASS_NAME = 'elementor-edit-mode'; +const EDIT_CONTEXT = exports.EDIT_CONTEXT = 'edit'; +const SAVE_HANDLE_CLASS_NAME = 'elementor-document-save-back-handle'; +const SAVE_CONTEXT = exports.SAVE_CONTEXT = 'save'; + +/** + * @param {Object} handleTarget + * @param {HTMLElement} handleTarget.element + * @param {string|number} handleTarget.id - Document ID. + * @param {string} handleTarget.title + * @param {string} context - Edit/Save + * @param {Function|null} onCloseDocument - Callback to run when outgoing document is closed. + * @param {string} selector + */ +function addDocumentHandle({ + element, + id, + title = __('Template', 'elementor-pro') +}, context = EDIT_CONTEXT, onCloseDocument = null, selector = null) { + if (EDIT_CONTEXT === context) { + if (!id || !element) { + throw Error('`id` and `element` are required.'); + } + if (isCurrentlyEditing(element) || hasHandle(element)) { + return; + } + } + const handleElement = createHandleElement({ + title, + onClick: () => onDocumentClick(id, context, onCloseDocument, selector) + }, context, element); + element.prepend(handleElement); + if (EDIT_CONTEXT === context) { + element.dataset.editableElementorDocument = id; + } +} + +/** + * @param {HTMLElement} element + * + * @return {boolean} Whether the element is currently being edited. + */ +function isCurrentlyEditing(element) { + return element.classList.contains(EDIT_MODE_CLASS_NAME); +} + +/** + * @param {HTMLElement} element + * + * @return {boolean} Whether the element has a handle. + */ +function hasHandle(element) { + return !!element.querySelector(`:scope > .${EDIT_HANDLE_CLASS_NAME}`); +} + +/** + * @param {Object} handleProperties + * @param {string} handleProperties.title + * @param {Function} handleProperties.onClick + * @param {string} context + * @param {HTMLElement} element + * + * @return {HTMLElement} The newly generated Handle element + */ +function createHandleElement({ + title, + onClick +}, context, element = null) { + const handleTitle = ['header', 'footer'].includes(element?.dataset.elementorType) ? '%s' : __('Edit %s', 'elementor-pro'); + const innerElement = createElement({ + tag: 'div', + classNames: [`${EDIT_HANDLE_CLASS_NAME}__inner`], + children: [createElement({ + tag: 'i', + classNames: [getHandleIcon(context)] + }), createElement({ + tag: 'div', + classNames: [`${EDIT_CONTEXT === context ? EDIT_HANDLE_CLASS_NAME : SAVE_HANDLE_CLASS_NAME}__title`], + children: [document.createTextNode(EDIT_CONTEXT === context ? handleTitle.replace('%s', title) : __('Save %s', 'elementor-pro').replace('%s', title))] + })] + }); + const classNames = [EDIT_HANDLE_CLASS_NAME]; + if (EDIT_CONTEXT !== context) { + classNames.push(SAVE_HANDLE_CLASS_NAME); + } + const containerElement = createElement({ + tag: 'div', + classNames, + children: [innerElement] + }); + containerElement.addEventListener('click', onClick); + return containerElement; +} +function getHandleIcon(context) { + let icon = 'eicon-edit'; + if (SAVE_CONTEXT === context) { + icon = elementorFrontend.config.is_rtl ? 'eicon-arrow-right' : 'eicon-arrow-left'; + } + return icon; +} + +/** + * Util for creating HTML element. + * + * @param {Object} elementProperties + * @param {string} elementProperties.tag + * @param {string[]} elementProperties.classNames + * @param {HTMLElement[]} elementProperties.children + * + * @return {HTMLElement} Generated Element + */ +function createElement({ + tag, + classNames = [], + children = [] +}) { + const element = document.createElement(tag); + element.classList.add(...classNames); + children.forEach(child => element.appendChild(child)); + return element; +} + +/** + * @param {string|number} id + * @param {string} context + * @param {Function|null} onCloseDocument + * @param {string} selector + * @return {Promise} + */ +async function onDocumentClick(id, context, onCloseDocument = null, selector = null) { + if (EDIT_CONTEXT === context) { + window.top.$e.internal('panel/state-loading'); + await window.top.$e.run('editor/documents/switch', { + id: parseInt(id), + onClose: onCloseDocument, + selector + }); + window.top.$e.internal('panel/state-ready'); + } else { + elementorCommon.api.internal('panel/state-loading'); + elementorCommon.api.run('editor/documents/switch', { + id: elementor.config.initial_document.id, + mode: 'save', + shouldScroll: false, + selector + }).finally(() => elementorCommon.api.internal('panel/state-ready')); + } +} + +/***/ }), + +/***/ "../core/app/modules/site-editor/assets/js/data/commands/conditions-config.js": +/*!************************************************************************************!*\ + !*** ../core/app/modules/site-editor/assets/js/data/commands/conditions-config.js ***! + \************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.ConditionsConfig = void 0; +class ConditionsConfig extends $e.modules.CommandData { + static signature = 'site-editor/conditions-config'; + static getEndpointFormat() { + return 'site-editor/conditions-config/{id}'; + } +} +exports.ConditionsConfig = ConditionsConfig; +var _default = exports["default"] = ConditionsConfig; + +/***/ }), + +/***/ "../core/app/modules/site-editor/assets/js/data/commands/index.js": +/*!************************************************************************!*\ + !*** ../core/app/modules/site-editor/assets/js/data/commands/index.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "ConditionsConfig", ({ + enumerable: true, + get: function () { + return _conditionsConfig.ConditionsConfig; + } +})); +Object.defineProperty(exports, "Templates", ({ + enumerable: true, + get: function () { + return _templates.Templates; + } +})); +Object.defineProperty(exports, "TemplatesConditions", ({ + enumerable: true, + get: function () { + return _templatesConditions.TemplatesConditions; + } +})); +Object.defineProperty(exports, "TemplatesConditionsConflicts", ({ + enumerable: true, + get: function () { + return _templatesConditionsConflicts.TemplatesConditionsConflicts; + } +})); +var _templates = __webpack_require__(/*! ./templates */ "../core/app/modules/site-editor/assets/js/data/commands/templates.js"); +var _conditionsConfig = __webpack_require__(/*! ./conditions-config */ "../core/app/modules/site-editor/assets/js/data/commands/conditions-config.js"); +var _templatesConditions = __webpack_require__(/*! ./templates-conditions */ "../core/app/modules/site-editor/assets/js/data/commands/templates-conditions.js"); +var _templatesConditionsConflicts = __webpack_require__(/*! ./templates-conditions-conflicts */ "../core/app/modules/site-editor/assets/js/data/commands/templates-conditions-conflicts.js"); + +/***/ }), + +/***/ "../core/app/modules/site-editor/assets/js/data/commands/templates-conditions-conflicts.js": +/*!*************************************************************************************************!*\ + !*** ../core/app/modules/site-editor/assets/js/data/commands/templates-conditions-conflicts.js ***! + \*************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.TemplatesConditionsConflicts = void 0; +class TemplatesConditionsConflicts extends $e.modules.CommandData { + static signature = 'site-editor/templates-conditions-conflicts'; + static getEndpointFormat() { + return `${TemplatesConditionsConflicts.signature}/{id}`; + } +} +exports.TemplatesConditionsConflicts = TemplatesConditionsConflicts; +var _default = exports["default"] = TemplatesConditionsConflicts; + +/***/ }), + +/***/ "../core/app/modules/site-editor/assets/js/data/commands/templates-conditions.js": +/*!***************************************************************************************!*\ + !*** ../core/app/modules/site-editor/assets/js/data/commands/templates-conditions.js ***! + \***************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.TemplatesConditions = void 0; +class TemplatesConditions extends $e.modules.CommandData { + static signature = 'site-editor/templates-conditions'; + static getEndpointFormat() { + return 'site-editor/templates-conditions/{id}'; + } +} +exports.TemplatesConditions = TemplatesConditions; +var _default = exports["default"] = TemplatesConditions; + +/***/ }), + +/***/ "../core/app/modules/site-editor/assets/js/data/commands/templates.js": +/*!****************************************************************************!*\ + !*** ../core/app/modules/site-editor/assets/js/data/commands/templates.js ***! + \****************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.Templates = void 0; +class Templates extends $e.modules.CommandData { + static signature = 'site-editor/templates'; + static getEndpointFormat() { + return 'site-editor/templates/{id}'; + } +} +exports.Templates = Templates; +var _default = exports["default"] = Templates; + +/***/ }), + +/***/ "../core/app/modules/site-editor/assets/js/data/component.js": +/*!*******************************************************************!*\ + !*** ../core/app/modules/site-editor/assets/js/data/component.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var dataCommands = _interopRequireWildcard(__webpack_require__(/*! ./commands */ "../core/app/modules/site-editor/assets/js/data/commands/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class Component extends $e.modules.ComponentBase { + static namespace = 'site-editor'; + getNamespace() { + return this.constructor.namespace; + } + defaultData() { + return this.importCommands(dataCommands); + } +} +exports["default"] = Component; + +/***/ }), + +/***/ "../core/app/modules/site-editor/assets/js/editor.js": +/*!***********************************************************!*\ + !*** ../core/app/modules/site-editor/assets/js/editor.js ***! + \***********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _component = _interopRequireDefault(__webpack_require__(/*! ./data/component */ "../core/app/modules/site-editor/assets/js/data/component.js")); +var _commands = __webpack_require__(/*! ./data/commands */ "../core/app/modules/site-editor/assets/js/data/commands/index.js"); +class Module extends elementorModules.editor.utils.Module { + onElementorInit() { + const config = elementor.documents.getCurrent().config; + if (config.support_site_editor) { + $e.components.register(new _component.default()); + $e.data.deleteCache($e.components.get(_component.default.namespace), _commands.Templates.signature); + } + } +} +exports["default"] = Module; + +/***/ }), + +/***/ "../modules/assets-manager/assets/js/editor/editor.js": +/*!************************************************************!*\ + !*** ../modules/assets-manager/assets/js/editor/editor.js ***! + \************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + onElementorInit() { + var FontsManager = __webpack_require__(/*! ./font-manager */ "../modules/assets-manager/assets/js/editor/font-manager.js"); + this.assets = { + font: new FontsManager() + }; + } +}); + +/***/ }), + +/***/ "../modules/assets-manager/assets/js/editor/font-manager.js": +/*!******************************************************************!*\ + !*** ../modules/assets-manager/assets/js/editor/font-manager.js ***! + \******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +__webpack_require__(/*! core-js/modules/es.array.push.js */ "../node_modules/core-js/modules/es.array.push.js"); +module.exports = elementorModules.Module.extend({ + _enqueuedFonts: [], + _enqueuedTypekit: false, + onFontChange(fontType, font) { + if ('custom' !== fontType && 'typekit' !== fontType) { + return; + } + if (-1 !== this._enqueuedFonts.indexOf(font)) { + return; + } + if ('typekit' === fontType && this._enqueuedTypekit) { + return; + } + this.getCustomFont(fontType, font); + }, + getCustomFont(fontType, font) { + elementorPro.ajax.addRequest('assets_manager_panel_action_data', { + unique_id: 'font_' + fontType + font, + data: { + service: 'font', + type: fontType, + font + }, + success(data) { + if (data.font_face) { + elementor.$previewContents.find('style').last().after(''); + } + if (data.font_url) { + elementor.$previewContents.find('link').last().after(''); + } + } + }); + this._enqueuedFonts.push(font); + if ('typekit' === fontType) { + this._enqueuedTypekit = true; + } + }, + onInit() { + elementor.channels.editor.on('font:insertion', this.onFontChange.bind(this)); + } +}); + +/***/ }), + +/***/ "../modules/custom-css/assets/js/editor/editor.js": +/*!********************************************************!*\ + !*** ../modules/custom-css/assets/js/editor/editor.js ***! + \********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +class _default extends elementorModules.editor.utils.Module { + addCustomCss(css, context) { + if (!context) { + return; + } + const model = context.model, + customCSS = model.get('settings').get('custom_css'); + let selector = '.elementor-element.elementor-element-' + model.get('id'); + if ('document' === model.get('elType')) { + selector = elementor.config.document.settings.cssWrapperSelector; + } + if (customCSS) { + css += customCSS.replace(/selector/g, selector); + } + return css; + } + onElementorInit() { + elementor.hooks.addFilter('editor/style/styleText', this.addCustomCss); + elementor.on('navigator:init', this.onNavigatorInit.bind(this)); + } + onNavigatorInit() { + elementor.navigator.indicators.customCSS = { + icon: 'code-bold', + settingKeys: ['custom_css'], + title: __('Custom CSS', 'elementor-pro'), + section: 'section_custom_css' + }; + } +} +exports["default"] = _default; + +/***/ }), + +/***/ "../modules/flip-box/assets/js/editor/editor.js": +/*!******************************************************!*\ + !*** ../modules/flip-box/assets/js/editor/editor.js ***! + \******************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + onElementorInit() { + elementor.channels.editor.on('section:activated', this.onSectionActivated); + }, + onSectionActivated(sectionName, editor) { + var editedElement = editor.getOption('editedElementView'); + if ('flip-box' !== editedElement.model.get('widgetType')) { + return; + } + var isSideBSection = -1 !== ['section_side_b_content', 'section_style_b'].indexOf(sectionName); + editedElement.$el.toggleClass('elementor-flip-box--flipped', isSideBSection); + var $backLayer = editedElement.$el.find('.elementor-flip-box__back'); + if (isSideBSection) { + $backLayer.css('transition', 'none'); + } + if (!isSideBSection) { + setTimeout(function () { + $backLayer.css('transition', ''); + }, 10); + } + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/component.js": +/*!******************************************************!*\ + !*** ../modules/forms/assets/js/editor/component.js ***! + \******************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var hooks = _interopRequireWildcard(__webpack_require__(/*! ./hooks/ */ "../modules/forms/assets/js/editor/hooks/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class Component extends $e.modules.ComponentBase { + getNamespace() { + return 'forms'; + } + defaultHooks() { + return this.importHooks(hooks); + } +} +exports["default"] = Component; + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/fields-map-control.js": +/*!***************************************************************!*\ + !*** ../modules/forms/assets/js/editor/fields-map-control.js ***! + \***************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +module.exports = elementor.modules.controls.Repeater.extend({ + onBeforeRender() { + this.$el.hide(); + }, + updateMap(fields) { + var self = this, + savedMapObject = {}; + self.collection.each(function (model) { + savedMapObject[model.get('remote_id')] = model.get('local_id'); + }); + self.collection.reset(); + _.each(fields, function (field) { + var model = { + remote_id: field.remote_id, + remote_label: field.remote_label, + remote_type: field.remote_type ? field.remote_type : '', + remote_required: field.remote_required ? field.remote_required : false, + local_id: savedMapObject[field.remote_id] ? savedMapObject[field.remote_id] : '' + }; + self.collection.add(model); + }); + self.render(); + }, + onRender() { + elementor.modules.controls.Base.prototype.onRender.apply(this, arguments); + var self = this; + self.children.each(function (view) { + var localFieldsControl = view.children.last(), + options = { + '': '- ' + __('None', 'elementor') + ' -' + }, + label = view.model.get('remote_label'); + if (view.model.get('remote_required')) { + label += '*'; + } + _.each(self.elementSettingsModel.get('form_fields').models, function (model, index) { + // If it's an email field, add only email fields from thr form + var remoteType = view.model.get('remote_type'); + if ('text' !== remoteType && remoteType !== model.get('field_type')) { + return; + } + options[model.get('custom_id')] = model.get('field_label') || 'Field #' + (index + 1); + }); + localFieldsControl.model.set('label', label); + localFieldsControl.model.set('options', options); + localFieldsControl.render(); + view.$el.find('.elementor-repeater-row-tools').hide(); + view.$el.find('.elementor-repeater-row-controls').removeClass('elementor-repeater-row-controls').find('.elementor-control').css({ + paddingBottom: 0 + }); + }); + self.$el.find('.elementor-button-wrapper').remove(); + if (self.children.length) { + self.$el.show(); + } + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/fields-repeater-control.js": +/*!********************************************************************!*\ + !*** ../modules/forms/assets/js/editor/fields-repeater-control.js ***! + \********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +var _fieldsRepeaterRow = _interopRequireDefault(__webpack_require__(/*! ./fields-repeater-row */ "../modules/forms/assets/js/editor/fields-repeater-row.js")); +module.exports = class extends elementor.modules.controls.Repeater { + className() { + let classes = super.className(); + classes += ' elementor-control-type-repeater'; + return classes; + } + getChildView() { + return _fieldsRepeaterRow.default; + } + initialize(...args) { + super.initialize(...args); + const formFields = this.container.settings.get('form_fields'); + this.listenTo(formFields, 'change', model => this.onFormFieldChange(model)).listenTo(formFields, 'remove', model => this.onFormFieldRemove(model)); + } + getFirstChild() { + return this.children.findByModel(this.collection.models[0]); + } + lockFirstStep() { + const firstChild = this.getFirstChild(); + if ('step' !== firstChild.model.get('field_type')) { + return; + } + const stepFields = this.collection.where({ + field_type: 'step' + }); + if (1 < stepFields.length) { + firstChild.toggleFieldTypeControl(false); + firstChild.toggleTools(false); + } + firstChild.toggleSort(false); + } + onFormFieldChange(model) { + const fieldType = model.changed.field_type; + if (!fieldType || 'step' !== fieldType && 'step' !== model._previousAttributes.field_type) { + return; + } + const isStep = 'step' === fieldType; + this.children.findByModel(model).toggleStepField(isStep); + this.onStepFieldChanged(isStep); + } + onFormFieldRemove(model) { + if ('step' === model.get('field_type')) { + this.onStepFieldChanged(false); + } + } + onStepFieldChanged(isStep) { + if (isStep) { + this.lockFirstStep(); + return; + } + const stepFields = this.collection.where({ + field_type: 'step' + }); + if (stepFields.length > 1) { + return; + } + const firstChild = this.getFirstChild(); + if (1 === stepFields.length) { + firstChild.toggleTools(true); + firstChild.toggleFieldTypeControl(true); + return; + } + firstChild.toggleSort(true); + } + onAddChild(childView) { + super.onAddChild(childView); + if ('step' === childView.model.get('field_type')) { + this.lockFirstStep(); + childView.toggleStepField(true); + } + } +}; + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/fields-repeater-row.js": +/*!****************************************************************!*\ + !*** ../modules/forms/assets/js/editor/fields-repeater-row.js ***! + \****************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +class _default extends elementor.modules.controls.RepeaterRow { + toggleFieldTypeControl(show) { + const fieldTypeModel = this.collection.findWhere({ + name: 'field_type' + }), + fieldTypeControl = this.children.findByModel(fieldTypeModel); + fieldTypeControl.$el.toggle(show); + } + toggleStepField(isStep) { + this.$el.toggleClass('elementor-repeater-row--form-step', isStep); + } + toggleTools(show) { + this.ui.removeButton.add(this.ui.duplicateButton).toggle(show); + } +} +exports["default"] = _default; + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/fields/acceptance.js": +/*!**************************************************************!*\ + !*** ../modules/forms/assets/js/editor/fields/acceptance.js ***! + \**************************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + renderField(inputField, item, i, settings) { + var itemClasses = _.escape(item.css_classes), + required = '', + label = '', + checked = ''; + if (item.required) { + required = 'required'; + } + if (item.acceptance_text) { + label = ''; + } + if (item.checked_by_default) { + checked = ' checked="checked"'; + } + return '
' + '' + ' ' + label + '
'; + }, + onInit() { + elementor.hooks.addFilter('elementor_pro/forms/content_template/field/acceptance', this.renderField, 10, 4); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/fields/date.js": +/*!********************************************************!*\ + !*** ../modules/forms/assets/js/editor/fields/date.js ***! + \********************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + renderField(inputField, item, i, settings) { + var itemClasses = _.escape(item.css_classes), + required = '', + min = '', + max = '', + placeholder = ''; + if (item.required) { + required = 'required'; + } + if (item.min_date) { + min = ' min="' + item.min_date + '"'; + } + if (item.max_date) { + max = ' max="' + item.max_date + '"'; + } + if (item.placeholder) { + placeholder = ' placeholder="' + item.placeholder + '"'; + } + if ('yes' === item.use_native_date) { + itemClasses += ' elementor-use-native'; + } + return ''; + }, + onInit() { + elementor.hooks.addFilter('elementor_pro/forms/content_template/field/date', this.renderField, 10, 4); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/fields/tel.js": +/*!*******************************************************!*\ + !*** ../modules/forms/assets/js/editor/fields/tel.js ***! + \*******************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + renderField(inputField, item, i, settings) { + var itemClasses = _.escape(item.css_classes), + required = '', + placeholder = ''; + if (item.required) { + required = 'required'; + } + if (item.placeholder) { + placeholder = ' placeholder="' + item.placeholder + '"'; + } + itemClasses = 'elementor-field-textual ' + itemClasses; + return ''; + }, + onInit() { + elementor.hooks.addFilter('elementor_pro/forms/content_template/field/tel', this.renderField, 10, 4); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/fields/time.js": +/*!********************************************************!*\ + !*** ../modules/forms/assets/js/editor/fields/time.js ***! + \********************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + renderField(inputField, item, i, settings) { + var itemClasses = _.escape(item.css_classes), + required = '', + placeholder = ''; + if (item.required) { + required = 'required'; + } + if (item.placeholder) { + placeholder = ' placeholder="' + item.placeholder + '"'; + } + if ('yes' === item.use_native_time) { + itemClasses += ' elementor-use-native'; + } + return ''; + }, + onInit() { + elementor.hooks.addFilter('elementor_pro/forms/content_template/field/time', this.renderField, 10, 4); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/fields/upload.js": +/*!**********************************************************!*\ + !*** ../modules/forms/assets/js/editor/fields/upload.js ***! + \**********************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + renderField(inputField, item, i, settings) { + var itemClasses = _.escape(item.css_classes), + required = '', + multiple = '', + fieldName = 'form_field_'; + if (item.required) { + required = 'required'; + } + if (item.allow_multiple_upload) { + multiple = ' multiple="multiple"'; + fieldName += '[]'; + } + return ''; + }, + onInit() { + elementor.hooks.addFilter('elementor_pro/forms/content_template/field/upload', this.renderField, 10, 4); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/hooks/data/form-fields-sanitize-custom-id.js": +/*!**************************************************************************************!*\ + !*** ../modules/forms/assets/js/editor/hooks/data/form-fields-sanitize-custom-id.js ***! + \**************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.FormFieldsSanitizeCustomId = void 0; +class FormFieldsSanitizeCustomId extends $e.modules.hookData.Dependency { + ID_SANITIZE_FILTER = /[^\w]/g; + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 'elementor-pro-forms-fields-sanitize-custom-id'; + } + getContainerType() { + return 'repeater'; + } + getConditions(args) { + return undefined !== args.settings.custom_id; + } + apply(args) { + const { + containers = [args.container], + settings + } = args, + // `custom_id` is the control name. + { + custom_id: customId + } = settings; + if (customId.match(this.ID_SANITIZE_FILTER)) { + // Re-render with old settings. + containers.forEach(container => { + const panelView = container.panel.getControlView('form_fields'), + currentItemView = panelView.children.findByModel(container.settings), + idView = currentItemView.children.find(view => 'custom_id' === view.model.get('name')); + idView.render(); + idView.$el.find('input').trigger('focus'); + }); + + // Hook-Break. + return false; + } + return true; + } +} +exports.FormFieldsSanitizeCustomId = FormFieldsSanitizeCustomId; +var _default = exports["default"] = FormFieldsSanitizeCustomId; + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/hooks/data/form-fields-set-custom-id.js": +/*!*********************************************************************************!*\ + !*** ../modules/forms/assets/js/editor/hooks/data/form-fields-set-custom-id.js ***! + \*********************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.FormFieldsSetCustomId = void 0; +class FormFieldsSetCustomId extends $e.modules.hookData.After { + getCommand() { + return 'document/repeater/insert'; + } + getId() { + return 'elementor-pro-forms-fields-set-custom-id'; + } + getContainerType() { + return 'widget'; + } + getConditions(args) { + return 'form_fields' === args.name; + } + apply(args, model) { + const { + containers = [args.container] + } = args, + isDuplicate = $e.commands.isCurrentFirstTrace('document/repeater/duplicate'); + containers.forEach(( /** Container */container) => { + const itemContainer = container.repeaters.form_fields.children.find(childrenContainer => { + // Sometimes, one of children is {Empty}. + if (childrenContainer) { + return model.get('_id') === childrenContainer.id; + } + return false; + }); + if (!isDuplicate && itemContainer.settings.get('custom_id')) { + return; + } + $e.run('document/elements/settings', { + container: itemContainer, + settings: { + custom_id: 'field_' + itemContainer.id + }, + options: { + external: true + } + }); + }); + return true; + } +} +exports.FormFieldsSetCustomId = FormFieldsSetCustomId; +var _default = exports["default"] = FormFieldsSetCustomId; + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/hooks/data/form-fields-step.js": +/*!************************************************************************!*\ + !*** ../modules/forms/assets/js/editor/hooks/data/form-fields-step.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.FormFieldsAddFirstStep = void 0; +class FormFieldsAddFirstStep extends $e.modules.hookData.After { + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 'elementor-pro-forms-fields-first-step'; + } + getContainerType() { + return 'repeater'; + } + getConditions(args) { + const { + containers = [args.container] + } = args; + return 'form' === containers[0].parent.parent.model.get('widgetType') && 'step' === args.settings.field_type; + } + apply(args) { + const { + containers = [args.container] + } = args; + containers.forEach(( /** Container */container) => { + const firstItem = container.parent.children[0]; + if ('step' === firstItem.settings.get('field_type')) { + return; + } + $e.run('document/repeater/insert', { + container: container.parent.parent, + // Widget + name: 'form_fields', + model: { + field_type: 'step' + }, + options: { + at: 0, + external: true + } + }); + }); + return true; + } +} +exports.FormFieldsAddFirstStep = FormFieldsAddFirstStep; +var _default = exports["default"] = FormFieldsAddFirstStep; + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/hooks/data/form-sanitize-id.js": +/*!************************************************************************!*\ + !*** ../modules/forms/assets/js/editor/hooks/data/form-sanitize-id.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.FormSanitizeId = void 0; +class FormSanitizeId extends $e.modules.hookData.Dependency { + ID_SANITIZE_FILTER = /[^\w]/g; + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 'elementor-pro-forms-sanitize-id'; + } + getContainerType() { + return 'widget'; + } + getConditions(args) { + return undefined !== args.settings.form_id; + } + apply(args) { + const { + container, + settings + } = args; + const { + form_id: formId + } = settings; + + // Re-render with old settings. + if (formId.match(this.ID_SANITIZE_FILTER)) { + const formIdView = container.panel.getControlView('form_id'); + formIdView.render(); + formIdView.$el.find('input').trigger('focus'); + + // Hook-Break. + return false; + } + return true; + } +} +exports.FormSanitizeId = FormSanitizeId; +var _default = exports["default"] = FormSanitizeId; + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/hooks/data/index.js": +/*!*************************************************************!*\ + !*** ../modules/forms/assets/js/editor/hooks/data/index.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "FormFieldsAddFirstStep", ({ + enumerable: true, + get: function () { + return _formFieldsStep.FormFieldsAddFirstStep; + } +})); +Object.defineProperty(exports, "FormFieldsSanitizeCustomId", ({ + enumerable: true, + get: function () { + return _formFieldsSanitizeCustomId.FormFieldsSanitizeCustomId; + } +})); +Object.defineProperty(exports, "FormFieldsSetCustomId", ({ + enumerable: true, + get: function () { + return _formFieldsSetCustomId.FormFieldsSetCustomId; + } +})); +Object.defineProperty(exports, "FormSanitizeId", ({ + enumerable: true, + get: function () { + return _formSanitizeId.FormSanitizeId; + } +})); +var _formFieldsSanitizeCustomId = __webpack_require__(/*! ./form-fields-sanitize-custom-id */ "../modules/forms/assets/js/editor/hooks/data/form-fields-sanitize-custom-id.js"); +var _formFieldsSetCustomId = __webpack_require__(/*! ./form-fields-set-custom-id */ "../modules/forms/assets/js/editor/hooks/data/form-fields-set-custom-id.js"); +var _formFieldsStep = __webpack_require__(/*! ./form-fields-step */ "../modules/forms/assets/js/editor/hooks/data/form-fields-step.js"); +var _formSanitizeId = __webpack_require__(/*! ./form-sanitize-id */ "../modules/forms/assets/js/editor/hooks/data/form-sanitize-id.js"); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/hooks/index.js": +/*!********************************************************!*\ + !*** ../modules/forms/assets/js/editor/hooks/index.js ***! + \********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +var _data = __webpack_require__(/*! ./data/ */ "../modules/forms/assets/js/editor/hooks/data/index.js"); +Object.keys(_data).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _data[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _data[key]; + } + }); +}); +var _ui = __webpack_require__(/*! ./ui/ */ "../modules/forms/assets/js/editor/hooks/ui/index.js"); +Object.keys(_ui).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _ui[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _ui[key]; + } + }); +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/hooks/ui/form-fields-update-shortcode.js": +/*!**********************************************************************************!*\ + !*** ../modules/forms/assets/js/editor/hooks/ui/form-fields-update-shortcode.js ***! + \**********************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.FormFieldsUpdateShortCode = void 0; +class FormFieldsUpdateShortCode extends $e.modules.hookUI.After { + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 'elementor-pro-forms-fields-update-shortcode'; + } + getContainerType() { + return 'repeater'; + } + getConditions(args) { + if (!$e.routes.isPartOf('panel/editor') || undefined === args.settings.custom_id) { + return false; + } + return true; + } + apply(args) { + const { + containers = [args.container] + } = args; + containers.forEach(( /** Container */container) => { + const panelView = container.panel.getControlView('form_fields'), + currentItemView = panelView.children.find(view => container.id === view.model.get('_id')), + shortcodeView = currentItemView.children.find(view => 'shortcode' === view.model.get('name')); + shortcodeView.render(); + }); + } +} +exports.FormFieldsUpdateShortCode = FormFieldsUpdateShortCode; +var _default = exports["default"] = FormFieldsUpdateShortCode; + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/hooks/ui/index.js": +/*!***********************************************************!*\ + !*** ../modules/forms/assets/js/editor/hooks/ui/index.js ***! + \***********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "FormFieldsUpdateShortCode", ({ + enumerable: true, + get: function () { + return _formFieldsUpdateShortcode.FormFieldsUpdateShortCode; + } +})); +var _formFieldsUpdateShortcode = __webpack_require__(/*! ./form-fields-update-shortcode */ "../modules/forms/assets/js/editor/hooks/ui/form-fields-update-shortcode.js"); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/integrations/activecampaign.js": +/*!************************************************************************!*\ + !*** ../modules/forms/assets/js/editor/integrations/activecampaign.js ***! + \************************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +__webpack_require__(/*! core-js/modules/es.array.push.js */ "../node_modules/core-js/modules/es.array.push.js"); +var BaseIntegrationModule = __webpack_require__(/*! ./base */ "../modules/forms/assets/js/editor/integrations/base.js"); +module.exports = BaseIntegrationModule.extend({ + fields: {}, + getName() { + return 'activecampaign'; + }, + onElementChange(setting) { + switch (setting) { + case 'activecampaign_api_credentials_source': + case 'activecampaign_api_key': + case 'activecampaign_api_url': + this.onApiUpdate(); + break; + case 'activecampaign_list': + this.onListUpdate(); + break; + } + }, + onApiUpdate() { + const self = this, + apikeyControlView = self.getEditorControlView('activecampaign_api_key'), + apiUrlControlView = self.getEditorControlView('activecampaign_api_url'), + apiCredControlView = self.getEditorControlView('activecampaign_api_credentials_source'); + if ('default' !== apiCredControlView.getControlValue() && ('' === apikeyControlView.getControlValue() || '' === apiUrlControlView.getControlValue())) { + self.updateOptions('activecampaign_list', []); + self.getEditorControlView('activecampaign_list').setValue(''); + return; + } + self.addControlSpinner('activecampaign_list'); + const cacheKey = this.getCacheKey({ + controls: [apiCredControlView.getControlValue(), apiUrlControlView.getControlValue(), apikeyControlView.getControlValue()] + }); + self.getActiveCampaignCache('lists', 'activecampaign_list', cacheKey).done(function (data) { + self.updateOptions('activecampaign_list', data.lists); + self.fields = data.fields; + }); + }, + onListUpdate() { + this.updateFieldsMapping(); + }, + updateFieldsMapping() { + var controlView = this.getEditorControlView('activecampaign_list'); + if (!controlView.getControlValue()) { + return; + } + var remoteFields = [{ + remote_label: __('Email', 'elementor'), + remote_type: 'email', + remote_id: 'email', + remote_required: true + }, { + remote_label: __('First Name', 'elementor'), + remote_type: 'text', + remote_id: 'first_name', + remote_required: false + }, { + remote_label: __('Last Name', 'elementor'), + remote_type: 'text', + remote_id: 'last_name', + remote_required: false + }, { + remote_label: __('Phone', 'elementor'), + remote_type: 'text', + remote_id: 'phone', + remote_required: false + }, { + remote_label: __('Organization name', 'elementor'), + remote_type: 'text', + remote_id: 'orgname', + remote_required: false + }]; + for (var field in this.fields) { + if (Object.prototype.hasOwnProperty.call(this.fields, field)) { + remoteFields.push(this.fields[field]); + } + } + this.getEditorControlView('activecampaign_fields_map').updateMap(remoteFields); + }, + getActiveCampaignCache(type, action, cacheKey, requestArgs) { + if (_.has(this.cache[type], cacheKey)) { + var data = {}; + data[type] = this.cache[type][cacheKey]; + return jQuery.Deferred().resolve(data); + } + requestArgs = _.extend({}, requestArgs, { + service: 'activecampaign', + activecampaign_action: action, + api_key: this.getEditorControlView('activecampaign_api_key').getControlValue(), + api_url: this.getEditorControlView('activecampaign_api_url').getControlValue(), + api_cred: this.getEditorControlView('activecampaign_api_credentials_source').getControlValue() + }); + return this.fetchCache(type, cacheKey, requestArgs); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/integrations/base.js": +/*!**************************************************************!*\ + !*** ../modules/forms/assets/js/editor/integrations/base.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +var ElementEditorModule = __webpack_require__(/*! elementor-pro/editor/element-editor-module */ "../assets/dev/js/editor/element-editor-module.js"); +module.exports = ElementEditorModule.extend({ + __construct() { + this.cache = {}; + ElementEditorModule.prototype.__construct.apply(this, arguments); + }, + getName() { + return ''; + }, + getCacheKey(args) { + return JSON.stringify({ + service: this.getName(), + data: args + }); + }, + fetchCache(type, cacheKey, requestArgs, immediately = false) { + return elementorPro.ajax.addRequest('forms_panel_action_data', { + unique_id: 'integrations_' + this.getName(), + data: requestArgs, + success: data => { + this.cache[type] = _.extend({}, this.cache[type]); + this.cache[type][cacheKey] = data[type]; + } + }, immediately); + }, + onInit() { + this.addSectionListener('section_' + this.getName(), this.onSectionActive); + }, + onSectionActive() { + this.onApiUpdate(); + }, + onApiUpdate() {} +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/integrations/convertkit.js": +/*!********************************************************************!*\ + !*** ../modules/forms/assets/js/editor/integrations/convertkit.js ***! + \********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +var BaseIntegrationModule = __webpack_require__(/*! ./base */ "../modules/forms/assets/js/editor/integrations/base.js"); +module.exports = BaseIntegrationModule.extend({ + getName() { + return 'convertkit'; + }, + onElementChange(setting) { + switch (setting) { + case 'convertkit_api_key_source': + case 'convertkit_custom_api_key': + this.onApiUpdate(); + break; + case 'convertkit_form': + this.onListUpdate(); + break; + } + }, + onApiUpdate() { + var self = this, + apiKeyControlView = self.getEditorControlView('convertkit_api_key_source'), + customApikeyControlView = self.getEditorControlView('convertkit_custom_api_key'); + if ('default' !== apiKeyControlView.getControlValue() && '' === customApikeyControlView.getControlValue()) { + self.updateOptions('convertkit_form', []); + self.getEditorControlView('convertkit_form').setValue(''); + return; + } + self.addControlSpinner('convertkit_form'); + const cacheKey = this.getCacheKey({ + type: 'data', + controls: [apiKeyControlView.getControlValue(), customApikeyControlView.getControlValue()] + }); + self.getConvertKitCache('data', 'convertkit_get_forms', cacheKey).done(function (data) { + self.updateOptions('convertkit_form', data.data.forms); + self.updateOptions('convertkit_tags', data.data.tags); + }); + }, + onListUpdate() { + this.updateFieldsMapping(); + }, + updateFieldsMapping() { + var controlView = this.getEditorControlView('convertkit_form'); + if (!controlView.getControlValue()) { + return; + } + var remoteFields = [{ + remote_label: __('Email', 'elementor'), + remote_type: 'email', + remote_id: 'email', + remote_required: true + }, { + remote_label: __('First Name', 'elementor'), + remote_type: 'text', + remote_id: 'first_name', + remote_required: false + }]; + this.getEditorControlView('convertkit_fields_map').updateMap(remoteFields); + }, + getConvertKitCache(type, action, cacheKey, requestArgs) { + if (_.has(this.cache[type], cacheKey)) { + var data = {}; + data[type] = this.cache[type][cacheKey]; + return jQuery.Deferred().resolve(data); + } + requestArgs = _.extend({}, requestArgs, { + service: 'convertkit', + convertkit_action: action, + api_key: this.getEditorControlView('convertkit_api_key_source').getControlValue(), + custom_api_key: this.getEditorControlView('convertkit_custom_api_key').getControlValue() + }); + return this.fetchCache(type, cacheKey, requestArgs); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/integrations/drip.js": +/*!**************************************************************!*\ + !*** ../modules/forms/assets/js/editor/integrations/drip.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +var BaseIntegrationModule = __webpack_require__(/*! ./base */ "../modules/forms/assets/js/editor/integrations/base.js"); +module.exports = BaseIntegrationModule.extend({ + getName() { + return 'drip'; + }, + onElementChange(setting) { + switch (setting) { + case 'drip_api_token_source': + case 'drip_custom_api_token': + this.onApiUpdate(); + break; + case 'drip_account': + this.onDripAccountsUpdate(); + break; + } + }, + onApiUpdate() { + var self = this, + controlView = self.getEditorControlView('drip_api_token_source'), + customControlView = self.getEditorControlView('drip_custom_api_token'); + if ('default' !== controlView.getControlValue() && '' === customControlView.getControlValue()) { + self.updateOptions('drip_account', []); + self.getEditorControlView('drip_account').setValue(''); + return; + } + self.addControlSpinner('drip_account'); + this.getCacheKey({ + type: 'accounts', + controls: [controlView.getControlValue(), customControlView.getControlValue()] + }); + self.getDripCache('accounts', 'accounts', controlView.getControlValue()).done(function (data) { + self.updateOptions('drip_account', data.accounts); + }); + }, + onDripAccountsUpdate() { + this.updateFieldsMapping(); + }, + updateFieldsMapping() { + var controlView = this.getEditorControlView('drip_account'); + if (!controlView.getControlValue()) { + return; + } + var remoteFields = { + remote_label: __('Email', 'elementor'), + remote_type: 'email', + remote_id: 'email', + remote_required: true + }; + this.getEditorControlView('drip_fields_map').updateMap([remoteFields]); + }, + getDripCache(type, action, cacheKey, requestArgs) { + if (_.has(this.cache[type], cacheKey)) { + var data = {}; + data[type] = this.cache[type][cacheKey]; + return jQuery.Deferred().resolve(data); + } + requestArgs = _.extend({}, requestArgs, { + service: 'drip', + drip_action: action, + api_token: this.getEditorControlView('drip_api_token_source').getControlValue(), + custom_api_token: this.getEditorControlView('drip_custom_api_token').getControlValue() + }); + return this.fetchCache(type, cacheKey, requestArgs); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/integrations/getresponse.js": +/*!*********************************************************************!*\ + !*** ../modules/forms/assets/js/editor/integrations/getresponse.js ***! + \*********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +var BaseIntegrationModule = __webpack_require__(/*! ./base */ "../modules/forms/assets/js/editor/integrations/base.js"); +module.exports = BaseIntegrationModule.extend({ + getName() { + return 'getresponse'; + }, + onElementChange(setting) { + switch (setting) { + case 'getresponse_custom_api_key': + case 'getresponse_api_key_source': + this.onApiUpdate(); + break; + case 'getresponse_list': + this.onGetResonseListUpdate(); + break; + } + }, + onApiUpdate() { + var self = this, + controlView = self.getEditorControlView('getresponse_api_key_source'), + customControlView = self.getEditorControlView('getresponse_custom_api_key'); + if ('default' !== controlView.getControlValue() && '' === customControlView.getControlValue()) { + self.updateOptions('getresponse_list', []); + self.getEditorControlView('getresponse_list').setValue(''); + return; + } + self.addControlSpinner('getresponse_list'); + const cacheKey = this.getCacheKey({ + type: 'lists', + controls: [controlView.getControlValue(), customControlView.getControlValue()] + }); + self.getCache('lists', 'lists', cacheKey).done(function (data) { + self.updateOptions('getresponse_list', data.lists); + }); + }, + onGetResonseListUpdate() { + this.updatGetResonseList(); + }, + updatGetResonseList() { + var self = this, + controlView = self.getEditorControlView('getresponse_list'); + if (!controlView.getControlValue()) { + return; + } + self.addControlSpinner('getresponse_fields_map'); + const cacheKey = this.getCacheKey({ + type: 'fields', + controls: [controlView.getControlValue()] + }); + self.getCache('fields', 'get_fields', cacheKey, { + getresponse_list: controlView.getControlValue() + }).done(function (data) { + self.getEditorControlView('getresponse_fields_map').updateMap(data.fields); + }); + }, + getCache(type, action, cacheKey, requestArgs) { + if (_.has(this.cache[type], cacheKey)) { + var data = {}; + data[type] = this.cache[type][cacheKey]; + return jQuery.Deferred().resolve(data); + } + requestArgs = _.extend({}, requestArgs, { + service: 'getresponse', + getresponse_action: action, + api_key: this.getEditorControlView('getresponse_api_key_source').getControlValue(), + custom_api_key: this.getEditorControlView('getresponse_custom_api_key').getControlValue() + }); + return this.fetchCache(type, cacheKey, requestArgs); + }, + onSectionActive() { + BaseIntegrationModule.prototype.onSectionActive.apply(this, arguments); + this.updatGetResonseList(); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/integrations/mailchimp.js": +/*!*******************************************************************!*\ + !*** ../modules/forms/assets/js/editor/integrations/mailchimp.js ***! + \*******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +var BaseIntegrationModule = __webpack_require__(/*! ./base */ "../modules/forms/assets/js/editor/integrations/base.js"); +module.exports = BaseIntegrationModule.extend({ + getName() { + return 'mailchimp'; + }, + onElementChange(setting) { + switch (setting) { + case 'mailchimp_api_key_source': + case 'mailchimp_api_key': + this.onApiUpdate(); + break; + case 'mailchimp_list': + this.onMailchimpListUpdate(); + break; + } + }, + onApiUpdate() { + var self = this, + controlView = self.getEditorControlView('mailchimp_api_key'), + GlobalApiKeycontrolView = self.getEditorControlView('mailchimp_api_key_source'); + if ('default' !== GlobalApiKeycontrolView.getControlValue() && '' === controlView.getControlValue()) { + self.updateOptions('mailchimp_list', []); + self.getEditorControlView('mailchimp_list').setValue(''); + return; + } + + // Add a spinner to the `Audience` list control. + self.resetControlIndicators('mailchimp_list'); + self.addControlSpinner('mailchimp_list'); + const cacheKey = this.getCacheKey({ + type: 'lists', + controls: [controlView.getControlValue(), GlobalApiKeycontrolView.getControlValue()] + }); + + // Fetch data + self.getMailchimpCache('lists', 'lists', cacheKey).done(function (data) { + self.updateOptions('mailchimp_list', data.lists); + self.updatMailchimpList(); + }).fail(function (error) { + self.addControlError('mailchimp_list', error); + }).always(function () { + self.removeControlSpinner('mailchimp_list'); + }); + }, + onMailchimpListUpdate() { + this.updateOptions('mailchimp_groups', []); + this.getEditorControlView('mailchimp_groups').setValue(''); + this.updatMailchimpList(); + }, + updatMailchimpList() { + var self = this, + controlView = self.getEditorControlView('mailchimp_list'); + if (!controlView.getControlValue()) { + return; + } + + // Add a spinner to the groups select box. + self.resetControlIndicators('mailchimp_groups'); + self.addControlSpinner('mailchimp_groups'); + this.getCacheKey({ + type: 'list_details', + controls: [controlView.getControlValue()] + }); + + // Fetch The data + self.getMailchimpCache('list_details', 'list_details', controlView.getControlValue(), { + mailchimp_list: controlView.getControlValue() + }).done(function (data) { + self.updateOptions('mailchimp_groups', data.list_details.groups); + self.getEditorControlView('mailchimp_fields_map').updateMap(data.list_details.fields); + }).fail(function (error) { + self.addControlError('mailchimp_groups', error); + }).always(function () { + self.removeControlSpinner('mailchimp_groups'); + }); + + // Get list fields. + // The requests needed to be executed immediately in order to fill the `Field Mapping` select-boxes + // without waiting for other requests to finish. + const args = { + type: 'fields', + action: 'fields', + cacheKey: controlView.getControlValue(), + args: { + mailchimp_list: controlView.getControlValue() + }, + immediately: true + }; + self.getMailchimpCache(...Object.values(args)).done(function (data) { + self.getEditorControlView('mailchimp_fields_map').updateMap(data.fields); + }); + }, + getMailchimpCache(type, action, cacheKey, requestArgs, immediately = false) { + if (_.has(this.cache[type], cacheKey)) { + var data = {}; + data[type] = this.cache[type][cacheKey]; + return jQuery.Deferred().resolve(data); + } + requestArgs = _.extend({}, requestArgs, { + service: 'mailchimp', + mailchimp_action: action, + api_key: this.getEditorControlView('mailchimp_api_key').getControlValue(), + use_global_api_key: this.getEditorControlView('mailchimp_api_key_source').getControlValue() + }); + return this.fetchCache(type, cacheKey, requestArgs, immediately); + }, + onSectionActive() { + BaseIntegrationModule.prototype.onSectionActive.apply(this, arguments); + this.onApiUpdate(); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/integrations/mailerlite.js": +/*!********************************************************************!*\ + !*** ../modules/forms/assets/js/editor/integrations/mailerlite.js ***! + \********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +__webpack_require__(/*! core-js/modules/es.array.push.js */ "../node_modules/core-js/modules/es.array.push.js"); +const BaseIntegrationModule = __webpack_require__(/*! ./base */ "../modules/forms/assets/js/editor/integrations/base.js"); +module.exports = BaseIntegrationModule.extend({ + fields: {}, + getName() { + return 'mailerlite'; + }, + onElementChange(setting) { + switch (setting) { + case 'mailerlite_api_key_source': + case 'mailerlite_custom_api_key': + this.onMailerliteApiKeyUpdate(); + break; + case 'mailerlite_group': + this.updateFieldsMapping(); + break; + } + }, + onMailerliteApiKeyUpdate() { + var self = this, + controlView = self.getEditorControlView('mailerlite_custom_api_key'), + GlobalApiKeycontrolView = self.getEditorControlView('mailerlite_api_key_source'); + if ('default' !== GlobalApiKeycontrolView.getControlValue() && '' === controlView.getControlValue()) { + self.updateOptions('mailerlite_group', []); + self.getEditorControlView('mailerlite_group').setValue(''); + return; + } + self.addControlSpinner('mailerlite_group'); + const cacheKey = this.getCacheKey({ + type: 'groups', + controls: [controlView.getControlValue(), GlobalApiKeycontrolView.getControlValue()] + }); + self.getMailerliteCache('groups', 'groups', cacheKey).done(function (data) { + self.updateOptions('mailerlite_group', data.groups); + self.fields = data.fields; + }); + }, + updateFieldsMapping() { + const controlView = this.getEditorControlView('mailerlite_group'); + if (!controlView.getControlValue()) { + return; + } + const remoteFields = [{ + remote_label: __('Email', 'elementor'), + remote_type: 'email', + remote_id: 'email', + remote_required: true + }, { + remote_label: __('Name', 'elementor'), + remote_type: 'text', + remote_id: 'name', + remote_required: false + }, { + remote_label: __('Last Name', 'elementor'), + remote_type: 'text', + remote_id: 'last_name', + remote_required: false + }, { + remote_label: __('Company', 'elementor'), + remote_type: 'text', + remote_id: 'company', + remote_required: false + }, { + remote_label: __('Phone', 'elementor'), + remote_type: 'text', + remote_id: 'phone', + remote_required: false + }, { + remote_label: __('Country', 'elementor'), + remote_type: 'text', + remote_id: 'country', + remote_required: false + }, { + remote_label: __('State', 'elementor'), + remote_type: 'text', + remote_id: 'state', + remote_required: false + }, { + remote_label: __('City', 'elementor'), + remote_type: 'text', + remote_id: 'city', + remote_required: false + }, { + remote_label: __('Zip', 'elementor'), + remote_type: 'text', + remote_id: 'zip', + remote_required: false + }]; + for (const field in this.fields) { + if (Object.prototype.hasOwnProperty.call(this.fields, field)) { + remoteFields.push(this.fields[field]); + } + } + this.getEditorControlView('mailerlite_fields_map').updateMap(remoteFields); + }, + getMailerliteCache(type, action, cacheKey, requestArgs) { + if (_.has(this.cache[type], cacheKey)) { + const data = {}; + data[type] = this.cache[type][cacheKey]; + return jQuery.Deferred().resolve(data); + } + requestArgs = _.extend({}, requestArgs, { + service: 'mailerlite', + mailerlite_action: action, + custom_api_key: this.getEditorControlView('mailerlite_custom_api_key').getControlValue(), + api_key: this.getEditorControlView('mailerlite_api_key_source').getControlValue() + }); + return this.fetchCache(type, cacheKey, requestArgs); + }, + onSectionActive() { + BaseIntegrationModule.prototype.onSectionActive.apply(this, arguments); + this.onMailerliteApiKeyUpdate(); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/module.js": +/*!***************************************************!*\ + !*** ../modules/forms/assets/js/editor/module.js ***! + \***************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _component = _interopRequireDefault(__webpack_require__(/*! ./component */ "../modules/forms/assets/js/editor/component.js")); +class FormsModule extends elementorModules.editor.utils.Module { + onElementorInit() { + const ReplyToField = __webpack_require__(/*! ./reply-to-field */ "../modules/forms/assets/js/editor/reply-to-field.js"), + Recaptcha = __webpack_require__(/*! ./recaptcha */ "../modules/forms/assets/js/editor/recaptcha.js"), + MailerLite = __webpack_require__(/*! ./integrations/mailerlite */ "../modules/forms/assets/js/editor/integrations/mailerlite.js"), + Mailchimp = __webpack_require__(/*! ./integrations/mailchimp */ "../modules/forms/assets/js/editor/integrations/mailchimp.js"), + Drip = __webpack_require__(/*! ./integrations/drip */ "../modules/forms/assets/js/editor/integrations/drip.js"), + ActiveCampaign = __webpack_require__(/*! ./integrations/activecampaign */ "../modules/forms/assets/js/editor/integrations/activecampaign.js"), + GetResponse = __webpack_require__(/*! ./integrations/getresponse */ "../modules/forms/assets/js/editor/integrations/getresponse.js"), + ConvertKit = __webpack_require__(/*! ./integrations/convertkit */ "../modules/forms/assets/js/editor/integrations/convertkit.js"); + this.replyToField = new ReplyToField(); + this.mailchimp = new Mailchimp('form'); + this.recaptcha = new Recaptcha('form'); + this.drip = new Drip('form'); + this.activecampaign = new ActiveCampaign('form'); + this.getresponse = new GetResponse('form'); + this.convertkit = new ConvertKit('form'); + this.mailerlite = new MailerLite('form'); + + // Form fields + const TimeField = __webpack_require__(/*! ./fields/time */ "../modules/forms/assets/js/editor/fields/time.js"), + DateField = __webpack_require__(/*! ./fields/date */ "../modules/forms/assets/js/editor/fields/date.js"), + AcceptanceField = __webpack_require__(/*! ./fields/acceptance */ "../modules/forms/assets/js/editor/fields/acceptance.js"), + UploadField = __webpack_require__(/*! ./fields/upload */ "../modules/forms/assets/js/editor/fields/upload.js"), + TelField = __webpack_require__(/*! ./fields/tel */ "../modules/forms/assets/js/editor/fields/tel.js"); + this.Fields = { + time: new TimeField('form'), + date: new DateField('form'), + tel: new TelField('form'), + acceptance: new AcceptanceField('form'), + upload: new UploadField('form') + }; + elementor.addControlView('Fields_map', __webpack_require__(/*! ./fields-map-control */ "../modules/forms/assets/js/editor/fields-map-control.js")); + elementor.addControlView('form-fields-repeater', __webpack_require__(/*! ./fields-repeater-control */ "../modules/forms/assets/js/editor/fields-repeater-control.js")); + } + onElementorInitComponents() { + $e.components.register(new _component.default({ + manager: this + })); + } +} +exports["default"] = FormsModule; + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/recaptcha.js": +/*!******************************************************!*\ + !*** ../modules/forms/assets/js/editor/recaptcha.js ***! + \******************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + enqueueRecaptchaJs(url, type) { + if (!elementorFrontend.elements.$body.find('[src="' + url + '"]').length) { + elementorFrontend.elements.$body.append(''); + } + }, + renderField(inputField, item) { + inputField += '
'; + inputField += this.getDataSettings(item); + inputField += '
'; + return inputField; + }, + getDataSettings(item) { + const config = elementorPro.config.forms[item.field_type], + srcURL = 'https://www.google.com/recaptcha/api.js?render=explicit'; + if (!config.enabled) { + return '
' + config.setup_message + '
'; + } + let recaptchaData = 'data-sitekey="' + config.site_key + '" data-type="' + config.type + '"'; + switch (config.type) { + case 'v3': + recaptchaData += ' data-action="form" data-size="invisible" data-badge="' + item.recaptcha_badge + '"'; + break; + case 'v2_checkbox': + recaptchaData += ' data-theme="' + item.recaptcha_style + '"'; + recaptchaData += ' data-size="' + item.recaptcha_size + '"'; + break; + } + this.enqueueRecaptchaJs(srcURL, config.type); + return '
'; + }, + filterItem(item) { + if ('recaptcha' === item.field_type) { + item.field_label = false; + } + return item; + }, + onInit() { + elementor.hooks.addFilter('elementor_pro/forms/content_template/item', this.filterItem); + elementor.hooks.addFilter('elementor_pro/forms/content_template/field/recaptcha', this.renderField, 10, 2); + elementor.hooks.addFilter('elementor_pro/forms/content_template/field/recaptcha_v3', this.renderField, 10, 2); + } +}); + +/***/ }), + +/***/ "../modules/forms/assets/js/editor/reply-to-field.js": +/*!***********************************************************!*\ + !*** ../modules/forms/assets/js/editor/reply-to-field.js ***! + \***********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var sprintf = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["sprintf"]; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +module.exports = function () { + var editor, editedModel, replyToControl; + var setReplyToControl = function () { + replyToControl = editor.collection.findWhere({ + name: 'email_reply_to' + }); + }; + var getReplyToView = function () { + return editor.children.findByModelCid(replyToControl.cid); + }; + var refreshReplyToElement = function () { + var replyToView = getReplyToView(); + if (replyToView) { + replyToView.render(); + } + }; + var updateReplyToOptions = function () { + var settingsModel = editedModel.get('settings'), + emailModels = settingsModel.get('form_fields').where({ + field_type: 'email' + }), + emailFields; + emailModels = _.reject(emailModels, { + field_label: '' + }); + emailFields = _.map(emailModels, function (model) { + return { + id: model.get('custom_id'), + label: sprintf(__('%s Field', 'elementor-pro'), model.get('field_label')) + }; + }); + replyToControl.set('options', { + '': replyToControl.get('options')[''] + }); + _.each(emailFields, function (emailField) { + replyToControl.get('options')[emailField.id] = emailField.label; + }); + refreshReplyToElement(); + }; + var updateDefaultReplyTo = function (settingsModel) { + replyToControl.get('options')[''] = settingsModel.get('email_from'); + refreshReplyToElement(); + }; + var onFormFieldsChange = function (changedModel) { + // If it's repeater field + if (changedModel.get('custom_id')) { + if ('email' === changedModel.get('field_type')) { + updateReplyToOptions(); + } + } + if (changedModel.changed.email_from) { + updateDefaultReplyTo(changedModel); + } + }; + var onPanelShow = function (panel, model) { + editor = panel.getCurrentPageView(); + editedModel = model; + setReplyToControl(); + var settingsModel = editedModel.get('settings'); + settingsModel.on('change', onFormFieldsChange); + updateDefaultReplyTo(settingsModel); + updateReplyToOptions(); + }; + var init = function () { + elementor.hooks.addAction('panel/open_editor/widget/form', onPanelShow); + }; + init(); +}; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/commands-data/index.js": +/*!************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/commands-data/index.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "Templates", ({ + enumerable: true, + get: function () { + return _templates.Templates; + } +})); +var _templates = __webpack_require__(/*! ./templates */ "../modules/global-widget/assets/js/editor/commands-data/templates.js"); + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/commands-data/templates.js": +/*!****************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/commands-data/templates.js ***! + \****************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.Templates = void 0; +/** + * Data command: 'document/global/templates', accessing 'global-widget/templates' remote endpoint. + * Used to get global templates from the backend/cache. + */ +class Templates extends $e.modules.CommandData { + static getEndpointFormat() { + return 'global-widget/templates'; + } + onAfterApply(args = {}, result) { + // TODO: Remove - Manually handling of cache - This behavior should be automatically handled by passed `options` to $e.data. + $e.data.deleteCache(this.component, 'document/global/global-widget/templates', args.query); + Object.entries(result.data).forEach(([templateID, data]) => { + $e.data.setCache(this.component, `document/global/global-widget/templates/${templateID}`, {}, data); + }); + } +} +exports.Templates = Templates; +var _default = exports["default"] = Templates; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/commands-internal/index.js": +/*!****************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/commands-internal/index.js ***! + \****************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "SaveTemplates", ({ + enumerable: true, + get: function () { + return _saveTemplates.SaveTemplates; + } +})); +var _saveTemplates = __webpack_require__(/*! ./save-templates */ "../modules/global-widget/assets/js/editor/commands-internal/save-templates.js"); + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/commands-internal/save-templates.js": +/*!*************************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/commands-internal/save-templates.js ***! + \*************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.SaveTemplates = void 0; +__webpack_require__(/*! core-js/modules/es.array.push.js */ "../node_modules/core-js/modules/es.array.push.js"); +/** + * The command should run over all changed global widgets and + * update the settings of the `document/global/global-widget/templates`, + * And save cache templates according to the widget(s) which are under the save process. + */ +class SaveTemplates extends $e.modules.CommandInternalBase { + apply() { + const templateModels = this.getCurrentTemplatesModels(this.component.changedContainersId); + if (!templateModels.length) { + return; + } + return new Promise((resolve, reject) => { + elementorCommon.ajax.addRequest('update_templates', { + data: { + templates: templateModels.map(templateModel => { + // Map it to backend format. + return { + id: templateModel.get('id'), + content: JSON.stringify([templateModel.toJSON()]), + source: 'local', + type: 'widget' + }; + }) + }, + error: reject, + success: () => { + /** + * Since is used `document/global/global-widget/templates` to hold all globals template data. + * And currently there are no request to update template data on each update of global widget, + * editing the template will be not synced with The real latest data. + * In other words, if dont update templates on each save, + * Then the new created template will be different with the actual (saved) one, so updating the globals template + * according to saved global widget is the solution. + */ + // Clear changed containers. + this.component.changedContainersId = {}; + templateModels.forEach(template => { + const settings = template.get('settings'); + $e.data.setCache(this.component, `document/global/global-widget/templates/${template.id}`, {}, { + settings + }); + }); + resolve(templateModels); + } + }); + }); + } + getCurrentTemplatesModels(changedContainersId) { + const templatesData = []; + Object.entries(changedContainersId).forEach(([templateID, containerId]) => { + const templateData = $e.data.getCache(this.component, `document/global/global-widget/templates/${templateID}`); + if (!templateData) { + if ($e.devTools) { + $e.devTools.log.warn(`$e.data.getCache( component, \`document/global/global-widget/templates/${templateID}\` ) - not found.`); + } + } + const container = elementor.getContainer(containerId); + if (!container) { + return; + } + templatesData.push(new Backbone.Model({ + id: templateID, + elType: 'widget', + widgetType: container.model.get('widgetType'), + settings: container.settings.toJSON({ + remove: 'default' + }), + templateID + })); + }); + return templatesData; + } +} +exports.SaveTemplates = SaveTemplates; +var _default = exports["default"] = SaveTemplates; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/commands/index.js": +/*!*******************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/commands/index.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "Link", ({ + enumerable: true, + get: function () { + return _link.Link; + } +})); +Object.defineProperty(exports, "Unlink", ({ + enumerable: true, + get: function () { + return _unlink.Unlink; + } +})); +var _link = __webpack_require__(/*! ./link */ "../modules/global-widget/assets/js/editor/commands/link.js"); +var _unlink = __webpack_require__(/*! ./unlink */ "../modules/global-widget/assets/js/editor/commands/unlink.js"); + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/commands/link.js": +/*!******************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/commands/link.js ***! + \******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.Link = void 0; +class Link extends $e.modules.editor.document.CommandHistoryBase { + validateArgs(args) { + this.requireContainer(args); + this.requireArgumentConstructor('data', Object, args); + const { + containers = [args.container] + } = args; + containers.forEach(( /* Container */container) => { + if ('global' === container.model.get('widgetType')) { + throw Error(`Invalid container, id: '${container.id}' is already global.`); + } + }); + } + getHistory(args) { + const { + data + } = args; + return { + title: elementor.widgetsCache[data.widgetType].title, + subTitle: data.title, + type: __('Linked to Global', 'elementor-pro') + }; + } + apply(args) { + const { + data, + containers = [args.container] + } = args; + containers.forEach(( /** Container */container) => { + const widgetModel = container.model, + widgetModelIndex = widgetModel.collection.indexOf(widgetModel); + data.elType = data.type; + data.settings = widgetModel.get('settings').attributes; + data.widgetType = widgetModel.get('widgetType'); + const elementModel = elementorPro.modules.globalWidget.addGlobalWidget(data.template_id, data), + elementModelAttributes = elementModel.attributes; + $e.data.setCache(this.component, `document/global/global-widget/templates/${data.template_id}`, {}, data); + $e.run('document/elements/create', { + container: container.parent, + model: { + id: elementorCommon.helpers.getUniqueId(), + elType: elementModelAttributes.elType, + widgetType: elementModelAttributes.widgetType, + templateID: data.template_id + }, + options: { + at: widgetModelIndex + } + }); + $e.run('document/elements/delete', { + container + }); + }); + $e.route('panel/elements/global'); + } +} +exports.Link = Link; +var _default = exports["default"] = Link; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/commands/unlink.js": +/*!********************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/commands/unlink.js ***! + \********************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.Unlink = void 0; +class Unlink extends $e.modules.editor.document.CommandHistoryBase { + validateArgs(args) { + this.requireContainer(args); + } + getHistory(args) { + const { + containers = [args.container] + } = args; + return { + title: elementor.helpers.getModelLabel(containers[0].model), + // TODO: add support multi containers. + type: __('Unlink Widget', 'elementor-pro') + }; + } + async apply(args) { + const { + containers = [args.container] + } = args; + const ids = containers.map(( /** Container */container) => container.model.get('templateID')); + const { + data + } = await $e.data.get('document/global/templates', { + ids + }); + containers.forEach(( /** Container */container) => { + const id = container.model.get('templateID'), + elementModel = elementorPro.modules.globalWidget.createGlobalModel(id, data[id]); + $e.run('document/elements/create', { + container: container.parent, + model: { + id: elementorCommon.helpers.getUniqueId(), + elType: 'widget', + widgetType: elementModel.get('widgetType'), + settings: elementorCommon.helpers.cloneObject(elementModel.get('settings').attributes), + defaultEditSettings: elementorCommon.helpers.cloneObject(elementModel.get('editSettings').attributes) + }, + options: { + at: container.model.collection.indexOf(container.model), + edit: true + } + }); + $e.run('document/elements/delete', { + container + }); + }); + } +} +exports.Unlink = Unlink; +var _default = exports["default"] = Unlink; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/component.js": +/*!**************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/component.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var commands = _interopRequireWildcard(__webpack_require__(/*! ./commands/ */ "../modules/global-widget/assets/js/editor/commands/index.js")); +var commandsInternal = _interopRequireWildcard(__webpack_require__(/*! ./commands-internal/ */ "../modules/global-widget/assets/js/editor/commands-internal/index.js")); +var commandsData = _interopRequireWildcard(__webpack_require__(/*! ./commands-data/ */ "../modules/global-widget/assets/js/editor/commands-data/index.js")); +var hooks = _interopRequireWildcard(__webpack_require__(/*! ./hooks/ */ "../modules/global-widget/assets/js/editor/hooks/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class Component extends $e.modules.ComponentBase { + /** + * Holds all the template ids, which not available due they simply not exist in document data. + * Those templates will be loaded later after requesting 'panel/elements/global' (global elements panel). + * + * @type {Array} + */ + notLoadedTemplatesIds = []; + + /** + * Last changed global widget(s). + * + * @type {null | Array} Container[] + */ + lastChangedContainers = null; + + /** + * Hold unsaved changed container id for each template id. + * + * Each settings command that run over global widget, this logic is applied: + * `changedContainersId[ templateId ] = containerId`. + * + * @type {{}} + */ + changedContainersId = {}; + registerAPI() { + super.registerAPI(); + + // TODO: Remove when route hooks are available. + $e.routes.on('run:after', (component, route) => { + if ('panel/elements/global' === route) { + this.onRoutePanelElementsGlobal(); + } + }); + } + getNamespace() { + return 'document/global'; + } + defaultCommands() { + return this.importCommands(commands); + } + defaultCommandsInternal() { + return this.importCommands(commandsInternal); + } + defaultData() { + return this.importCommands(commandsData); + } + defaultHooks() { + return this.importHooks(hooks); + } + onRoutePanelElementsGlobal() { + if (this.notLoadedTemplatesIds.length) { + $e.data.get('document/global/templates', { + ids: this.notLoadedTemplatesIds + }).then(() => { + // Clear. + this.notLoadedTemplatesIds = []; + }); + } + } + + /** + * Update each 'Backbone.Model' will handle issue when the global widget saved only in draft. + * Scenario for better understanding the issue: + * - Have global widget save with custom color, refresh the editor. + * - Change it to global global color and save as draft (no update template). + * - Create another global-widget from same template. + * - Update one of first global widget that saved in draft to use custom color. + * - By dependency of only 'container.settings' the new template will have the new custom color, + * but new custom color will unseen (since it has global). + * + * @param {Object} targetContainer Container class + */ + updateGlobalsRecursive(targetContainer) { + const modelsToUpdate = ['dynamic', 'globals', 'settings']; + elementor.getPreviewContainer().forEachChildrenRecursive(container => { + // Will skip self. + if (targetContainer !== container && parseInt(container.model.get('templateID')) === parseInt(targetContainer.model.get('templateID'))) { + modelsToUpdate.forEach(modelName => { + const model = targetContainer[modelName]; + if (model instanceof Backbone.Model) { + const accordingTo = 'settings' === modelName ? targetContainer.settings.attributes : model.changed; + Object.entries(accordingTo).forEach(([key, setting]) => { + container[modelName].set(key, setting); + }); + } + }); + container.render(); + } + }); + } +} +exports["default"] = Component; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/hooks/data/base-global-widget-prepare-update.js": +/*!*************************************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/hooks/data/base-global-widget-prepare-update.js ***! + \*************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.BaseGlobalWidgetPrepareUpdate = void 0; +/** + * Hook is responsible for saving last changed global widget and update + * which containers are needed for updating the template. + */ +class BaseGlobalWidgetPrepareUpdate extends $e.modules.hookData.After { + getConditions(args) { + const { + containers = [args.container] + } = args; + + // When the container is repeater item it should add the global repeater itself to the `lastChangedContainers` and not the repeater item + return containers.some(container => container.renderer?.model?.get('templateID')); + } + apply(args) { + const { + containers = [args.container] + } = args, + component = $e.components.get('document/global'); + + // Filter only the containers that are global widgets. (Can pass multiple containers that some of them global widgets and some of them not). + const globalWidgetContainers = containers.filter(container => container.renderer?.model?.get('templateID')); + component.lastChangedContainers = globalWidgetContainers.map(container => container.renderer); + globalWidgetContainers.forEach(container => { + component.changedContainersId[container.renderer.model.get('templateID')] = container.renderer.id; + }); + } +} +exports.BaseGlobalWidgetPrepareUpdate = BaseGlobalWidgetPrepareUpdate; +var _default = exports["default"] = BaseGlobalWidgetPrepareUpdate; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/hooks/data/document/elements/set-settings/global-widget-prepare-update-element-set-settings.js": +/*!************************************************************************************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/hooks/data/document/elements/set-settings/global-widget-prepare-update-element-set-settings.js ***! + \************************************************************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.GlobalWidgetPrepareUpdateElementSetSettings = void 0; +var _baseGlobalWidgetPrepareUpdate = __webpack_require__(/*! ../../../base-global-widget-prepare-update */ "../modules/global-widget/assets/js/editor/hooks/data/base-global-widget-prepare-update.js"); +/** + * Hook is responsible for saving last changed global widget and update + * which containers are needed for updating the template. + */ + +class GlobalWidgetPrepareUpdateElementSetSettings extends _baseGlobalWidgetPrepareUpdate.BaseGlobalWidgetPrepareUpdate { + getCommand() { + return 'document/elements/set-settings'; + } + getId() { + return 'elementor-pro-global-widget-prepare-update-element-set-settings'; + } +} +exports.GlobalWidgetPrepareUpdateElementSetSettings = GlobalWidgetPrepareUpdateElementSetSettings; +var _default = exports["default"] = GlobalWidgetPrepareUpdateElementSetSettings; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/hooks/data/document/history/end-log/global-widget-do-update.js": +/*!****************************************************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/hooks/data/document/history/end-log/global-widget-do-update.js ***! + \****************************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.GlobalWidgetDoUpdate = void 0; +/** + * On after all `document/elements/set-settings` has stop, the history mechanism will call to + * `document/history/end-log` the hook will update all other global widgets according to this last change. + */ +class GlobalWidgetDoUpdate extends $e.modules.hookData.After { + getCommand() { + return 'document/history/end-log'; + } + getId() { + return 'elementor-pro-global-widget-do-update'; + } + getConditions() { + return $e.components.get('document/global').lastChangedContainers; + } + apply() { + const component = $e.components.get('document/global'), + containers = component.lastChangedContainers; + containers.forEach(container => component.updateGlobalsRecursive(container)); + component.lastChangedContainers = null; + } +} +exports.GlobalWidgetDoUpdate = GlobalWidgetDoUpdate; +var _default = exports["default"] = GlobalWidgetDoUpdate; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/hooks/data/document/repeater/insert/global-widget-prepare-update-repeater-insert.js": +/*!*************************************************************************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/hooks/data/document/repeater/insert/global-widget-prepare-update-repeater-insert.js ***! + \*************************************************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.GlobalWidgetPrepareUpdateRepeaterInsert = void 0; +var _baseGlobalWidgetPrepareUpdate = _interopRequireDefault(__webpack_require__(/*! ../../../base-global-widget-prepare-update */ "../modules/global-widget/assets/js/editor/hooks/data/base-global-widget-prepare-update.js")); +/** + * Hook is responsible for saving last changed global widget and update + * which containers are needed for updating the template. + */ + +class GlobalWidgetPrepareUpdateRepeaterInsert extends _baseGlobalWidgetPrepareUpdate.default { + getCommand() { + return 'document/repeater/insert'; + } + getId() { + return 'elementor-pro-global-widget-prepare-update-repeater-insert'; + } +} +exports.GlobalWidgetPrepareUpdateRepeaterInsert = GlobalWidgetPrepareUpdateRepeaterInsert; +var _default = exports["default"] = GlobalWidgetPrepareUpdateRepeaterInsert; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/hooks/data/document/repeater/remove/global-widget-prepare-update-repeater-remove.js": +/*!*************************************************************************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/hooks/data/document/repeater/remove/global-widget-prepare-update-repeater-remove.js ***! + \*************************************************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.GlobalWidgetPrepareUpdateRepeaterRemove = void 0; +var _baseGlobalWidgetPrepareUpdate = __webpack_require__(/*! ../../../base-global-widget-prepare-update */ "../modules/global-widget/assets/js/editor/hooks/data/base-global-widget-prepare-update.js"); +/** + * Hook is responsible for saving last changed global widget and update + * which containers are needed for updating the template. + */ + +class GlobalWidgetPrepareUpdateRepeaterRemove extends _baseGlobalWidgetPrepareUpdate.BaseGlobalWidgetPrepareUpdate { + getCommand() { + return 'document/repeater/remove'; + } + getId() { + return 'elementor-pro-global-widget-prepare-update-repeater-remove'; + } +} +exports.GlobalWidgetPrepareUpdateRepeaterRemove = GlobalWidgetPrepareUpdateRepeaterRemove; +var _default = exports["default"] = GlobalWidgetPrepareUpdateRepeaterRemove; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/hooks/data/document/save/save/global-widget-save-templates.js": +/*!***************************************************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/hooks/data/document/save/save/global-widget-save-templates.js ***! + \***************************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.GlobalWidgetSaveTemplates = void 0; +/** + * The hook is responsible for updating the global templates, on editor save, + * hook will run 'document/global/save-templates' to handle the save. + */ +class GlobalWidgetSaveTemplates extends $e.modules.hookData.After { + getCommand() { + return 'document/save/save'; + } + getId() { + return 'elementor-pro-global-widget-save-templates'; + } + getConditions(args) { + if (!Object.keys($e.components.get('document/global').changedContainersId).length) { + return false; + } + const { + document = elementor.documents.getCurrent() + } = args; + return document.config.panel.has_elements && args.status && -1 !== ['private', 'publish'].indexOf(args.status); + } + apply() { + $e.internal('document/global/save-templates'); + } +} +exports.GlobalWidgetSaveTemplates = GlobalWidgetSaveTemplates; +var _default = exports["default"] = GlobalWidgetSaveTemplates; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/hooks/data/editor/documents/attach-preview/global-widget-load-templates.js": +/*!****************************************************************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/hooks/data/editor/documents/attach-preview/global-widget-load-templates.js ***! + \****************************************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.GlobalWidgetLoadTemplates = void 0; +__webpack_require__(/*! core-js/modules/es.array.push.js */ "../node_modules/core-js/modules/es.array.push.js"); +/** + * Hook responsible to load current active templates ( global widget that are in used ) to `$e.data.cache`, + * also it tells the component which templates are not active and required to be loaded from the backend. + */ +class GlobalWidgetLoadTemplates extends $e.modules.hookData.After { + /** + * Since the hook called on each document load, but requires to run only the initial attach preview. + * + * @type {boolean} + */ + static calledOnce = false; + initialize() { + // Since 'initialize' called before the component is registered. + // TODO: apply this logic at HookBase for '.initialize. + setTimeout(() => { + this.component = $e.components.get('document/global'); + }); + } + getCommand() { + return 'editor/documents/attach-preview'; + } + getId() { + return 'elementor-pro-global-widget-load-templates'; + } + getConditions() { + return !GlobalWidgetLoadTemplates.calledOnce; + } + apply() { + GlobalWidgetLoadTemplates.calledOnce = true; + Object.entries(elementorPro.config.widget_templates).forEach(([id, data]) => { + elementorPro.modules.globalWidget.addGlobalWidget(id, data); + this.addTemplateToCache(id); + }); + } + addTemplateToCache(id) { + const container = elementor.getPreviewContainer().findChildrenRecursive(i => parseInt(i.model.get('templateID')) === parseInt(id)); + if (!container) { + return this.component.notLoadedTemplatesIds.push(id); + } + const args = { + id: container.model.get('templateID'), + elType: 'widget', + widgetType: container.model.get('widgetType'), + settings: container.settings.toJSON({ + remove: 'default' + }), + templateID: container.model.get('templateID') + }; + $e.data.setCache(this.component, `document/global/global-widget/templates/${id}`, {}, args); + } +} +exports.GlobalWidgetLoadTemplates = GlobalWidgetLoadTemplates; +var _default = exports["default"] = GlobalWidgetLoadTemplates; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/hooks/data/index.js": +/*!*********************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/hooks/data/index.js ***! + \*********************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "GlobalWidgetDoUpdate", ({ + enumerable: true, + get: function () { + return _globalWidgetDoUpdate.GlobalWidgetDoUpdate; + } +})); +Object.defineProperty(exports, "GlobalWidgetLoadTemplates", ({ + enumerable: true, + get: function () { + return _globalWidgetLoadTemplates.GlobalWidgetLoadTemplates; + } +})); +Object.defineProperty(exports, "GlobalWidgetPrepareUpdateElementSetSettings", ({ + enumerable: true, + get: function () { + return _globalWidgetPrepareUpdateElementSetSettings.GlobalWidgetPrepareUpdateElementSetSettings; + } +})); +Object.defineProperty(exports, "GlobalWidgetPrepareUpdateRepeaterInsert", ({ + enumerable: true, + get: function () { + return _globalWidgetPrepareUpdateRepeaterInsert.GlobalWidgetPrepareUpdateRepeaterInsert; + } +})); +Object.defineProperty(exports, "GlobalWidgetPrepareUpdateRepeaterRemove", ({ + enumerable: true, + get: function () { + return _globalWidgetPrepareUpdateRepeaterRemove.GlobalWidgetPrepareUpdateRepeaterRemove; + } +})); +Object.defineProperty(exports, "GlobalWidgetSaveTemplates", ({ + enumerable: true, + get: function () { + return _globalWidgetSaveTemplates.GlobalWidgetSaveTemplates; + } +})); +var _globalWidgetPrepareUpdateElementSetSettings = __webpack_require__(/*! ./document/elements/set-settings/global-widget-prepare-update-element-set-settings */ "../modules/global-widget/assets/js/editor/hooks/data/document/elements/set-settings/global-widget-prepare-update-element-set-settings.js"); +var _globalWidgetPrepareUpdateRepeaterInsert = __webpack_require__(/*! ./document/repeater/insert/global-widget-prepare-update-repeater-insert */ "../modules/global-widget/assets/js/editor/hooks/data/document/repeater/insert/global-widget-prepare-update-repeater-insert.js"); +var _globalWidgetPrepareUpdateRepeaterRemove = __webpack_require__(/*! ./document/repeater/remove/global-widget-prepare-update-repeater-remove */ "../modules/global-widget/assets/js/editor/hooks/data/document/repeater/remove/global-widget-prepare-update-repeater-remove.js"); +var _globalWidgetDoUpdate = __webpack_require__(/*! ./document/history/end-log/global-widget-do-update */ "../modules/global-widget/assets/js/editor/hooks/data/document/history/end-log/global-widget-do-update.js"); +var _globalWidgetSaveTemplates = __webpack_require__(/*! ./document/save/save/global-widget-save-templates */ "../modules/global-widget/assets/js/editor/hooks/data/document/save/save/global-widget-save-templates.js"); +var _globalWidgetLoadTemplates = __webpack_require__(/*! ./editor/documents/attach-preview/global-widget-load-templates */ "../modules/global-widget/assets/js/editor/hooks/data/editor/documents/attach-preview/global-widget-load-templates.js"); + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/hooks/index.js": +/*!****************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/hooks/index.js ***! + \****************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +var _data = __webpack_require__(/*! ./data/ */ "../modules/global-widget/assets/js/editor/hooks/data/index.js"); +Object.keys(_data).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _data[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _data[key]; + } + }); +}); +var _ui = __webpack_require__(/*! ./ui/ */ "../modules/global-widget/assets/js/editor/hooks/ui/index.js"); +Object.keys(_ui).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _ui[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _ui[key]; + } + }); +}); + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/hooks/ui/document/elements/set-settings/global-widget-history-update.js": +/*!*************************************************************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/hooks/ui/document/elements/set-settings/global-widget-history-update.js ***! + \*************************************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.GlobalWidgetHistoryUpdate = void 0; +/** + * Since editing of global widget applies changes to the all widgets with the same template id, + * the same needs to be done on undo/redo. + */ +class GlobalWidgetHistoryUpdate extends $e.modules.hookUI.After { + getCommand() { + return 'document/elements/set-settings'; + } + getId() { + return 'elementor-pro-global-widget-history-update'; + } + getContainerType() { + return 'widget'; + } + getConditions(args) { + const { + containers = [args.container] + } = args; + return !elementor.documents.getCurrent().history.getActive() && containers.some(container => container.model.get('templateID')); + } + apply(args) { + const { + containers = [args.container] + } = args; + containers.forEach(container => $e.components.get('document/global').updateGlobalsRecursive(container)); + } +} +exports.GlobalWidgetHistoryUpdate = GlobalWidgetHistoryUpdate; +var _default = exports["default"] = GlobalWidgetHistoryUpdate; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/hooks/ui/index.js": +/*!*******************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/hooks/ui/index.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "GlobalWidgetHistoryUpdate", ({ + enumerable: true, + get: function () { + return _globalWidgetHistoryUpdate.GlobalWidgetHistoryUpdate; + } +})); +var _globalWidgetHistoryUpdate = __webpack_require__(/*! ./document/elements/set-settings/global-widget-history-update */ "../modules/global-widget/assets/js/editor/hooks/ui/document/elements/set-settings/global-widget-history-update.js"); + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/module.js": +/*!***********************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/module.js ***! + \***********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _component = _interopRequireDefault(__webpack_require__(/*! ./component */ "../modules/global-widget/assets/js/editor/component.js")); +class Module extends elementorModules.editor.utils.Module { + panelWidgets = new Backbone.Collection(); + addGlobalWidget(templateId, templateData) { + return this.panelWidgets.add(this.createGlobalModel(templateId, templateData)); + } + createGlobalModel(templateId, templateData) { + templateData = Object.assign({}, templateData, { + id: templateId, + categories: [], + icon: elementor.widgetsCache[templateData.widgetType].icon, + widgetType: templateData.widgetType, + custom: { + templateID: templateId + } + }); + const elementModel = new elementor.modules.elements.models.Element(templateData); + elementModel.set('id', templateId); + return elementModel; + } + setWidgetType() { + elementor.hooks.addFilter('element/view', function (DefaultView, model) { + if (model.get('templateID')) { + return (__webpack_require__(/*! ./widget/view */ "../modules/global-widget/assets/js/editor/widget/view.js")["default"]); + } + return DefaultView; + }); + elementor.hooks.addFilter('element/model', function (DefaultModel, attrs) { + if (attrs.templateID) { + return (__webpack_require__(/*! ./widget/model */ "../modules/global-widget/assets/js/editor/widget/model.js")["default"]); + } + return DefaultModel; + }); + } + registerTemplateType() { + elementor.templates.registerTemplateType('widget', { + showInLibrary: false, + saveDialog: { + title: __('Save your widget as a global widget', 'elementor-pro'), + description: __('You\'ll be able to add this global widget to multiple areas on your site, and edit it from one single place.', 'elementor-pro') + }, + prepareSavedData(data) { + data.widgetType = data.content[0].widgetType; + return data; + }, + ajaxParams: { + success: this.onWidgetTemplateSaved.bind(this) + } + }); + } + addPanelPage() { + elementor.getPanelView().addPage('globalWidget', { + view: __webpack_require__(/*! ./views/panel-page */ "../modules/global-widget/assets/js/editor/views/panel-page.js") + }); + } + + /** + * @param {string} id - The ID. + * @deprecated since 3.5.0, use `$e.data.getCache( `document/global/global-widget/templates/${ id }` )` instead. + */ + getGlobalModels(id) { + elementorCommon.helpers.softDeprecated('elementorPro.modules.globalWidget.getGlobalModels( id )', '3.5.0', '$e.data.getCache( `document/global/global-widget/templates/${ id }` )'); + return $e.data.getCache(this.component, `document/global/global-widget/templates/${id}`); + } + + /** + * @deprecated since 3.5.0, use `$e.internal( 'document/global/save-templates' )` instead. + */ + saveTemplates() { + elementorCommon.helpers.softDeprecated('elementorPro.modules.globalWidget.saveTemplates()', '3.5.0', "$e.internal( 'document/global/save-templates' )"); + $e.internal('document/global/save-templates'); + } + + /** + * @param {*} globalModel - global model. + * @param {Function} callback - A callback function. + * @deprecated since 3.5.0, use `$e.data.get( 'document/global/templates' )` instead. + */ + requestGlobalModelSettings(globalModel, callback) { + elementorCommon.helpers.softDeprecated('elementorPro.modules.globalWidget.requestGlobalModelSettings()', '3.5.0', "$e.data.get( 'document/global/templates' )"); + $e.data.get('document/global/templates', { + ids: globalModel.id + }).then(data => { + callback(data); + }); + } + setWidgetContextMenuSaveAction() { + elementor.hooks.addFilter('elements/widget/contextMenuGroups', (groups, widget) => { + const saveGroup = _.findWhere(groups, { + name: 'save' + }); + if (!saveGroup) { + return groups; + } + const saveAction = _.findWhere(saveGroup.actions, { + name: 'save' + }); + if (elementorPro.config.should_show_promotion) { + const iconLink = '' + ''; + saveAction.shortcut = jQuery(iconLink); + saveAction.isEnabled = () => false; + delete saveAction.callback; + return groups; + } + saveAction.callback = widget.save.bind(widget); + delete saveAction.shortcut; + return groups; + }); + } + filterRegionViews(regionViews) { + if (elementorPro.config.should_show_promotion) { + _.extend(regionViews.global, { + view: __webpack_require__(/*! ./views/promotion */ "../modules/global-widget/assets/js/editor/views/promotion.js"), + options: {} + }); + return regionViews; + } + _.extend(regionViews.global, { + view: __webpack_require__(/*! ./views/global-templates-view */ "../modules/global-widget/assets/js/editor/views/global-templates-view.js"), + options: { + collection: this.panelWidgets + } + }); + return regionViews; + } + onElementorInit() { + elementor.on('panel:init', () => { + elementor.hooks.addFilter('panel/elements/regionViews', this.filterRegionViews.bind(this)); + }); + this.registerTemplateType(); + this.setWidgetContextMenuSaveAction(); + this.setWidgetType(); + } + onElementorInitComponents() { + $e.components.register(new _component.default()); + $e.data.get('document/global/templates', {}, { + refresh: true + }); + } + onElementorPreviewLoaded(isFirst) { + if (!isFirst) { + return; + } + this.addPanelPage(); + $e.routes.register('panel/editor', 'global', args => { + elementor.getPanelView().setPage('globalWidget', 'Global Editing', { + editedView: args.view + }); + }); + } + onWidgetTemplateSaved(data) { + elementor.templates.layout.hideModal(); + const container = elementor.getContainer(elementor.templates.layout.modalContent.currentView.model.id); + $e.run('document/global/link', { + container, + data + }); + } +} +exports["default"] = Module; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/views/global-templates-view.js": +/*!********************************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/views/global-templates-view.js ***! + \********************************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +module.exports = elementor.modules.layouts.panel.pages.elements.views.Elements.extend({ + id: 'elementor-global-templates', + getEmptyView() { + if (this.collection.length) { + return null; + } + return __webpack_require__(/*! ./no-templates */ "../modules/global-widget/assets/js/editor/views/no-templates.js"); + }, + onFilterEmpty() {} +}); + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/views/no-templates.js": +/*!***********************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/views/no-templates.js ***! + \***********************************************************************/ +/***/ ((module) => { + +"use strict"; + + +var GlobalWidgetsView = elementor.modules.layouts.panel.pages.elements.views.Global; +module.exports = GlobalWidgetsView.extend({ + template: '#tmpl-elementor-panel-global-widget-no-templates', + id: 'elementor-panel-global-widget-no-templates', + className: 'elementor-nerd-box elementor-panel-nerd-box e-responsive-panel-stretch' +}); + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/views/panel-page.js": +/*!*********************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/views/panel-page.js ***! + \*********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +module.exports = Marionette.ItemView.extend({ + id: 'elementor-panel-global-widget', + template: '#tmpl-elementor-panel-global-widget', + ui: { + editButton: '#elementor-global-widget-locked-edit .elementor-button', + unlinkButton: '#elementor-global-widget-locked-unlink .elementor-button', + loading: '#elementor-global-widget-loading' + }, + events: { + 'click @ui.editButton': 'onEditButtonClick', + 'click @ui.unlinkButton': 'onUnlinkButtonClick' + }, + initialize() { + this.initUnlinkDialog(); + }, + buildUnlinkDialog() { + var self = this; + return elementorCommon.dialogsManager.createWidget('confirm', { + id: 'elementor-global-widget-unlink-dialog', + headerMessage: __('Unlink Widget', 'elementor-pro'), + message: __('This will make the widget stop being global. It\'ll be reverted into being just a regular widget.', 'elementor-pro'), + position: { + my: 'center center', + at: 'center center' + }, + strings: { + confirm: __('Unlink', 'elementor-pro'), + cancel: __('Cancel', 'elementor-pro') + }, + onConfirm() { + self.getOption('editedView').unlink(); + } + }); + }, + initUnlinkDialog() { + var dialog; + this.getUnlinkDialog = function () { + if (!dialog) { + dialog = this.buildUnlinkDialog(); + } + return dialog; + }; + }, + editGlobalModel() { + var editedView = this.getOption('editedView'); + $e.run('panel/editor/open', { + model: editedView.getEditModel(), + view: editedView + }); + }, + onEditButtonClick() { + this.editGlobalModel(); + }, + onUnlinkButtonClick() { + this.getUnlinkDialog().show(); + } +}); + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/views/promotion.js": +/*!********************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/views/promotion.js ***! + \********************************************************************/ +/***/ ((module) => { + +"use strict"; + + +var GlobalWidgetsView = elementor.modules.layouts.panel.pages.elements.views.Global; +module.exports = GlobalWidgetsView.extend({ + template: '#tmpl-elementor-promotion', + id: 'tmpl-elementor-promotion', + className: 'elementor-nerd-box elementor-panel-nerd-box e-responsive-panel-stretch' +}); + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/widget/model.js": +/*!*****************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/widget/model.js ***! + \*****************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +const ElementModel = elementor.modules.elements.models.Element; +class Model extends ElementModel { + initSettings() { + // If global widget is created, the settings should come from recent template. + // The widget that's hold the panel may not have the recent data, the template can be changed during the editing. + if ($e.commands.is('document/elements/create')) { + return this.initSettingsFromTemplate(); + } + super.initSettings(); + } + initEditSettings() { + super.initEditSettings(); + + // Set default edit tab. + this.get('editSettings').set('editTab', 'global'); + } + initSettingsFromTemplate() { + const id = this.get('templateID'), + component = $e.components.get('document/global'), + data = $e.data.getCache(component, `document/global/global-widget/templates/${id}`) || this.attributes, + elementModel = elementorPro.modules.globalWidget.createGlobalModel(id, data); + this.set('settings', elementModel.get('settings')); + elementorFrontend.config.elements.data[this.cid] = this.get('settings'); + } +} +exports["default"] = Model; + +/***/ }), + +/***/ "../modules/global-widget/assets/js/editor/widget/view.js": +/*!****************************************************************!*\ + !*** ../modules/global-widget/assets/js/editor/widget/view.js ***! + \****************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +const WidgetView = elementor.modules.elements.views.Widget; +class View extends WidgetView { + className() { + return super.className() + ' elementor-global-widget elementor-global-' + this.model.get('templateID'); + } + addInlineEditingAttributes() { + // See `this.removeInlineAddingAttributes` for more information. + } + unlink() { + $e.run('document/global/unlink', { + container: this.getContainer() + }); + } + onEditRequest() { + $e.route('panel/editor/global', { + view: this + }); + } + getContextMenuGroups() { + // Remove 'Save as global' for global widget view. + return super.getContextMenuGroups().filter(group => 'save' !== group.name); + } + getContainer() { + if (this.container) { + return this.container; + } + const container = super.getContainer(); + container.label = container.label + ' (' + __('global', 'elementor-pro') + ')'; + return container; + } + render() { + super.render(); + setTimeout(this.removeInlineAddingAttributes.bind(this)); + } + + /** + * The issue is complex: + * 1. There is a mechanism in the editor which responsible for adding inline the method below: `addInlineEditingAttributes`. + * 2. There is a mechanism in the backend that adds inline attributes for each widget most of the time. + * its effect also the Global-Widget itself, in two ways: + * 1. global-widget instance is calling to `$this->get_original_element_instance()->render_content();`. + * It means that the mechanism in the backend with adds the inline attributes will be triggered. + * 2. each time you 'leave the editing mode' for most of the widgets it triggers `renderRemoteServer()`, + * which sends a request for `remoteRendering` for 'non-global widget' (the server doesn't know that it + * was linked to a template), that will trigger the original widget without knowing it's a part of the + * global mechanism. + * eventually it will trigger the logic of the backend for adding the inline attributes. + */ + removeInlineAddingAttributes() { + const globalWidgetElementDom = this.el.querySelector('.elementor-inline-editing'); + if (globalWidgetElementDom) { + globalWidgetElementDom.classList.remove('elementor-inline-editing'); + } + } +} +exports["default"] = View; + +/***/ }), + +/***/ "../modules/library/assets/js/editor.js": +/*!**********************************************!*\ + !*** ../modules/library/assets/js/editor.js ***! + \**********************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + onElementorPreviewLoaded() { + var EditButton = __webpack_require__(/*! ./editor/edit-button */ "../modules/library/assets/js/editor/edit-button.js"); + this.editButton = new EditButton(); + } +}); + +/***/ }), + +/***/ "../modules/library/assets/js/editor/edit-button.js": +/*!**********************************************************!*\ + !*** ../modules/library/assets/js/editor/edit-button.js ***! + \**********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +module.exports = function () { + var self = this; + self.onPanelShow = function (panel) { + var model = panel.content.currentView.collection.findWhere({ + name: 'template_id' + }); + self.templateIdView = panel.content.currentView.children.findByModelCid(model.cid); + + // Change Edit link on render & on change template. + self.templateIdView.elementSettingsModel.on('change', self.onTemplateIdChange); + self.templateIdView.on('render', self.onTemplateIdChange); + }; + self.onTemplateIdChange = function () { + var templateID = self.templateIdView.elementSettingsModel.get('template_id'), + $editButton = self.templateIdView.$el.find('.elementor-edit-template'); + if (!templateID) { + $editButton.remove(); + return; + } + var editUrl = ElementorConfig.home_url + '?p=' + templateID + '&elementor'; + if ($editButton.length) { + $editButton.prop('href', editUrl); + } else { + $editButton = jQuery('', { + target: '_blank', + class: 'elementor-button elementor-edit-template', + href: editUrl, + html: ' ' + __('Edit Template', 'elementor-pro') + }); + self.templateIdView.$el.find('.elementor-control-input-wrapper').after($editButton); + } + }; + self.init = function () { + elementor.hooks.addAction('panel/open_editor/widget/template', self.onPanelShow); + }; + self.init(); +}; + +/***/ }), + +/***/ "../modules/loop-builder/assets/js/editor/behavior.js": +/*!************************************************************!*\ + !*** ../modules/loop-builder/assets/js/editor/behavior.js ***! + \************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +class LoopBuilderBehavior extends Marionette.Behavior { + ui() { + return { + postSourceControlSelector: '[data-setting="post_taxonomy_query_post_type"]', + productSourceControlSelector: '[data-setting="product_taxonomy_query_post_type"]' + }; + } + events() { + return { + 'change @ui.postSourceControlSelector': 'onApplySourceChange', + 'change @ui.productSourceControlSelector': 'onApplySourceChange' + }; + } + onApplySourceChange(event) { + const sourceType = event.target?.value || this.getDefaultSourceType(); + this.getOption('updateTaxonomyTabsIdControls')(sourceType, true); + } + onRender() { + const postType = this.getOption('getSourceControlValue')(); + this.getOption('updateTaxonomyTabsIdControls')(postType); + } + getDefaultSourceType() { + const skinType = this.getOption('getSkinType')(); + return this.getOption('getDefaultSourceType')(skinType); + } +} +exports["default"] = LoopBuilderBehavior; + +/***/ }), + +/***/ "../modules/loop-builder/assets/js/editor/component.js": +/*!*************************************************************!*\ + !*** ../modules/loop-builder/assets/js/editor/component.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var hooks = _interopRequireWildcard(__webpack_require__(/*! ./hooks/ */ "../modules/loop-builder/assets/js/editor/hooks/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class LoopBuilderComponent extends $e.modules.ComponentBase { + getNamespace() { + return 'document/loop'; + } + defaultHooks() { + return this.importHooks(hooks); + } +} +exports["default"] = LoopBuilderComponent; + +/***/ }), + +/***/ "../modules/loop-builder/assets/js/editor/hooks/index.js": +/*!***************************************************************!*\ + !*** ../modules/loop-builder/assets/js/editor/hooks/index.js ***! + \***************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "LoopBuilderAddLibraryTab", ({ + enumerable: true, + get: function () { + return _addLoopBuildersTab.LoopBuilderAddLibraryTab; + } +})); +Object.defineProperty(exports, "LoopBuilderRemoveLibraryTab", ({ + enumerable: true, + get: function () { + return _removeLoopBuildersTab.LoopBuilderRemoveLibraryTab; + } +})); +var _addLoopBuildersTab = __webpack_require__(/*! ./ui/editor/documents/open/add-loop-builders-tab */ "../modules/loop-builder/assets/js/editor/hooks/ui/editor/documents/open/add-loop-builders-tab.js"); +var _removeLoopBuildersTab = __webpack_require__(/*! ./ui/editor/documents/close/remove-loop-builders-tab */ "../modules/loop-builder/assets/js/editor/hooks/ui/editor/documents/close/remove-loop-builders-tab.js"); + +/***/ }), + +/***/ "../modules/loop-builder/assets/js/editor/hooks/ui/editor/documents/close/remove-loop-builders-tab.js": +/*!************************************************************************************************************!*\ + !*** ../modules/loop-builder/assets/js/editor/hooks/ui/editor/documents/close/remove-loop-builders-tab.js ***! + \************************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.LoopBuilderRemoveLibraryTab = void 0; +class LoopBuilderRemoveLibraryTab extends $e.modules.hookUI.After { + getCommand() { + return 'editor/documents/unload'; + } + getId() { + return 'elementor-loop-items-remove-library-tab'; + } + getConditions(args) { + const { + document + } = args; + return 'loop-item' === document?.config?.type; + } + apply() { + $e.components.get('library').removeTab('templates/loop-items'); + $e.components.get('library').addTab('templates/blocks'); + $e.components.get('library').addTab('templates/pages'); + } +} +exports.LoopBuilderRemoveLibraryTab = LoopBuilderRemoveLibraryTab; +var _default = exports["default"] = LoopBuilderRemoveLibraryTab; + +/***/ }), + +/***/ "../modules/loop-builder/assets/js/editor/hooks/ui/editor/documents/open/add-loop-builders-tab.js": +/*!********************************************************************************************************!*\ + !*** ../modules/loop-builder/assets/js/editor/hooks/ui/editor/documents/open/add-loop-builders-tab.js ***! + \********************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.LoopBuilderAddLibraryTab = void 0; +class LoopBuilderAddLibraryTab extends $e.modules.hookUI.After { + getCommand() { + return 'editor/documents/open'; + } + getId() { + return 'elementor-loop-items-add-library-tab'; + } + getConditions(args) { + const document = elementor.documents?.get(args.id); + return 'loop-item' === document?.config?.type; + } + apply() { + $e.components.get('library').addTab('templates/loop-items', { + title: __('Loop', 'elementor'), + filter: { + source: 'remote', + type: 'lb', + subtype: elementor.config.document.settings.settings.source + } + }, 0); + $e.components.get('library').removeTab('templates/blocks'); + $e.components.get('library').removeTab('templates/pages'); + } +} +exports.LoopBuilderAddLibraryTab = LoopBuilderAddLibraryTab; +var _default = exports["default"] = LoopBuilderAddLibraryTab; + +/***/ }), + +/***/ "../modules/loop-builder/assets/js/editor/module.js": +/*!**********************************************************!*\ + !*** ../modules/loop-builder/assets/js/editor/module.js ***! + \**********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +var _documentHandle = _interopRequireWildcard(__webpack_require__(/*! elementor-pro/preview/utils/document-handle */ "../assets/dev/js/preview/utils/document-handle.js")); +var _component = _interopRequireDefault(__webpack_require__(/*! ./component */ "../modules/loop-builder/assets/js/editor/component.js")); +var _behavior = _interopRequireDefault(__webpack_require__(/*! ./behavior */ "../modules/loop-builder/assets/js/editor/behavior.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class loopBuilderModule extends elementorModules.editor.utils.Module { + taxonomyQueryOptions = ['post_taxonomy', 'product_taxonomy']; + onElementorFrontendInit() { + elementor.hooks.addFilter('controls/base/behaviors', this.registerControlBehavior); + elementorFrontend.elements.$body.on('click', '.e-loop-empty-view__box-cta', () => { + this.createTemplate(); + }); + this.createDocumentSaveHandles(); + elementor.on('document:loaded', this.createDocumentSaveHandles.bind(this)); + } + registerControlBehavior = (behaviors = {}, view) => { + const taxonomyQueryOptions = ['post_taxonomy_query_post_type', 'product_taxonomy_query_post_type']; + if (!taxonomyQueryOptions.includes(view.options.model.get('name'))) { + return behaviors; + } + behaviors.loopBuilder = { + behaviorClass: _behavior.default, + getSourceControlValue: this.getSourceControlValue, + updateTaxonomyTabsIdControls: this.updateTaxonomyTabsIdControls + }; + return behaviors; + }; + createTemplate() { + setTimeout(() => { + elementor.getPanelView().getCurrentPageView().activateSection('section_layout')._renderChildren(); + this.getEditorControlView('template_id').createTemplate(); + }); + } + createDocumentSaveHandles() { + Object.entries(elementorFrontend.config?.elements?.data).forEach(([cid, element]) => { + const elementData = elementor.getElementData(element); + if (!elementData?.is_loop) { + return; + } + const templateId = element.attributes.template_id; + if (!templateId) { + return; + } + const widgetSelector = `.elementor-element[data-model-cid="${cid}"]`, + editHandleSelector = `[data-elementor-type="loop-item"].elementor-${templateId}`, + editHandleElement = elementorFrontend.elements.$body.find(`${widgetSelector} ${editHandleSelector}`).first()[0]; + if (editHandleElement) { + (0, _documentHandle.default)({ + element: editHandleElement, + id: 0, + title: '& Back' + }, _documentHandle.SAVE_CONTEXT, null, '.elementor-' + elementor.config.initial_document.id); + } + }); + } + onElementorLoaded() { + elementor.on('document:loaded', this.onDocumentLoaded.bind(this)); + elementor.on('document:unload', this.onDocumentUnloaded.bind(this)); + this.component = $e.components.register(new _component.default({ + manager: this + })); + } + onDocumentLoaded = document => { + if (!document.config.theme_builder) { + return; + } + elementor.channels.editor.on('elementorLoopBuilder:ApplySourceChange', this.onApplySourceChange); + }; + onDocumentUnloaded = document => { + if (!document.config.theme_builder) { + return; + } + elementor.channels.editor.off('elementorLoopBuilder:ApplySourceChange', this.onApplySourceChange); + }; + onApplySourceChange = () => { + this.saveAndRefresh().then(() => { + location.reload(); + }); + }; + async saveAndRefresh() { + await $e.run('document/save/update', { + force: true + }); + } + getCtaStyles = () => { + const ctaStyle = document.createElement('link'); + ctaStyle.setAttribute('rel', 'stylesheet'); + ctaStyle.setAttribute('href', `${elementorAppProConfig.baseUrl}/assets/css/loop-grid-cta.css`); + return ctaStyle; + }; + getCtaContent = widgetName => { + const ctaContent = document.createElement('div'); + ctaContent.classList.add('e-loop-empty-view__container', 'elementor-grid', widgetName); + ctaContent.innerHTML = Marionette.Renderer.render('#tmpl-' + widgetName + '-cta'); + return ctaContent; + }; + getSourceControlValue = () => { + const skinType = this.getSkinType(), + controlView = this.getEditorControlView(`${skinType}_query_post_type`); + if (!controlView) { + return skinType.includes('product') ? 'product_cat' : 'category'; + } + return controlView.getControlValue(); + }; + getSkinType = () => { + const sectionLayout = this.getEditorControlView('section_layout'); + return sectionLayout.options.container.settings.get('_skin'); + }; + getTemplateType = templateKey => { + return templateKey.split('_')[0]; + }; + onApplySkinChange = () => { + const skinType = this.getSkinType(); + if (!this.taxonomyQueryOptions.includes(skinType)) { + return; + } + const postType = this.getDefaultSourceType(skinType); + this.updateTaxonomyTabsIdControls(postType, true); + }; + getDefaultSourceType = skinType => { + const defaultSourceTypes = { + post: 'post', + product: 'product', + post_taxonomy: 'category', + product_taxonomy: 'product_cat' + }; + return defaultSourceTypes[skinType]; + }; + updateTaxonomyTabsIdControls = (postType, shouldResetControlValues = false) => { + const skinType = this.getSkinType(); + if (!this.taxonomyQueryOptions.includes(skinType)) { + return; + } + const querySectionView = elementorPro.modules.loopBuilder.getEditorControlView('section_query'), + includeIds = querySectionView.model.collection.findWhere({ + name: `${skinType}_posts_ids` + }), + excludeIds = querySectionView.model.collection.findWhere({ + name: `${skinType}_exclude_ids` + }); + [includeIds, excludeIds].forEach(control => { + const controlView = elementor.getPanelView()?.getCurrentPageView()?.children?.findByModel(control); + this.updateControlQuery({ + control, + controlView, + postType, + shouldResetControlValues + }); + }); + }; + updateControlQuery = ({ + control, + controlView, + postType, + shouldResetControlValues + }) => { + control.set({ + autocomplete: { + object: 'tax', + query: { + taxonomy: postType + } + } + }); + if (controlView && shouldResetControlValues) { + controlView.setValue([]); + controlView.applySavedValue(); + } + }; +} +module.exports = loopBuilderModule; + +/***/ }), + +/***/ "../modules/motion-fx/assets/js/editor/editor.js": +/*!*******************************************************!*\ + !*** ../modules/motion-fx/assets/js/editor/editor.js ***! + \*******************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +class _default extends elementorModules.editor.utils.Module { + onElementorInit() { + elementor.on('navigator:init', this.onNavigatorInit.bind(this)); + } + onNavigatorInit() { + elementor.navigator.indicators.motionFX = { + icon: 'flash', + title: __('Motion Effects', 'elementor-pro'), + settingKeys: ['motion_fx_motion_fx_scrolling', 'motion_fx_motion_fx_mouse', 'background_motion_fx_motion_fx_scrolling', 'background_motion_fx_motion_fx_mouse'], + section: 'section_effects' + }; + } +} +exports["default"] = _default; + +/***/ }), + +/***/ "../modules/notes/assets/js/notes-context-menu.js": +/*!********************************************************!*\ + !*** ../modules/notes/assets/js/notes-context-menu.js ***! + \********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.notesContextMenu = exports["default"] = void 0; +class notesContextMenu { + constructor() { + const elTypes = ['widget', 'section', 'column', 'container']; + elTypes.forEach(type => { + elementor.hooks.addFilter(`elements/${type}/contextMenuGroups`, this.notesContextMenuAddGroup); + }); + } + + /** + * Enable the 'Notes' context menu item + * + * @since 3.8.0 + * + * @param {Array} groups + * @return {Array} The updated groups. + */ + notesContextMenuAddGroup(groups) { + const notesGroup = _.findWhere(groups, { + name: 'notes' + }), + notesGroupIndex = groups.indexOf(notesGroup), + notesActionItem = { + name: 'open_notes', + title: __('Notes', 'elementor-pro'), + shortcut: '⇧+C', + isEnabled: () => true, + callback: () => $e.route('notes') + }; + if (elementorPro.config.should_show_promotion) { + const iconLink = '' + ''; + notesActionItem.shortcut = jQuery(iconLink); + notesActionItem.isEnabled = () => false; + delete notesActionItem.callback; + } + + // Create the Notes group if it doesn't exist + if (-1 === notesGroupIndex) { + const deleteGroup = _.findWhere(groups, { + name: 'delete' + }), + deleteGroupIndex = groups.indexOf(deleteGroup), + newGroupPosition = -1 !== deleteGroupIndex ? deleteGroupIndex : groups.length; + groups.splice(newGroupPosition, 0, { + name: 'notes', + actions: [notesActionItem] + }); + return groups; + } + const openNotesAction = _.findWhere(notesGroup.actions, { + name: 'open_notes' + }), + openNotesActionIndex = notesGroup.actions.indexOf(openNotesAction); + groups[notesGroupIndex].actions[openNotesActionIndex] = notesActionItem; + return groups; + } +} +exports.notesContextMenu = notesContextMenu; +var _default = exports["default"] = notesContextMenu; + +/***/ }), + +/***/ "../modules/page-transitions/assets/js/editor/commands/animate.js": +/*!************************************************************************!*\ + !*** ../modules/page-transitions/assets/js/editor/commands/animate.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.Animate = void 0; +class Animate extends $e.modules.CommandBase { + /** + * Animate the Page Transition element. + * + * @return {void} + */ + apply() { + const pageTransition = elementor.$previewContents[0].querySelector('e-page-transition'); + if (!pageTransition) { + return; + } + pageTransition.animate(); + } +} +exports.Animate = Animate; +var _default = exports["default"] = Animate; + +/***/ }), + +/***/ "../modules/page-transitions/assets/js/editor/commands/index.js": +/*!**********************************************************************!*\ + !*** ../modules/page-transitions/assets/js/editor/commands/index.js ***! + \**********************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "Animate", ({ + enumerable: true, + get: function () { + return _animate.Animate; + } +})); +var _animate = __webpack_require__(/*! ./animate */ "../modules/page-transitions/assets/js/editor/commands/animate.js"); + +/***/ }), + +/***/ "../modules/page-transitions/assets/js/editor/component.js": +/*!*****************************************************************!*\ + !*** ../modules/page-transitions/assets/js/editor/component.js ***! + \*****************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var commands = _interopRequireWildcard(__webpack_require__(/*! ./commands/ */ "../modules/page-transitions/assets/js/editor/commands/index.js")); +var hooks = _interopRequireWildcard(__webpack_require__(/*! ./hooks/ */ "../modules/page-transitions/assets/js/editor/hooks/index.js")); +var _pageTransitionPreview = _interopRequireDefault(__webpack_require__(/*! ./hooks/routes/page-transition-preview */ "../modules/page-transitions/assets/js/editor/hooks/routes/page-transition-preview.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class Component extends $e.modules.ComponentBase { + /** + * Initialize the component. + * + * @return {void} + */ + constructor() { + super(); + this.routesHooks = {}; + this.initRouteHooks(); + } + + /** + * Add route hooks & listen to route changes. + * + * @return {void} + */ + initRouteHooks() { + // TODO: Remove when route hooks are available. + this.routesHooks.pageTransitionPreview = new _pageTransitionPreview.default(); + $e.routes.on('run:after', (component, route) => { + this.routesHooks.pageTransitionPreview.run(component, route); + }); + } + + /** + * Get the component namespace. + * + * @return {string} - Component namespace. + */ + getNamespace() { + return 'page-transitions'; + } + + /** + * Get the component hooks. + * + * @return {Object} - Component hooks. + */ + defaultHooks() { + return this.importHooks(hooks); + } + + /** + * Get the component commands. + * + * @return {Object} - Component commands. + */ + defaultCommands() { + return this.importCommands(commands); + } +} +exports["default"] = Component; + +/***/ }), + +/***/ "../modules/page-transitions/assets/js/editor/hooks/data/animate-page-transition.js": +/*!******************************************************************************************!*\ + !*** ../modules/page-transitions/assets/js/editor/hooks/data/animate-page-transition.js ***! + \******************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.AnimatePageTransition = void 0; +/** + * Data hook that animates the Page Transition component when entrance / exit animations are changed. + */ +class AnimatePageTransition extends $e.modules.hookData.After { + // Page Transitions settings prefix. + prefix = 'settings_page_transitions_'; + + // Controls that the hook should listen to. + settings = ['entrance_animation', 'exit_animation']; + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 'animate-page-transitions--document/elements/settings'; + } + getContainerType() { + return 'document'; + } + getConditions(args) { + // Execute only for specific settings. + return Object.keys(args.settings).some(key => { + key = key.replace(this.prefix, ''); + return this.settings.includes(key); + }); + } + apply() { + $e.run('page-transitions/animate'); + } +} +exports.AnimatePageTransition = AnimatePageTransition; +var _default = exports["default"] = AnimatePageTransition; + +/***/ }), + +/***/ "../modules/page-transitions/assets/js/editor/hooks/data/index.js": +/*!************************************************************************!*\ + !*** ../modules/page-transitions/assets/js/editor/hooks/data/index.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "AnimatePageTransition", ({ + enumerable: true, + get: function () { + return _animatePageTransition.AnimatePageTransition; + } +})); +Object.defineProperty(exports, "ReRenderPageTransition", ({ + enumerable: true, + get: function () { + return _reRenderPageTransition.ReRenderPageTransition; + } +})); +var _animatePageTransition = __webpack_require__(/*! ./animate-page-transition */ "../modules/page-transitions/assets/js/editor/hooks/data/animate-page-transition.js"); +var _reRenderPageTransition = __webpack_require__(/*! ./re-render-page-transition */ "../modules/page-transitions/assets/js/editor/hooks/data/re-render-page-transition.js"); + +/***/ }), + +/***/ "../modules/page-transitions/assets/js/editor/hooks/data/re-render-page-transition.js": +/*!********************************************************************************************!*\ + !*** ../modules/page-transitions/assets/js/editor/hooks/data/re-render-page-transition.js ***! + \********************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.ReRenderPageTransition = void 0; +var _utils = __webpack_require__(/*! ../utils */ "../modules/page-transitions/assets/js/editor/hooks/utils.js"); +/** + * Data hook that passes the new settings from the panel as attributes to the Page Transition component, in order to re-render it. + */ +class ReRenderPageTransition extends $e.modules.hookData.After { + // Page Transitions settings prefix. + prefix = 'settings_page_transitions_'; + + // Controls that the hook should listen to. + settings = ['entrance_animation', 'preloader_type', 'preloader_icon', 'preloader_image', 'preloader_animation_type']; + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 're-render-page-transitions--document/elements/settings'; + } + getContainerType() { + return 'document'; + } + getConditions(args) { + // Execute only for specific settings. + return Object.keys(args.settings).some(key => { + key = key.replace(this.prefix, ''); + return this.settings.includes(key); + }); + } + apply(args) { + (0, _utils.renderPageTransition)(args.container); + } +} +exports.ReRenderPageTransition = ReRenderPageTransition; +var _default = exports["default"] = ReRenderPageTransition; + +/***/ }), + +/***/ "../modules/page-transitions/assets/js/editor/hooks/index.js": +/*!*******************************************************************!*\ + !*** ../modules/page-transitions/assets/js/editor/hooks/index.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +var _data = __webpack_require__(/*! ./data */ "../modules/page-transitions/assets/js/editor/hooks/data/index.js"); +Object.keys(_data).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _data[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _data[key]; + } + }); +}); + +/***/ }), + +/***/ "../modules/page-transitions/assets/js/editor/hooks/routes/page-transition-preview.js": +/*!********************************************************************************************!*\ + !*** ../modules/page-transitions/assets/js/editor/hooks/routes/page-transition-preview.js ***! + \********************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _utils = __webpack_require__(/*! ../utils */ "../modules/page-transitions/assets/js/editor/hooks/utils.js"); +/** + * A route hook that listens to route changes in the panel and change the preview mode for + * the Page Transitions feature when navigating to the `Site Settings -> Page Transitions` tab. + * + * TODO: Convert to `$e.modules.hookRoute.After` when available. + */ +class PageTransitionPreview { + /** + * Run the hook. + * + * @param {Object} component + * @param {string} route + * + * @return {void} + */ + run(component, route) { + if ('panel/global/settings-page-transitions' === route) { + (0, _utils.renderPageTransition)(elementor.documents.getCurrent().container); + this.togglePageTransitionPreview(true); + } else { + this.togglePageTransitionPreview(false); + } + } + + /** + * Toggle the Page Transition state to show or hide preview. + * + * @param {boolean} on + * + * @return {void} + */ + togglePageTransitionPreview(on = true) { + const className = 'e-page-transition--preview', + pageTransition = elementor.$previewContents[0].body.querySelector('e-page-transition'); + if (!pageTransition) { + return; + } + pageTransition.classList.toggle(className, on); + } +} +exports["default"] = PageTransitionPreview; + +/***/ }), + +/***/ "../modules/page-transitions/assets/js/editor/hooks/utils.js": +/*!*******************************************************************!*\ + !*** ../modules/page-transitions/assets/js/editor/hooks/utils.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.getPageTransitionSettings = getPageTransitionSettings; +exports.renderPageTransition = renderPageTransition; +const prefix = 'settings_page_transitions_'; + +/** + * Get only the Page Transitions controls' values from a Container. + * + * @param {Object} container + * + * @return {Object} - Controls' values. + */ +function getPageTransitionSettings(container) { + // Filter only the Page Transitions controls which doesn't change CSS values. + // (since they shouldn't affect the render) + const controls = Object.entries(container.settings.getActiveControls()).filter(([key, control]) => { + return key.startsWith(prefix) && !control.selectors; + }); + const settings = {}; + controls.forEach(([control]) => { + settings[control] = container.settings.get(control); + }); + return settings; +} + +/** + * Live render the Page Transition element, based on settings from the user. + * + * @param {Object} container - The container to get the settings from. + * + * @return {void} + */ +function renderPageTransition(container) { + let pageTransition = elementor.$previewContents[0].querySelector('e-page-transition'); + const hasEntranceAnimation = !!container.settings.get(`${prefix}entrance_animation`), + hasPreloader = !!container.settings.get(`${prefix}preloader_type`), + shouldRender = hasEntranceAnimation || hasPreloader; + + // Create the Page Transition element if it doesn't exist. + if (!pageTransition) { + pageTransition = document.createElement('e-page-transition'); + pageTransition.classList.add('e-page-transition--preview'); + elementor.$previewContents[0].body.append(pageTransition); + } + + // Disable the Page Transition if needed. + pageTransition.toggleAttribute('disabled', !shouldRender); + const settings = getPageTransitionSettings(container); + + // Iterate over the settings and set them as attributes. + Object.entries(settings).forEach(([key, value]) => { + key = key.replace(prefix, ''); + key = key.replaceAll('_', '-'); + if (!value) { + pageTransition.removeAttribute(key); + return; + } + if ('string' === typeof value) { + pageTransition.setAttribute(key, value); + return; + } + + // For object values (e.g. image control). + Object.entries(value).forEach(([subKey, subValue]) => { + let newKey = key; + + // Append the sub key only if it's not `value` (e.g. `url`), in order to avoid weird + // attributes like `preloader-icon-value`. + if (subKey !== 'value') { + newKey = `${key}-${subKey}`; + } + pageTransition.setAttribute(newKey, subValue); + }); + }); +} + +/***/ }), + +/***/ "../modules/page-transitions/assets/js/editor/module.js": +/*!**************************************************************!*\ + !*** ../modules/page-transitions/assets/js/editor/module.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _component = _interopRequireDefault(__webpack_require__(/*! ./component */ "../modules/page-transitions/assets/js/editor/component.js")); +class _default extends elementorModules.editor.utils.Module { + /** + * Register the component & bind events on init. + * + * @return {void} + */ + onInit() { + $e.components.register(new _component.default()); + this.bindEvents(); + } + + /** + * Listen to Page Transition event. + * + * @return {void} + */ + bindEvents() { + // Make sure that `window.elementor` is initialized. + // TODO: Find a better solution. It's caused because of the dynamic import. + if (window.elementor) { + this.onAnimateButtonClick(); + return; + } + jQuery(window).on('elementor:init', () => this.onAnimateButtonClick()); + } + + /** + * Listen to `animate` button click event and animate the Page Transition. + * + * @return {void} + */ + onAnimateButtonClick() { + elementor.channels.editor.on('elementorPageTransitions:animate', () => { + $e.run('page-transitions/animate'); + }); + } +} +exports["default"] = _default; + +/***/ }), + +/***/ "../modules/payments/assets/js/editor/module.js": +/*!******************************************************!*\ + !*** ../modules/payments/assets/js/editor/module.js ***! + \******************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _stripe = _interopRequireDefault(__webpack_require__(/*! ./stripe */ "../modules/payments/assets/js/editor/stripe.js")); +class StripeModule extends elementorModules.editor.utils.Module { + onElementorInit() { + this.stripeButton = new _stripe.default('stripe-button'); + } +} +exports["default"] = StripeModule; + +/***/ }), + +/***/ "../modules/payments/assets/js/editor/stripe.js": +/*!******************************************************!*\ + !*** ../modules/payments/assets/js/editor/stripe.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +const ElementEditorModule = __webpack_require__(/*! elementor-pro/editor/element-editor-module */ "../assets/dev/js/editor/element-editor-module.js"); +module.exports = ElementEditorModule.extend({ + __construct() { + ElementEditorModule.prototype.__construct.apply(this, arguments); + }, + getName() { + return 'stripe-button'; + }, + onInit() { + elementor.channels.editor.on('editor:widget:stripe-button:section_stripe_account:activated', this.onSectionActive); + }, + onSectionActive() { + return elementorPro.ajax.addRequest('get_stripe_tax_rates', { + success: data => { + this.updateOptions('stripe_test_env_tax_rates_list', data.test_api_key); + this.updateOptions('stripe_live_env_tax_rates_list', data.live_api_key); + } + }, true); + } +}); + +/***/ }), + +/***/ "../modules/popup/assets/js/editor/component.js": +/*!******************************************************!*\ + !*** ../modules/popup/assets/js/editor/component.js ***! + \******************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var hooks = _interopRequireWildcard(__webpack_require__(/*! ./hooks/ */ "../modules/popup/assets/js/editor/hooks/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class PopupComponent extends $e.modules.ComponentBase { + /** + * @type {null|Function} + */ + onPageSettingsCloseHandler = null; + getNamespace() { + return 'document/popup'; + } + defaultHooks() { + return this.importHooks(hooks); + } +} +exports["default"] = PopupComponent; + +/***/ }), + +/***/ "../modules/popup/assets/js/editor/controls/display-settings.js": +/*!**********************************************************************!*\ + !*** ../modules/popup/assets/js/editor/controls/display-settings.js ***! + \**********************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +class _default extends elementorModules.editor.views.ControlsStack { + constructor(...args) { + super(...args); + this.template = _.noop; + this.activeTab = 'content'; + this.listenTo(this.model, 'change', this.onModelChange); + } + getNamespaceArray() { + return ['popup', 'display-settings']; + } + className() { + return super.className() + ' elementor-popup__display-settings'; + } + toggleGroup(groupName, $groupElement) { + $groupElement.toggleClass('elementor-active', !!this.model.get(groupName)); + } + onRenderTemplate() { + this.activateFirstSection(); + } + onRender() { + const name = this.getOption('name'); + let $groupWrapper; + this.children.each(child => { + const type = child.model.get('type'); + if ('heading' !== type) { + if ($groupWrapper) { + $groupWrapper.append(child.$el); + } + return; + } + const groupName = child.model.get('name').replace('_heading', ''); + $groupWrapper = jQuery('
', { + id: `elementor-popup__${name}-controls-group--${groupName}`, + class: 'elementor-popup__display-settings_controls_group' + }); + const $imageWrapper = jQuery('
', { + class: 'elementor-popup__display-settings_controls_group__icon' + }), + $image = jQuery('', { + src: elementorPro.config.urls.modules + `popup/assets/images/${name}/${groupName}.svg` + }); + $imageWrapper.html($image); + $groupWrapper.html($imageWrapper); + child.$el.before($groupWrapper); + $groupWrapper.append(child.$el); + this.toggleGroup(groupName, $groupWrapper); + }); + } + onModelChange() { + const changedControlName = Object.keys(this.model.changed)[0], + changedControlView = this.getControlViewByName(changedControlName); + if ('switcher' !== changedControlView.model.get('type')) { + return; + } + this.toggleGroup(changedControlName, changedControlView.$el.parent()); + } +} +exports["default"] = _default; + +/***/ }), + +/***/ "../modules/popup/assets/js/editor/hooks/data/index.js": +/*!*************************************************************!*\ + !*** ../modules/popup/assets/js/editor/hooks/data/index.js ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "PopupSave", ({ + enumerable: true, + get: function () { + return _save.PopupSave; + } +})); +var _save = __webpack_require__(/*! ./save */ "../modules/popup/assets/js/editor/hooks/data/save.js"); + +/***/ }), + +/***/ "../modules/popup/assets/js/editor/hooks/data/save.js": +/*!************************************************************!*\ + !*** ../modules/popup/assets/js/editor/hooks/data/save.js ***! + \************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.PopupSave = void 0; +class PopupSave extends $e.modules.hookData.After { + getCommand() { + return 'document/save/save'; + } + getId() { + return 'elementor-pro-popup-save'; + } + getConditions() { + return 'popup' === elementor.config.document.type; + } + apply() { + const settings = {}; + jQuery.each(elementorPro.modules.popup.displaySettingsTypes, (type, data) => { + settings[type] = data.model.toJSON({ + remove: ['default'] + }); + }); + elementorPro.ajax.addRequest('popup_save_display_settings', { + data: { + settings + } + }); + } +} +exports.PopupSave = PopupSave; +var _default = exports["default"] = PopupSave; + +/***/ }), + +/***/ "../modules/popup/assets/js/editor/hooks/index.js": +/*!********************************************************!*\ + !*** ../modules/popup/assets/js/editor/hooks/index.js ***! + \********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +var _data = __webpack_require__(/*! ./data/ */ "../modules/popup/assets/js/editor/hooks/data/index.js"); +Object.keys(_data).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _data[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _data[key]; + } + }); +}); +var _ui = __webpack_require__(/*! ./ui/ */ "../modules/popup/assets/js/editor/hooks/ui/index.js"); +Object.keys(_ui).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _ui[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _ui[key]; + } + }); +}); + +/***/ }), + +/***/ "../modules/popup/assets/js/editor/hooks/ui/editor/documents/close/remove-library-tab.js": +/*!***********************************************************************************************!*\ + !*** ../modules/popup/assets/js/editor/hooks/ui/editor/documents/close/remove-library-tab.js ***! + \***********************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.PopupRemoveLibraryTab = void 0; +class PopupRemoveLibraryTab extends $e.modules.hookUI.After { + getCommand() { + return 'editor/documents/unload'; + } + getId() { + return 'elementor-pro-popup-remove-library-tab'; + } + getConditions(args) { + const { + document + } = args; + return 'popup' === document.config.type; + } + apply() { + $e.components.get('library').removeTab('templates/popups'); + } +} +exports.PopupRemoveLibraryTab = PopupRemoveLibraryTab; +var _default = exports["default"] = PopupRemoveLibraryTab; + +/***/ }), + +/***/ "../modules/popup/assets/js/editor/hooks/ui/editor/documents/close/remove-triggers.js": +/*!********************************************************************************************!*\ + !*** ../modules/popup/assets/js/editor/hooks/ui/editor/documents/close/remove-triggers.js ***! + \********************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.PopupRemoveTriggers = void 0; +class PopupRemoveTriggers extends $e.modules.hookUI.After { + getCommand() { + return 'editor/documents/unload'; + } + getId() { + return 'elementor-pro-popup-remove-triggers'; + } + getConditions(args) { + const { + document + } = args; + return 'popup' === document.config.type; + } + apply() { + this.removePanelFooterSubmenuItems(); + this.removePublishTabs(); + } + removePanelFooterSubmenuItems() { + const displaySettingsTypes = elementorPro.modules.popup.displaySettingsTypes; + jQuery.each(displaySettingsTypes, type => { + elementor.getPanelView().footer.currentView.removeSubMenuItem('saver-options', { + name: type + }); + }); + } + removePublishTabs() { + const component = $e.components.get('theme-builder-publish'), + displaySettingsTypes = elementorPro.modules.popup.displaySettingsTypes; + jQuery.each(displaySettingsTypes, type => { + component.removeTab(type); + }); + } +} +exports.PopupRemoveTriggers = PopupRemoveTriggers; +var _default = exports["default"] = PopupRemoveTriggers; + +/***/ }), + +/***/ "../modules/popup/assets/js/editor/hooks/ui/editor/documents/open/add-library-tab.js": +/*!*******************************************************************************************!*\ + !*** ../modules/popup/assets/js/editor/hooks/ui/editor/documents/open/add-library-tab.js ***! + \*******************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.PopupAddLibraryTab = void 0; +class PopupAddLibraryTab extends $e.modules.hookUI.After { + getCommand() { + return 'editor/documents/open'; + } + getId() { + return 'elementor-pro-popup-add-library-tab'; + } + getConditions(args) { + const document = elementor.documents.get(args.id); + return 'popup' === document.config.type; + } + apply() { + $e.components.get('library').addTab('templates/popups', { + title: __('Popups', 'elementor-pro'), + filter: { + source: 'remote', + type: 'popup' + } + }, 1); + } +} +exports.PopupAddLibraryTab = PopupAddLibraryTab; +var _default = exports["default"] = PopupAddLibraryTab; + +/***/ }), + +/***/ "../modules/popup/assets/js/editor/hooks/ui/editor/documents/open/add-triggers.js": +/*!****************************************************************************************!*\ + !*** ../modules/popup/assets/js/editor/hooks/ui/editor/documents/open/add-triggers.js ***! + \****************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.PopupAddTriggers = void 0; +var _displaySettings = _interopRequireDefault(__webpack_require__(/*! modules/popup/assets/js/editor/controls/display-settings */ "../modules/popup/assets/js/editor/controls/display-settings.js")); +class PopupAddTriggers extends $e.modules.hookUI.After { + getCommand() { + return 'editor/documents/open'; + } + getId() { + return 'elementor-pro-popup-add-triggers'; + } + getConditions(args) { + const document = elementor.documents.get(args.id); + return 'popup' === document.config.type; + } + apply() { + if (elementor.panel) { + this.addUI(); + } else { + // First open, the panel is not available yet. + elementor.once('preview:loaded', this.addUI.bind(this)); + } + } + addUI() { + // Since 'addUI' can be called each document load, if 'theme-builder-publish/triggers' exists, the UI already exist. + if ($e.routes.commands['theme-builder-publish/triggers']) { + return; + } + this.addPanelFooterSubmenuItems(); + this.addPublishTabs(); + } + addPublishTabs() { + const config = elementor.config.document.displaySettings, + component = $e.components.get('theme-builder-publish'), + module = elementorPro.modules.popup; + jQuery.each(module.displaySettingsTypes, (type, data) => { + // Init models for editor save. + data.model = new elementorModules.editor.elements.models.BaseSettings(config[type].settings, { + controls: config[type].controls + }); + component.addTab(type, { + View: _displaySettings.default, + viewOptions: { + name: type, + id: `elementor-popup-${type}__controls`, + model: data.model, + controls: data.model.controls + }, + name: type, + title: data.title, + description: data.publishScreenDescription, + image: elementorPro.config.urls.modules + `popup/assets/images/${type}-tab.svg` + }); + }); + } + addPanelFooterSubmenuItems() { + const component = $e.components.get('theme-builder-publish'), + displaySettingsTypes = elementorPro.modules.popup.displaySettingsTypes; + jQuery.each(displaySettingsTypes, (type, data) => { + elementor.getPanelView().footer.currentView.addSubMenuItem('saver-options', { + before: 'save-template', + name: type, + icon: data.icon, + title: data.title, + callback: () => $e.route(component.getTabRoute(type)) + }); + }); + } +} +exports.PopupAddTriggers = PopupAddTriggers; +var _default = exports["default"] = PopupAddTriggers; + +/***/ }), + +/***/ "../modules/popup/assets/js/editor/hooks/ui/index.js": +/*!***********************************************************!*\ + !*** ../modules/popup/assets/js/editor/hooks/ui/index.js ***! + \***********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "PopupAddLibraryTab", ({ + enumerable: true, + get: function () { + return _addLibraryTab.PopupAddLibraryTab; + } +})); +Object.defineProperty(exports, "PopupAddTriggers", ({ + enumerable: true, + get: function () { + return _addTriggers.PopupAddTriggers; + } +})); +Object.defineProperty(exports, "PopupRemoveLibraryTab", ({ + enumerable: true, + get: function () { + return _removeLibraryTab.PopupRemoveLibraryTab; + } +})); +Object.defineProperty(exports, "PopupRemoveTriggers", ({ + enumerable: true, + get: function () { + return _removeTriggers.PopupRemoveTriggers; + } +})); +var _addLibraryTab = __webpack_require__(/*! ./editor/documents/open/add-library-tab */ "../modules/popup/assets/js/editor/hooks/ui/editor/documents/open/add-library-tab.js"); +var _addTriggers = __webpack_require__(/*! ./editor/documents/open/add-triggers */ "../modules/popup/assets/js/editor/hooks/ui/editor/documents/open/add-triggers.js"); +var _removeLibraryTab = __webpack_require__(/*! ./editor/documents/close/remove-library-tab */ "../modules/popup/assets/js/editor/hooks/ui/editor/documents/close/remove-library-tab.js"); +var _removeTriggers = __webpack_require__(/*! ./editor/documents/close/remove-triggers */ "../modules/popup/assets/js/editor/hooks/ui/editor/documents/close/remove-triggers.js"); + +/***/ }), + +/***/ "../modules/popup/assets/js/editor/module.js": +/*!***************************************************!*\ + !*** ../modules/popup/assets/js/editor/module.js ***! + \***************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +var _component = _interopRequireDefault(__webpack_require__(/*! ./component */ "../modules/popup/assets/js/editor/component.js")); +class PopupModule extends elementorModules.editor.utils.Module { + constructor(...args) { + super(...args); + this.displaySettingsTypes = { + triggers: { + icon: 'eicon-click', + title: __('Triggers', 'elementor-pro'), + publishScreenDescription: __('What action the user needs to do for the popup to open.', 'elementor-pro') + }, + timing: { + icon: 'eicon-cog', + title: __('Advanced Rules', 'elementor-pro'), + publishScreenDescription: __('Requirements that have to be met for the popup to open.', 'elementor-pro') + } + }; + } + onElementorLoaded() { + this.component = $e.components.register(new _component.default({ + manager: this + })); + } +} +module.exports = PopupModule; + +/***/ }), + +/***/ "../modules/query-control/assets/js/editor.js": +/*!****************************************************!*\ + !*** ../modules/query-control/assets/js/editor.js ***! + \****************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + onElementorPreviewLoaded() { + elementor.addControlView('Query', __webpack_require__(/*! ./editor/query-control */ "../modules/query-control/assets/js/editor/query-control.js")); + __webpack_require__.e(/*! import() */ "modules_query-control_assets_js_editor_template-query-control_js").then(__webpack_require__.bind(__webpack_require__, /*! ./editor/template-query-control */ "../modules/query-control/assets/js/editor/template-query-control.js")).then(({ + default: TemplateQueryControl + }) => elementor.addControlView('template_query', TemplateQueryControl)); + } +}); + +/***/ }), + +/***/ "../modules/query-control/assets/js/editor/query-control.js": +/*!******************************************************************!*\ + !*** ../modules/query-control/assets/js/editor/query-control.js ***! + \******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +module.exports = elementor.modules.controls.Select2.extend({ + cache: null, + isTitlesReceived: false, + getSelect2Placeholder() { + return { + id: '', + text: __('All', 'elementor-pro') + }; + }, + getControlValueByName(controlName) { + const name = this.model.get('group_prefix') + controlName; + return this.elementSettingsModel.attributes[name]; + }, + getQueryDataDeprecated() { + return { + filter_type: this.model.get('filter_type'), + object_type: this.model.get('object_type'), + include_type: this.model.get('include_type'), + query: this.model.get('query') + }; + }, + getQueryData() { + // Use a clone to keep model data unchanged: + const autocomplete = elementorCommon.helpers.cloneObject(this.model.get('autocomplete')); + if (_.isEmpty(autocomplete.query)) { + autocomplete.query = {}; + } + // Specific for Group_Control_Query + if ('cpt_tax' === autocomplete.object) { + autocomplete.object = 'tax'; + if (_.isEmpty(autocomplete.query) || _.isEmpty(autocomplete.query.post_type)) { + autocomplete.query.post_type = this.getControlValueByName('post_type'); + } + } + return { + autocomplete + }; + }, + getSelect2DefaultOptions() { + const self = this; + return jQuery.extend(elementor.modules.controls.Select2.prototype.getSelect2DefaultOptions.apply(this, arguments), { + ajax: { + transport(params, success, failure) { + const bcFormat = !_.isEmpty(self.model.get('filter_type')); + let data = {}, + action = 'panel_posts_control_filter_autocomplete'; + if (bcFormat) { + data = self.getQueryDataDeprecated(); + action = 'panel_posts_control_filter_autocomplete_deprecated'; + } else { + data = self.getQueryData(); + } + data.q = params.data.q; + return elementorPro.ajax.addRequest(action, { + data, + success, + error: failure + }); + }, + data(params) { + return { + q: params.term, + page: params.page + }; + }, + cache: true + }, + escapeMarkup(markup) { + return markup; + }, + minimumInputLength: 1 + }); + }, + getValueTitles() { + const self = this, + data = {}, + bcFormat = !_.isEmpty(this.model.get('filter_type')); + let ids = this.getControlValue(), + action = 'query_control_value_titles', + filterTypeName = 'autocomplete', + filterType = {}; + if (bcFormat) { + filterTypeName = 'filter_type'; + filterType = this.model.get(filterTypeName).object; + data.filter_type = filterType; + data.object_type = self.model.get('object_type'); + data.include_type = self.model.get('include_type'); + data.unique_id = '' + self.cid + filterType; + action = 'query_control_value_titles_deprecated'; + } else { + filterType = this.model.get(filterTypeName).object; + data.get_titles = self.getQueryData().autocomplete; + data.unique_id = '' + self.cid + filterType; + } + if (!ids || !filterType) { + return; + } + if (!_.isArray(ids)) { + ids = [ids]; + } + elementorCommon.ajax.loadObjects({ + action, + ids, + data, + before() { + self.addControlSpinner(); + }, + success(ajaxData) { + self.isTitlesReceived = true; + self.model.set('options', ajaxData); + self.render(); + } + }); + }, + addControlSpinner() { + this.ui.select.prop('disabled', true); + this.$el.find('.elementor-control-title').after('  '); + }, + onReady() { + if (!this.isTitlesReceived) { + this.getValueTitles(); + } + } +}); + +/***/ }), + +/***/ "../modules/screenshots/assets/js/editor/component.js": +/*!************************************************************!*\ + !*** ../modules/screenshots/assets/js/editor/component.js ***! + \************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var dataHooks = _interopRequireWildcard(__webpack_require__(/*! ./hooks/data */ "../modules/screenshots/assets/js/editor/hooks/data/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class _default extends $e.modules.ComponentBase { + getNamespace() { + return 'screenshots'; + } + defaultHooks() { + return this.importHooks(dataHooks); + } +} +exports["default"] = _default; + +/***/ }), + +/***/ "../modules/screenshots/assets/js/editor/hooks/data/document/save/save/delete-screenshot.js": +/*!**************************************************************************************************!*\ + !*** ../modules/screenshots/assets/js/editor/hooks/data/document/save/save/delete-screenshot.js ***! + \**************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.DeleteScreenshot = void 0; +class DeleteScreenshot extends $e.modules.hookData.After { + getCommand() { + return 'document/save/save'; + } + getConditions(args) { + const { + status + } = args, + config = elementor.documents.getCurrent().config; + return 'publish' === status && config.support_site_editor; + } + getId() { + return 'document/save/save::delete-screenshot'; + } + apply() { + const postId = elementor.documents.getCurrent().id; + return elementorCommon.ajax.addRequest('screenshot_delete', { + unique_id: `delete_screenshot_${postId}`, + data: { + post_id: postId + } + }); + } +} +exports.DeleteScreenshot = DeleteScreenshot; +var _default = exports["default"] = DeleteScreenshot; + +/***/ }), + +/***/ "../modules/screenshots/assets/js/editor/hooks/data/index.js": +/*!*******************************************************************!*\ + !*** ../modules/screenshots/assets/js/editor/hooks/data/index.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "DeleteScreenshot", ({ + enumerable: true, + get: function () { + return _deleteScreenshot.DeleteScreenshot; + } +})); +var _deleteScreenshot = __webpack_require__(/*! ./document/save/save/delete-screenshot */ "../modules/screenshots/assets/js/editor/hooks/data/document/save/save/delete-screenshot.js"); + +/***/ }), + +/***/ "../modules/screenshots/assets/js/editor/module.js": +/*!*********************************************************!*\ + !*** ../modules/screenshots/assets/js/editor/module.js ***! + \*********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _component = _interopRequireDefault(__webpack_require__(/*! ./component */ "../modules/screenshots/assets/js/editor/component.js")); +class Module extends elementorModules.editor.utils.Module { + onElementorInit() { + $e.components.register(new _component.default()); + } +} +exports["default"] = Module; + +/***/ }), + +/***/ "../modules/scroll-snap/assets/js/editor/component.js": +/*!************************************************************!*\ + !*** ../modules/scroll-snap/assets/js/editor/component.js ***! + \************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var hooks = _interopRequireWildcard(__webpack_require__(/*! ./hooks/ui */ "../modules/scroll-snap/assets/js/editor/hooks/ui/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class ScrollSnapComponent extends $e.modules.ComponentBase { + getNamespace() { + return 'scroll-snap'; + } + defaultHooks() { + return this.importHooks(hooks); + } +} +exports["default"] = ScrollSnapComponent; + +/***/ }), + +/***/ "../modules/scroll-snap/assets/js/editor/hooks/ui/document/elements/settings/focus-preview.js": +/*!****************************************************************************************************!*\ + !*** ../modules/scroll-snap/assets/js/editor/hooks/ui/document/elements/settings/focus-preview.js ***! + \****************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.FocusPreview = void 0; +class FocusPreview extends $e.modules.hookData.After { + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 'focus-preview--document/elements/settings'; + } + getConditions(args) { + return args.settings.scroll_snap_padding?.size !== ''; + } + apply() { + setTimeout(() => { + elementor.$preview[0].contentWindow.scrollBy(0, 0); + }, 100); + } +} +exports.FocusPreview = FocusPreview; +var _default = exports["default"] = FocusPreview; + +/***/ }), + +/***/ "../modules/scroll-snap/assets/js/editor/hooks/ui/index.js": +/*!*****************************************************************!*\ + !*** ../modules/scroll-snap/assets/js/editor/hooks/ui/index.js ***! + \*****************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "FocusPreview", ({ + enumerable: true, + get: function () { + return _focusPreview.FocusPreview; + } +})); +var _focusPreview = __webpack_require__(/*! ./document/elements/settings/focus-preview */ "../modules/scroll-snap/assets/js/editor/hooks/ui/document/elements/settings/focus-preview.js"); + +/***/ }), + +/***/ "../modules/scroll-snap/assets/js/editor/module.js": +/*!*********************************************************!*\ + !*** ../modules/scroll-snap/assets/js/editor/module.js ***! + \*********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _component = _interopRequireDefault(__webpack_require__(/*! ./component */ "../modules/scroll-snap/assets/js/editor/component.js")); +class Module extends elementorModules.editor.utils.Module { + /** + * Init + */ + onInit() { + super.onInit(); + $e.components.register(new _component.default()); + } +} +exports["default"] = Module; + +/***/ }), + +/***/ "../modules/share-buttons/assets/js/editor/editor.js": +/*!***********************************************************!*\ + !*** ../modules/share-buttons/assets/js/editor/editor.js ***! + \***********************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + config: elementorPro.config.shareButtonsNetworks, + networksClassDictionary: { + google: 'fab fa-google-plus', + pocket: 'fab fa-get-pocket', + email: 'fas fa-envelope', + print: 'fas fa-print' + }, + getNetworkClass(networkName) { + let networkClass = this.networksClassDictionary[networkName] || 'fab fa-' + networkName; + if (elementor.config.icons_update_needed) { + networkClass = 'fa ' + networkClass; + } + return networkClass; + }, + getNetworkTitle(buttonSettings) { + // BC for items that are already selected and have been removed from the options list. + return buttonSettings.text || this.getNetworkData(buttonSettings)?.title; + }, + getNetworkData(buttonSettings) { + return this.config[buttonSettings.button]; + }, + hasCounter(networkName, settings) { + return 'icon' !== settings.view && 'yes' === settings.show_counter && this.config[networkName].has_counter; + } +}); + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/conditions/repeater-row.js": +/*!****************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/conditions/repeater-row.js ***! + \****************************************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = elementor.modules.controls.RepeaterRow.extend({ + template: '#tmpl-elementor-theme-builder-conditions-repeater-row', + childViewContainer: '.elementor-theme-builder-conditions-repeater-row-controls', + conflictCheckedOnFirstRender: false, + id() { + return 'elementor-condition-id-' + this.model.get('_id'); + }, + onBeforeRender() { + var subNameModel = this.collection.findWhere({ + name: 'sub_name' + }), + subIdModel = this.collection.findWhere({ + name: 'sub_id' + }), + subConditionConfig = this.config.conditions[this.model.attributes.sub_name]; + subNameModel.attributes.groups = this.getOptions(); + if (subConditionConfig && subConditionConfig.controls) { + _(subConditionConfig.controls).each(function (control) { + subIdModel.set(control); + subIdModel.set('name', 'sub_id'); + }); + } + }, + initialize() { + elementor.modules.controls.RepeaterRow.prototype.initialize.apply(this, arguments); + this.config = elementor.config.document.theme_builder; + }, + updateOptions() { + if (this.model.changed.name) { + this.model.set({ + sub_name: '', + sub_id: '' + }); + } + if (this.model.changed.name || this.model.changed.sub_name) { + this.model.set('sub_id', '', { + silent: true + }); + var subIdModel = this.collection.findWhere({ + name: 'sub_id' + }); + subIdModel.set({ + type: 'select', + options: { + '': 'All' + } + }); + this.render(); + } + if (this.model.changed.type) { + this.setTypeAttribute(); + } + }, + getOptions() { + var self = this, + conditionConfig = self.config.conditions[this.model.get('name')]; + if (!conditionConfig) { + return; + } + var options = { + '': conditionConfig.all_label + }; + _(conditionConfig.sub_conditions).each(function (conditionId, conditionIndex) { + var subConditionConfig = self.config.conditions[conditionId], + group; + if (!subConditionConfig) { + return; + } + if (subConditionConfig.sub_conditions.length) { + group = { + label: subConditionConfig.label, + options: {} + }; + group.options[conditionId] = subConditionConfig.all_label; + _(subConditionConfig.sub_conditions).each(function (subConditionId) { + group.options[subConditionId] = self.config.conditions[subConditionId].label; + }); + + // Use a sting key - to keep order + options['key' + conditionIndex] = group; + } else { + options[conditionId] = subConditionConfig.label; + } + }); + return options; + }, + setTypeAttribute() { + var typeView = this.children.findByModel(this.collection.findWhere({ + name: 'type' + })); + typeView.$el.attr('data-elementor-condition-type', typeView.getControlValue()); + }, + // Moved from `modules/theme-builder/assets/js/editor/conditions/repeater.js`. + checkConflicts() { + var modelId = this.model.get('_id'), + rowId = 'elementor-condition-id-' + modelId, + errorMessageId = 'elementor-conditions-conflict-message-' + modelId, + $error = jQuery('#' + errorMessageId); + + // On render - the row isn't exist, so don't cache it. + jQuery('#' + rowId).removeClass('elementor-error'); + $error.remove(); + elementorPro.ajax.addRequest('theme_builder_conditions_check_conflicts', { + unique_id: rowId, + data: { + condition: this.model.toJSON() + }, + success(data) { + if (!_.isEmpty(data)) { + jQuery('#' + rowId).addClass('elementor-error').after('
' + data + '
'); + } + } + }); + }, + onRender() { + var nameModel = this.collection.findWhere({ + name: 'name' + }), + subNameModel = this.collection.findWhere({ + name: 'sub_name' + }), + subIdModel = this.collection.findWhere({ + name: 'sub_id' + }), + nameView = this.children.findByModel(nameModel), + subNameView = this.children.findByModel(subNameModel), + subIdView = this.children.findByModel(subIdModel), + conditionConfig = this.config.conditions[this.model.attributes.name], + subConditionConfig = this.config.conditions[this.model.attributes.sub_name], + typeConfig = this.config.types[this.config.settings.template_type]; + if (typeConfig.condition_type === nameView.getControlValue() && 'general' !== nameView.getControlValue() && !_.isEmpty(conditionConfig.sub_conditions)) { + nameView.$el.hide(); + } + if (!conditionConfig || _.isEmpty(conditionConfig.sub_conditions) && _.isEmpty(conditionConfig.controls) || !nameView.getControlValue() || 'general' === nameView.getControlValue()) { + subNameView.$el.hide(); + } + if (!subConditionConfig || _.isEmpty(subConditionConfig.controls) || !subNameView.getControlValue()) { + subIdView.$el.hide(); + } + + // Avoid set a `single` for a-l-l singular types. (conflicted with 404 & custom cpt like Shops and Events plugins). + if ('singular' === typeConfig.condition_type) { + if ('' === subNameView.getControlValue()) { + subNameView.setValue('post'); + } + } + this.setTypeAttribute(); + if (!this.conflictCheckedOnFirstRender) { + this.checkConflicts(); + this.conflictCheckedOnFirstRender = true; + } + }, + onModelChange() { + this.updateOptions(); + this.checkConflicts(); + } +}); + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/conditions/repeater.js": +/*!************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/conditions/repeater.js ***! + \************************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +var _repeaterRow = _interopRequireDefault(__webpack_require__(/*! ./repeater-row */ "../modules/theme-builder/assets/js/editor/conditions/repeater-row.js")); +module.exports = elementor.modules.controls.Repeater.extend({ + childView: _repeaterRow.default, + updateActiveRow() {}, + initialize() { + elementor.modules.controls.Repeater.prototype.initialize.apply(this, arguments); + this.config = elementor.config.document.theme_builder; + this.updateConditionsOptions(this.config.settings.template_type); + }, + updateConditionsOptions(templateType) { + var self = this, + conditionType = self.config.types[templateType].condition_type, + options = {}; + _([conditionType]).each(function (conditionId, conditionIndex) { + var conditionConfig = self.config.conditions[conditionId], + group = { + label: conditionConfig.label, + options: {} + }; + group.options[conditionId] = conditionConfig.all_label; + _(conditionConfig.sub_conditions).each(function (subConditionId) { + group.options[subConditionId] = self.config.conditions[subConditionId].label; + }); + options[conditionIndex] = group; + }); + var fields = this.model.get('fields'); + fields[1].default = conditionType; + if ('general' === conditionType) { + fields[1].groups = options; + } else { + fields[2].groups = options; + } + }, + onRender() { + this.ui.btnAddRow.text(__('Add Condition', 'elementor-pro')); + } +}); + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/conditions/view.js": +/*!********************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/conditions/view.js ***! + \********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +var inlineControlsStack = __webpack_require__(/*! elementor-pro/editor/inline-controls-stack.js */ "../assets/dev/js/editor/inline-controls-stack.js"); +module.exports = inlineControlsStack.extend({ + id: 'elementor-theme-builder-conditions-view', + template: '#tmpl-elementor-theme-builder-conditions-view', + childViewContainer: '#elementor-theme-builder-conditions-controls', + childViewOptions() { + return { + elementSettingsModel: this.model + }; + } +}); + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/data/document/elements/settings/index.js": +/*!************************************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/data/document/elements/settings/index.js ***! + \************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "ThemeBuilderSaveAndReload", ({ + enumerable: true, + get: function () { + return _saveAndReload.ThemeBuilderSaveAndReload; + } +})); +Object.defineProperty(exports, "ThemeBuilderUpdatePreviewOptions", ({ + enumerable: true, + get: function () { + return _updatePreviewOptions.ThemeBuilderUpdatePreviewOptions; + } +})); +var _saveAndReload = __webpack_require__(/*! ./save-and-reload */ "../modules/theme-builder/assets/js/editor/hooks/data/document/elements/settings/save-and-reload.js"); +var _updatePreviewOptions = __webpack_require__(/*! ./update-preview-options */ "../modules/theme-builder/assets/js/editor/hooks/data/document/elements/settings/update-preview-options.js"); + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/data/document/elements/settings/save-and-reload.js": +/*!**********************************************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/data/document/elements/settings/save-and-reload.js ***! + \**********************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.ThemeBuilderSaveAndReload = void 0; +/** + * Hook fired when template: 'single' page layout changed. + */ +class ThemeBuilderSaveAndReload extends $e.modules.hookData.After { + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 'elementor-pro-theme-builder-save-and-reload'; + } + getContainerType() { + return 'document'; + } + getConditions(args) { + return args.settings && args.settings.page_template; + } + apply() { + $e.run('document/save/auto', { + force: true, + onSuccess: () => { + elementor.reloadPreview(); + elementor.once('preview:loaded', () => { + $e.route('panel/page-settings/settings'); + }); + } + }); + } +} +exports.ThemeBuilderSaveAndReload = ThemeBuilderSaveAndReload; +var _default = exports["default"] = ThemeBuilderSaveAndReload; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/data/document/elements/settings/update-preview-options.js": +/*!*****************************************************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/data/document/elements/settings/update-preview-options.js ***! + \*****************************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.ThemeBuilderUpdatePreviewOptions = void 0; +class ThemeBuilderUpdatePreviewOptions extends $e.modules.hookData.After { + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 'elementor-pro-theme-builder-update-preview-options'; + } + getContainerType() { + return 'document'; + } + getConditions(args) { + return args.settings && args.settings.preview_type; + } + apply(args) { + const { + containers = [args.container] + } = args, + { + themeBuilder + } = elementorPro.modules; + $e.run('document/elements/settings', { + containers, + settings: { + preview_id: '', + preview_search_term: '' + } + }); + if ($e.routes.is('panel/page-settings/settings')) { + themeBuilder.updatePreviewIdOptions(true); + } + } +} +exports.ThemeBuilderUpdatePreviewOptions = ThemeBuilderUpdatePreviewOptions; +var _default = exports["default"] = ThemeBuilderUpdatePreviewOptions; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/data/document/save/save-conditions.js": +/*!*********************************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/data/document/save/save-conditions.js ***! + \*********************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.ThemeBuilderSaveConditions = void 0; +class ThemeBuilderSaveConditions extends $e.modules.hookData.After { + getCommand() { + return 'document/save/save'; + } + getId() { + return 'elementor-pro-theme-builder-save-conditions'; + } + getConditions() { + return !!elementor.config.document.theme_builder; + } + apply() { + const { + conditionsModel + } = elementorPro.modules.themeBuilder; + elementorPro.ajax.addRequest('theme_builder_save_conditions', { + data: conditionsModel.toJSON({ + remove: ['default'] + }), + success: () => { + elementor.config.document.theme_builder.settings.conditions = conditionsModel.get('conditions'); + } + }); + } +} +exports.ThemeBuilderSaveConditions = ThemeBuilderSaveConditions; +var _default = exports["default"] = ThemeBuilderSaveConditions; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/data/document/save/show-conditions.js": +/*!*********************************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/data/document/save/show-conditions.js ***! + \*********************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.ThemeBuilderShowConditions = void 0; +class ThemeBuilderShowConditions extends $e.modules.hookData.Dependency { + getCommand() { + return 'document/save/default'; + } + getId() { + return 'elementor-pro-theme-builder-show-conditions'; + } + getConditions(args) { + const { + force = false + } = args; + + // If force save, do not show conditions. + if (force) { + return false; + } + let showConditions = false; + const themeBuilder = elementor.config.document.theme_builder; + if (themeBuilder) { + const hasConditions = themeBuilder.settings.conditions.length, + hasLocation = themeBuilder.settings.location, + isDraft = 'draft' === elementor.settings.page.model.get('post_status'); + if (hasLocation && (!hasConditions || isDraft)) { + showConditions = true; + } + } + return showConditions; + } + apply() { + $e.route('theme-builder-publish/conditions'); + return false; // HookBreak. + } +} +exports.ThemeBuilderShowConditions = ThemeBuilderShowConditions; +var _default = exports["default"] = ThemeBuilderShowConditions; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/data/editor/documents/preview/preview-break.js": +/*!******************************************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/data/editor/documents/preview/preview-break.js ***! + \******************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.ThemeBuilderPreviewBreak = void 0; +class ThemeBuilderPreviewBreak extends $e.modules.hookData.Dependency { + getCommand() { + return 'editor/documents/preview'; + } + getId() { + return 'elementor-pro-theme-builder-preview-break'; + } + getConditions(args) { + // If preview is forced, do not break it. + if (args.force) { + return false; + } + return !!elementor.documents.get(args.id).config.theme_builder; + } + apply() { + return false; // HookBreak. + } +} +exports.ThemeBuilderPreviewBreak = ThemeBuilderPreviewBreak; +var _default = exports["default"] = ThemeBuilderPreviewBreak; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/data/index.js": +/*!*********************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/data/index.js ***! + \*********************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +var _exportNames = { + ThemeBuilderSaveConditions: true, + ThemeBuilderShowConditions: true, + ThemeBuilderPreviewBreak: true +}; +Object.defineProperty(exports, "ThemeBuilderPreviewBreak", ({ + enumerable: true, + get: function () { + return _previewBreak.ThemeBuilderPreviewBreak; + } +})); +Object.defineProperty(exports, "ThemeBuilderSaveConditions", ({ + enumerable: true, + get: function () { + return _saveConditions.ThemeBuilderSaveConditions; + } +})); +Object.defineProperty(exports, "ThemeBuilderShowConditions", ({ + enumerable: true, + get: function () { + return _showConditions.ThemeBuilderShowConditions; + } +})); +var _settings = __webpack_require__(/*! ./document/elements/settings */ "../modules/theme-builder/assets/js/editor/hooks/data/document/elements/settings/index.js"); +Object.keys(_settings).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _settings[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _settings[key]; + } + }); +}); +var _saveConditions = __webpack_require__(/*! ./document/save/save-conditions */ "../modules/theme-builder/assets/js/editor/hooks/data/document/save/save-conditions.js"); +var _showConditions = __webpack_require__(/*! ./document/save/show-conditions */ "../modules/theme-builder/assets/js/editor/hooks/data/document/save/show-conditions.js"); +var _previewBreak = __webpack_require__(/*! ./editor/documents/preview/preview-break */ "../modules/theme-builder/assets/js/editor/hooks/data/editor/documents/preview/preview-break.js"); + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/index.js": +/*!****************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/index.js ***! + \****************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +var _data = __webpack_require__(/*! ./data/ */ "../modules/theme-builder/assets/js/editor/hooks/data/index.js"); +Object.keys(_data).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _data[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _data[key]; + } + }); +}); +var _ui = __webpack_require__(/*! ./ui/ */ "../modules/theme-builder/assets/js/editor/hooks/ui/index.js"); +Object.keys(_ui).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _ui[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _ui[key]; + } + }); +}); + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/ui/editor/document/elements/settings/toggle-menu-conditions.js": +/*!**********************************************************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/ui/editor/document/elements/settings/toggle-menu-conditions.js ***! + \**********************************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.ThemeBuilderToggleMenuConditions = void 0; +class ThemeBuilderToggleMenuConditions extends $e.modules.hookUI.After { + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 'elementor-pro-theme-builder-toggle-menu-conditions'; + } + getContainerType() { + return 'document'; + } + getConditions(args) { + return args.settings && args.settings.location; + } + apply() { + const { + themeBuilder + } = elementorPro.modules; + themeBuilder.ui.menuConditions.toggle(!!elementor.config.document.theme_builder.settings.location); + } +} +exports.ThemeBuilderToggleMenuConditions = ThemeBuilderToggleMenuConditions; +var _default = exports["default"] = ThemeBuilderToggleMenuConditions; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/ui/editor/documents/close/remove-editor-ui.js": +/*!*****************************************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/ui/editor/documents/close/remove-editor-ui.js ***! + \*****************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.ThemeBuilderRemoveEditorUI = void 0; +class ThemeBuilderRemoveEditorUI extends $e.modules.hookUI.After { + getCommand() { + return 'editor/documents/unload'; + } + getId() { + return 'elementor-pro-theme-builder-remove-editor-ui'; + } + getConditions(args) { + const { + document + } = args; + return document.config.theme_builder; + } + apply() { + this.removePanelFooterSubmenuItems(); + this.removePublishTabs(); + } + removePanelFooterSubmenuItems() { + const footerView = elementor.getPanelView().footer.currentView, + behavior = footerView._behaviors[Object.keys(footerView.behaviors()).indexOf('saver')]; + elementor.getPanelView().footer.currentView.removeSubMenuItem('saver-options', { + name: 'conditions' + }); + behavior.ui.buttonPreview.tipsy('enable').removeClass('elementor-panel-footer-theme-builder-buttons-wrapper elementor-toggle-state'); + } + removePublishTabs() { + const component = $e.components.get('theme-builder-publish'); + component.removeTab('conditions'); + } +} +exports.ThemeBuilderRemoveEditorUI = ThemeBuilderRemoveEditorUI; +var _default = exports["default"] = ThemeBuilderRemoveEditorUI; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/ui/editor/documents/open/add-editor-ui.js": +/*!*************************************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/ui/editor/documents/open/add-editor-ui.js ***! + \*************************************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.ThemeBuilderAddEditorUI = void 0; +var _view = _interopRequireDefault(__webpack_require__(/*! ../../../../../conditions/view */ "../modules/theme-builder/assets/js/editor/conditions/view.js")); +class ThemeBuilderAddEditorUI extends $e.modules.hookUI.After { + getCommand() { + return 'editor/documents/open'; + } + getId() { + return 'elementor-pro-theme-builder-add-editor-ui'; + } + getConditions(args) { + return elementor.documents.get(args.id).config.theme_builder; + } + apply() { + if (elementor.panel) { + this.addUI(); + } else { + // First open, the panel is not available yet. + elementor.once('preview:loaded', this.addUI.bind(this)); + } + } + addUI() { + this.addRepeaterControlView(); + this.addPanelFooterSubmenuItems(); + this.addPublishTabs(); + } + addRepeaterControlView() { + elementor.addControlView('Conditions_repeater', __webpack_require__(/*! ../../../../../conditions/repeater */ "../modules/theme-builder/assets/js/editor/conditions/repeater.js")); + } + addPublishTabs() { + const component = $e.components.get('theme-builder-publish'), + themeBuilderModuleConfig = elementor.config.document.theme_builder, + settings = themeBuilderModuleConfig.settings; + component.manager.conditionsModel = new elementorModules.editor.elements.models.BaseSettings(settings, { + controls: themeBuilderModuleConfig.template_conditions.controls + }); + component.addTab('conditions', { + title: __('Conditions', 'elementor-pro'), + View: _view.default, + viewOptions: { + model: component.manager.conditionsModel, + controls: component.manager.conditionsModel.controls + }, + name: 'conditions', + description: __('Apply current template to these pages.', 'elementor-pro'), + image: elementorPro.config.urls.modules + 'theme-builder/assets/images/conditions-tab.svg' + }); + } + addPanelFooterSubmenuItems() { + const footerView = elementor.getPanelView().footer.currentView, + behavior = footerView._behaviors[Object.keys(footerView.behaviors()).indexOf('saver')]; + footerView.ui.menuConditions = footerView.addSubMenuItem('saver-options', { + before: 'save-template', + name: 'conditions', + icon: 'eicon-flow', + title: __('Display Conditions', 'elementor-pro'), + callback: () => $e.route('theme-builder-publish/conditions') + }); + footerView.ui.menuConditions.toggle(!!elementor.config.document.theme_builder.settings.location); + behavior.ui.buttonPreview.tipsy('disable').html(jQuery('#tmpl-elementor-theme-builder-button-preview').html()).addClass('elementor-panel-footer-theme-builder-buttons-wrapper elementor-toggle-state'); + } +} +exports.ThemeBuilderAddEditorUI = ThemeBuilderAddEditorUI; +var _default = exports["default"] = ThemeBuilderAddEditorUI; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/ui/index.js": +/*!*******************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/ui/index.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "ThemeBuilderAddEditorUI", ({ + enumerable: true, + get: function () { + return _addEditorUi.ThemeBuilderAddEditorUI; + } +})); +Object.defineProperty(exports, "ThemeBuilderFooterSaverAfterSave", ({ + enumerable: true, + get: function () { + return _after.ThemeBuilderFooterSaverAfterSave; + } +})); +Object.defineProperty(exports, "ThemeBuilderRemoveEditorUI", ({ + enumerable: true, + get: function () { + return _removeEditorUi.ThemeBuilderRemoveEditorUI; + } +})); +Object.defineProperty(exports, "ThemeBuilderToggleMenuConditions", ({ + enumerable: true, + get: function () { + return _toggleMenuConditions.ThemeBuilderToggleMenuConditions; + } +})); +var _addEditorUi = __webpack_require__(/*! ./editor/documents/open/add-editor-ui */ "../modules/theme-builder/assets/js/editor/hooks/ui/editor/documents/open/add-editor-ui.js"); +var _removeEditorUi = __webpack_require__(/*! ./editor/documents/close/remove-editor-ui */ "../modules/theme-builder/assets/js/editor/hooks/ui/editor/documents/close/remove-editor-ui.js"); +var _toggleMenuConditions = __webpack_require__(/*! ./editor/document/elements/settings/toggle-menu-conditions */ "../modules/theme-builder/assets/js/editor/hooks/ui/editor/document/elements/settings/toggle-menu-conditions.js"); +var _after = __webpack_require__(/*! ./save/after */ "../modules/theme-builder/assets/js/editor/hooks/ui/save/after.js"); + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/hooks/ui/save/after.js": +/*!************************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/hooks/ui/save/after.js ***! + \************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.ThemeBuilderFooterSaverAfterSave = void 0; +class ThemeBuilderFooterSaverAfterSave extends $e.modules.hookUI.After { + getCommand() { + return 'document/save/save'; + } + getId() { + return 'theme-builder-footer-saver-after-save'; + } + getConditions() { + return elementor.config.document.support_site_editor; + } + apply(args, result) { + const { + status + } = args; + if (result.statusChanged) { + this.onPageStatusChange(status); + } + } + onPageStatusChange(newStatus) { + if ('publish' !== newStatus) { + return; + } + const options = { + classes: 'e-theme-builder-save-toaster', + message: elementor.config.document.panel.messages.publish_notification, + buttons: [{ + name: 'open_site_editor', + text: '' + __('Open Site Editor', 'elementor-pro') + '', + callback() { + $e.run('app/open'); + } + }, { + name: 'view_live_site', + text: '' + __('View Live Site', 'elementor-pro') + '', + callback() { + open(elementor.config.document.urls.permalink); + } + }] + }; + elementor.notifications.showToast(options); + } +} +exports.ThemeBuilderFooterSaverAfterSave = ThemeBuilderFooterSaverAfterSave; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/module.js": +/*!***********************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/module.js ***! + \***********************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _component = _interopRequireDefault(__webpack_require__(/*! ./publish/component */ "../modules/theme-builder/assets/js/editor/publish/component.js")); +class ThemeBuilderModule extends elementorModules.editor.utils.Module { + __construct(...args) { + super.__construct(...args); + Object.defineProperty(elementorPro.config, 'theme_builder', { + get() { + elementorCommon.helpers.softDeprecated('theme_builder', '2.9.0', 'elementor.config.document.theme_builder'); + return elementor.config.document.theme_builder; + } + }); + } + onElementorLoaded() { + this.component = $e.components.register(new _component.default({ + manager: this + })); + elementor.on('document:loaded', this.onDocumentLoaded.bind(this)); + elementor.on('document:unload', this.onDocumentUnloaded.bind(this)); + this.onApplyPreview = this.onApplyPreview.bind(this); + this.onSectionPreviewSettingsActive = this.onSectionPreviewSettingsActive.bind(this); + elementor.channels.editor.on('elementorProSiteLogo:change', this.openSiteIdentity); + } + onDocumentLoaded(document) { + if (!document.config.theme_builder) { + return; + } + elementor.getPanelView().on('set:page:page_settings', this.updatePreviewIdOptions); + elementor.channels.editor.on('elementorThemeBuilder:ApplyPreview', this.onApplyPreview); + elementor.channels.editor.on('page_settings:preview_settings:activated', this.onSectionPreviewSettingsActive); + } + onDocumentUnloaded(document) { + if (!document.config.theme_builder) { + return; + } + elementor.getPanelView().off('set:page:page_settings', this.updatePreviewIdOptions); + elementor.channels.editor.off('elementorThemeBuilder:ApplyPreview', this.onApplyPreview); + elementor.channels.editor.off('page_settings:preview_settings:activated', this.onSectionPreviewSettingsActive); + } + saveAndReload() { + $e.run('document/save/auto', { + force: true, + onSuccess: () => { + elementor.dynamicTags.cleanCache(); + const isInitialDocument = elementor.config.initial_document.id === elementor.documents.getCurrentId(); + if (isInitialDocument) { + // Page templates (e.g. single) with header/footer requires a full reload in order + // to change the main query also for them. + elementor.reloadPreview(); + } else { + $e.internal('editor/documents/attach-preview'); + } + } + }); + } + onApplyPreview() { + this.saveAndReload(); + } + onSectionPreviewSettingsActive() { + this.updatePreviewIdOptions(true); + } + updatePreviewIdOptions = render => { + let previewType = elementor.settings.page.model.get('preview_type'); + if (!previewType) { + return; + } + previewType = previewType.split('/'); + const currentView = elementor.getPanelView().getCurrentPageView(), + controlModel = currentView.collection.findWhere({ + name: 'preview_id' + }); + const templateType = previewType[0], + sourceType = previewType[1]; + if ('author' === previewType[1]) { + controlModel.set({ + autocomplete: { + object: 'author' + } + }); + } else if (this.isTemplateTypeTaxonomyLoop(templateType)) { + controlModel.set({ + autocomplete: { + object: 'tax', + query: { + taxonomy: sourceType + } + } + }); + } else if ('single' === templateType) { + controlModel.set({ + autocomplete: { + object: 'post', + query: { + post_type: sourceType + } + } + }); + } else { + controlModel.set({ + autocomplete: { + object: '' + } + }); + } + if (true === render) { + // Can be model. + const controlView = currentView.children.findByModel(controlModel); + controlView.render(); + controlView.$el.toggle(!!controlModel.get('autocomplete').object); + } + }; + isTemplateTypeTaxonomyLoop(templateType) { + return ['post_taxonomy', 'product_taxonomy'].includes(templateType); + } + async openSiteIdentity() { + await $e.run('panel/global/open'); + $e.route('panel/global/settings-site-identity'); + } +} +exports["default"] = ThemeBuilderModule; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/publish/component.js": +/*!**********************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/publish/component.js ***! + \**********************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _content = _interopRequireDefault(__webpack_require__(/*! ./content */ "../modules/theme-builder/assets/js/editor/publish/content.js")); +var _layout = _interopRequireDefault(__webpack_require__(/*! ./layout */ "../modules/theme-builder/assets/js/editor/publish/layout.js")); +var hooks = _interopRequireWildcard(__webpack_require__(/*! ../hooks */ "../modules/theme-builder/assets/js/editor/hooks/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class Component extends $e.modules.ComponentModalBase { + getNamespace() { + // TODO: should be 'theme-builder/publish'. + return 'theme-builder-publish'; + } + getModalLayout() { + return _layout.default; + } + defaultCommands() { + return { + next: () => { + const tabs = Object.keys(this.tabs), + next = tabs[this.currentTabIndex + 1]; + if (next) { + $e.route(this.getTabRoute(next)); + } + }, + save: () => { + $e.run('document/save/default', { + force: true + }); + this.layout.hideModal(); + }, + 'preview-settings': () => { + // TODO: This is function is not part of this component. + const panel = elementor.getPanelView(); + $e.route('panel/page-settings/settings'); + panel.getCurrentPageView().activateSection('preview_settings')._renderChildren(); + } + }; + } + defaultHooks() { + return this.importHooks(hooks); + } + getTabsWrapperSelector() { + return '#elementor-publish__tabs'; + } + renderTab(tab) { + const tabs = this.getTabs(), + keys = Object.keys(tabs), + tabArgs = tabs[tab]; + this.currentTabIndex = keys.indexOf(tab); + const isLastTab = !keys[this.currentTabIndex + 1]; + this.layout.modalContent.currentView.screen.show(new tabArgs.View(tabArgs.viewOptions)); + this.layout.modal.getElements('next').toggle(!isLastTab); + this.layout.modal.getElements('publish').toggleClass('e-primary', isLastTab); + } + activateTab(tab) { + $e.routes.saveState(this.getNamespace()); + super.activateTab(tab); + } + open() { + super.open(); + if (!this.layoutContent) { + this.layout.showLogo(); + this.layout.modalContent.show(new _content.default({ + component: this + })); + this.layoutContent = true; + } + return true; + } +} +exports["default"] = Component; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/publish/content.js": +/*!********************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/publish/content.js ***! + \********************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +class _default extends Marionette.LayoutView { + id() { + return 'elementor-publish'; + } + getTemplate() { + return Marionette.TemplateCache.get('#tmpl-elementor-component-publish'); + } + regions() { + return { + screen: '#elementor-publish__screen' + }; + } + templateHelpers() { + return { + tabs: this.getOption('component').getTabs() + }; + } +} +exports["default"] = _default; + +/***/ }), + +/***/ "../modules/theme-builder/assets/js/editor/publish/layout.js": +/*!*******************************************************************!*\ + !*** ../modules/theme-builder/assets/js/editor/publish/layout.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +class _default extends elementorModules.common.views.modal.Layout { + getModalOptions() { + return { + id: 'elementor-publish__modal', + hide: { + onButtonClick: false + } + }; + } + getLogoOptions() { + return { + title: __('Publish Settings', 'elementor-pro') + }; + } + initModal() { + super.initModal(); + this.modal.addButton({ + name: 'publish', + text: __('Save & Close', 'elementor-pro'), + callback: () => $e.run('theme-builder-publish/save') + }); + this.modal.getElements('publish').addClass('e-btn-txt'); + this.modal.addButton({ + name: 'next', + text: __('Next', 'elementor-pro'), + callback: () => $e.run('theme-builder-publish/next') + }); + const $publishButton = this.modal.getElements('publish'); + this.modal.getElements('next').addClass('e-primary').add($publishButton).addClass('elementor-button').removeClass('dialog-button'); + } +} +exports["default"] = _default; + +/***/ }), + +/***/ "../modules/theme-elements/assets/js/editor/comments-skin.js": +/*!*******************************************************************!*\ + !*** ../modules/theme-elements/assets/js/editor/comments-skin.js ***! + \*******************************************************************/ +/***/ ((module) => { + +"use strict"; + + +module.exports = function () { + var self = this; + self.onPanelShow = function (panel, model) { + var settingsModel = model.get('settings'); + + // If no skins - set the skin to `theme_comments`. + if (!settingsModel.controls._skin.default) { + settingsModel.set('_skin', 'theme_comments'); + } + }; + self.init = function () { + elementor.hooks.addAction('panel/open_editor/widget/post-comments', self.onPanelShow); + }; + self.init(); +}; + +/***/ }), + +/***/ "../modules/theme-elements/assets/js/editor/editor.js": +/*!************************************************************!*\ + !*** ../modules/theme-elements/assets/js/editor/editor.js ***! + \************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +module.exports = elementorModules.editor.utils.Module.extend({ + onElementorPreviewLoaded() { + var CommentsSkin = __webpack_require__(/*! ./comments-skin */ "../modules/theme-elements/assets/js/editor/comments-skin.js"); + this.commentsSkin = new CommentsSkin(); + } +}); + +/***/ }), + +/***/ "../modules/video-playlist/assets/js/editor/component.js": +/*!***************************************************************!*\ + !*** ../modules/video-playlist/assets/js/editor/component.js ***! + \***************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var hooks = _interopRequireWildcard(__webpack_require__(/*! ./hooks/ui */ "../modules/video-playlist/assets/js/editor/hooks/ui/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class VideoPlaylistComponent extends $e.modules.ComponentBase { + getNamespace() { + return 'video-playlist'; + } + defaultHooks() { + return this.importHooks(hooks); + } +} +exports["default"] = VideoPlaylistComponent; + +/***/ }), + +/***/ "../modules/video-playlist/assets/js/editor/hooks/ui/document/elements/settings/active-tab.js": +/*!****************************************************************************************************!*\ + !*** ../modules/video-playlist/assets/js/editor/hooks/ui/document/elements/settings/active-tab.js ***! + \****************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.ActiveTab = void 0; +/** + * Hook fired when template: 'single' page layout changed. + */ +class ActiveTab extends $e.modules.hookData.After { + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 'active-tab--document/elements/settings'; + } + getContainerType() { + return 'repeater'; + } + getConditions(args) { + return args.settings.inner_tab_content_1 || args.settings.inner_tab_content_2; + } + apply(args) { + if (args.settings.inner_tab_content_1) { + args.container.view.model.get('editSettings').set('innerActiveIndex', 0); + } else if (args.settings.inner_tab_content_2) { + args.container.view.model.get('editSettings').set('innerActiveIndex', 1); + } + } +} +exports.ActiveTab = ActiveTab; +var _default = exports["default"] = ActiveTab; + +/***/ }), + +/***/ "../modules/video-playlist/assets/js/editor/hooks/ui/index.js": +/*!********************************************************************!*\ + !*** ../modules/video-playlist/assets/js/editor/hooks/ui/index.js ***! + \********************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "ActiveTab", ({ + enumerable: true, + get: function () { + return _activeTab.ActiveTab; + } +})); +var _activeTab = __webpack_require__(/*! ./document/elements/settings/active-tab */ "../modules/video-playlist/assets/js/editor/hooks/ui/document/elements/settings/active-tab.js"); + +/***/ }), + +/***/ "../modules/video-playlist/assets/js/editor/module.js": +/*!************************************************************!*\ + !*** ../modules/video-playlist/assets/js/editor/module.js ***! + \************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _component = _interopRequireDefault(__webpack_require__(/*! ./component */ "../modules/video-playlist/assets/js/editor/component.js")); +class Module extends elementorModules.editor.utils.Module { + /** + * Init + */ + onInit() { + super.onInit(); + $e.components.register(new _component.default()); + } + onElementorLoaded() { + elementor.channels.editor.on('elementorPlaylistWidget:setVideoData', e => { + $e.run('document/elements/settings', { + container: e.container, + settings: { + thumbnail: { + url: e.currentItem.thumbnail ? e.currentItem.thumbnail.url : '' + }, + title: e.currentItem.video_title ? e.currentItem.video_title : '', + duration: e.currentItem.duration ? e.currentItem.duration : '' + }, + options: { + external: true + } + }); + }); + } +} +exports["default"] = Module; + +/***/ }), + +/***/ "../modules/woocommerce/assets/js/editor/component.js": +/*!************************************************************!*\ + !*** ../modules/woocommerce/assets/js/editor/component.js ***! + \************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var hooks = _interopRequireWildcard(__webpack_require__(/*! ./hooks/ */ "../modules/woocommerce/assets/js/editor/hooks/index.js")); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +class Component extends $e.modules.ComponentBase { + getNamespace() { + return 'woocommerce'; + } + defaultHooks() { + return this.importHooks(hooks); + } +} +exports["default"] = Component; + +/***/ }), + +/***/ "../modules/woocommerce/assets/js/editor/hooks/data/create-widget-activate-settings-modal.js": +/*!***************************************************************************************************!*\ + !*** ../modules/woocommerce/assets/js/editor/hooks/data/create-widget-activate-settings-modal.js ***! + \***************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.WoocommerceCreateWidgetActivateSettingsModal = void 0; +class WoocommerceCreateWidgetActivateSettingsModal extends $e.modules.hookData.After { + getCommand() { + return 'document/elements/create'; + } + getId() { + return 'elementor-pro-woocommerce-create-widget-activate-settings-modal'; + } + getContainerType() { + return 'column'; + } + getConditions(args, container) { + return Object.prototype.hasOwnProperty.call(elementorPro.modules.woocommerce.pageSettingsWidgets, container.model.get('widgetType')); + } + apply(args, container) { + elementorPro.modules.woocommerce.onCreateWidget(container); + } +} +exports.WoocommerceCreateWidgetActivateSettingsModal = WoocommerceCreateWidgetActivateSettingsModal; + +/***/ }), + +/***/ "../modules/woocommerce/assets/js/editor/hooks/data/delete-widget-deactivate-settings-modal.js": +/*!*****************************************************************************************************!*\ + !*** ../modules/woocommerce/assets/js/editor/hooks/data/delete-widget-deactivate-settings-modal.js ***! + \*****************************************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.WoocommerceDeleteWidgetDeactivateSettingsModal = void 0; +class WoocommerceDeleteWidgetDeactivateSettingsModal extends $e.modules.hookData.After { + getCommand() { + return 'document/elements/delete'; + } + getId() { + return 'elementor-pro-woocommerce-delete-widget-deactivate-settings-modal'; + } + getContainerType() { + return 'widget'; + } + getConditions(args, container) { + return Object.prototype.hasOwnProperty.call(elementorPro.modules.woocommerce.pageSettingsWidgets, container.model.get('widgetType')); + } + apply(args, container) { + elementorPro.modules.woocommerce.onDeleteWidget(container); + } +} +exports.WoocommerceDeleteWidgetDeactivateSettingsModal = WoocommerceDeleteWidgetDeactivateSettingsModal; + +/***/ }), + +/***/ "../modules/woocommerce/assets/js/editor/hooks/data/index.js": +/*!*******************************************************************!*\ + !*** ../modules/woocommerce/assets/js/editor/hooks/data/index.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "WoocommerceCreateWidgetActivateSettingsModal", ({ + enumerable: true, + get: function () { + return _createWidgetActivateSettingsModal.WoocommerceCreateWidgetActivateSettingsModal; + } +})); +Object.defineProperty(exports, "WoocommerceDeleteWidgetDeactivateSettingsModal", ({ + enumerable: true, + get: function () { + return _deleteWidgetDeactivateSettingsModal.WoocommerceDeleteWidgetDeactivateSettingsModal; + } +})); +Object.defineProperty(exports, "WoocommerceNotices", ({ + enumerable: true, + get: function () { + return _notices.WoocommerceNotices; + } +})); +Object.defineProperty(exports, "WoocommerceSaveShowModal", ({ + enumerable: true, + get: function () { + return _saveShowModal.WoocommerceSaveShowModal; + } +})); +var _saveShowModal = __webpack_require__(/*! ./save-show-modal */ "../modules/woocommerce/assets/js/editor/hooks/data/save-show-modal.js"); +var _createWidgetActivateSettingsModal = __webpack_require__(/*! ./create-widget-activate-settings-modal */ "../modules/woocommerce/assets/js/editor/hooks/data/create-widget-activate-settings-modal.js"); +var _deleteWidgetDeactivateSettingsModal = __webpack_require__(/*! ./delete-widget-deactivate-settings-modal */ "../modules/woocommerce/assets/js/editor/hooks/data/delete-widget-deactivate-settings-modal.js"); +var _notices = __webpack_require__(/*! ./notices */ "../modules/woocommerce/assets/js/editor/hooks/data/notices.js"); + +/***/ }), + +/***/ "../modules/woocommerce/assets/js/editor/hooks/data/notices.js": +/*!*********************************************************************!*\ + !*** ../modules/woocommerce/assets/js/editor/hooks/data/notices.js ***! + \*********************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.WoocommerceNotices = void 0; +class WoocommerceNotices extends $e.modules.hookData.After { + getCommand() { + return 'document/elements/settings'; + } + getId() { + return 'woocommerce-notices'; + } + getConditions(args) { + return 'kit' === elementor.documents.getCurrent().config.type && Array.isArray(args.settings.woocommerce_notices_elements); + } + apply(args) { + const { + woocommerce + } = elementorPro.modules; + woocommerce.renderMockNotices(args.settings.woocommerce_notices_elements); + } +} +exports.WoocommerceNotices = WoocommerceNotices; + +/***/ }), + +/***/ "../modules/woocommerce/assets/js/editor/hooks/data/save-show-modal.js": +/*!*****************************************************************************!*\ + !*** ../modules/woocommerce/assets/js/editor/hooks/data/save-show-modal.js ***! + \*****************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.WoocommerceSaveShowModal = void 0; +class WoocommerceSaveShowModal extends $e.modules.hookData.After { + getCommand() { + return 'document/save/save'; + } + getId() { + return 'elementor-pro-woocommerce-save-show-modal'; + } + getConditions(args) { + return args.status && -1 !== ['private', 'publish'].indexOf(args.status); + } + apply() { + elementorPro.modules.woocommerce.onUpdateDocument(); + } +} +exports.WoocommerceSaveShowModal = WoocommerceSaveShowModal; + +/***/ }), + +/***/ "../modules/woocommerce/assets/js/editor/hooks/index.js": +/*!**************************************************************!*\ + !*** ../modules/woocommerce/assets/js/editor/hooks/index.js ***! + \**************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +var _data = __webpack_require__(/*! ./data/ */ "../modules/woocommerce/assets/js/editor/hooks/data/index.js"); +Object.keys(_data).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _data[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _data[key]; + } + }); +}); + +/***/ }), + +/***/ "../modules/woocommerce/assets/js/editor/module.js": +/*!*********************************************************!*\ + !*** ../modules/woocommerce/assets/js/editor/module.js ***! + \*********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +__webpack_require__(/*! core-js/modules/es.array.push.js */ "../node_modules/core-js/modules/es.array.push.js"); +var _component = _interopRequireDefault(__webpack_require__(/*! ./component */ "../modules/woocommerce/assets/js/editor/component.js")); +class WoocommerceModule extends elementorModules.editor.utils.Module { + constructor(...args) { + super(...args); + this.pageSettingsWidgets = { + 'woocommerce-checkout-page': { + headerMessage: __('Want to save this as your checkout page?', 'elementor-pro'), + message: __('Changes you make here will override your existing WooCommerce settings.', 'elementor-pro'), + confirmMessage: __('You\'ve updated your checkout page.', 'elementor-pro'), + cancelMessage: __('

Set up a checkout page


Without a checkout page, visitors can\'t complete transactions on your site. To set one up, go to Site Settings.', 'elementor-pro'), + failedMessage: __('

Sorry, something went wrong.


To define a checkout page for your site, head over to Site Settings.', 'elementor-pro'), + optionName: 'woocommerce_checkout_page_id', + woocommercePageName: 'checkout' + }, + 'woocommerce-cart': { + headerMessage: __('Want to save this as your cart page?', 'elementor-pro'), + message: __('Changes you make here will override your existing WooCommerce settings.', 'elementor-pro'), + confirmMessage: __('You\'ve updated your cart page.', 'elementor-pro'), + cancelMessage: __('

Set up a cart page


The cart page shows an order summary. To set one up, go to Site Settings.', 'elementor-pro'), + failedMessage: __('

Sorry, something went wrong.


To define a cart page for your site, head over to Site Settings.', 'elementor-pro'), + optionName: 'woocommerce_cart_page_id', + woocommercePageName: 'cart' + }, + 'woocommerce-my-account': { + headerMessage: __('Want to save this as your my account page?', 'elementor-pro'), + message: __('Changes you make here will override your existing WooCommerce settings.', 'elementor-pro'), + confirmMessage: __('You\'ve updated your my account page.', 'elementor-pro'), + cancelMessage: __('

Set up a My Account page


Without it, customers can\'t update their billing details, review past orders, etc. To set up My Account, go to Site Settings.', 'elementor-pro'), + failedMessage: __('

Sorry, something went wrong.


To define a my account page for your site, head over to Site Settings.', 'elementor-pro'), + optionName: 'woocommerce_myaccount_page_id', + woocommercePageName: 'myaccount' + }, + 'woocommerce-purchase-summary': { + headerMessage: __('Want to save this as your purchase summary page?', 'elementor-pro'), + message: __('Changes you make here will override your WooCommerce default purchase summary page.', 'elementor-pro'), + confirmMessage: __('You\'ve updated your summary page.', 'elementor-pro'), + cancelMessage: __('

Set up a purchase summary page


This page shows payment and order details. To set one up, go to Site Settings.', 'elementor-pro'), + failedMessage: __('

Sorry, something went wrong.


To define a purchase summary page for your site, head over to Site Settings.', 'elementor-pro'), + optionName: 'elementor_woocommerce_purchase_summary_page_id', + woocommercePageName: 'summary' + } + }; + this.createdPageSettingsWidgets = []; + } + addWooCommerceClassToLoopWrapper(LoopGridHandler) { + LoopGridHandler.$element.addClass('woocommerce'); + } + onElementorInit() { + elementor.hooks.addAction('editor/widgets/loop-grid/on-init', this.addWooCommerceClassToLoopWrapper); + } + onElementorFrontendInit() { + elementorFrontend.elements.$body.on('added_to_cart', (e, data) => { + // We do not want the page to reload in the Editor after we triggered the 'added_to_cart' event. + if (this.didManuallyTriggerAddToCartEvent(data)) { + return false; + } + }); + if ('loop-item' === elementor.documents.currentDocument.config.type && 'product' === elementor.documents.currentDocument.config.settings.settings.source) { + // Add the 'woocommerce' class to the Loop document wrapper only when editing a Product Loop Template in the + // theme builder. + elementor.on('document:loaded', () => { + elementor.$previewContents[0].querySelector('.e-loop-item').classList.add('woocommerce'); + }); + } + } + didManuallyTriggerAddToCartEvent(data = null) { + return data?.e_manually_triggered; + } + onElementorLoaded() { + this.component = $e.components.register(new _component.default({ + manager: this + })); + // WooCommerce Notice Settings. + const noticeSections = ['section_woocommerce_notices', 'woocommerce_message_notices', 'woocommerce_info_notices', 'woocommerce_error_notices']; + for (const section of noticeSections) { + elementor.channels.editor.on('kit_settings:' + section + ':activated', () => { + this.renderMockNotices(elementor.documents.getCurrent().container.settings.get('woocommerce_notices_elements')); + }); + } + + // Custom Empty Cart Template. + elementor.channels.editor.on('editor:widget:woocommerce-cart:section_additional_options:activated', () => { + this.onTemplateIdChange('additional_template_select'); + }); + + // Custom My Account Dashboard Template + elementor.channels.editor.on('editor:widget:woocommerce-my-account:section_additional_options:activated', () => { + this.onTemplateIdChange('customize_dashboard_select'); + }); + } + renderMockNotices(noticeElements) { + const noticesWrapper = elementor.$previewContents.find('.woocommerce-notices-wrapper'); + if (noticeElements.length <= 0) { + noticesWrapper.remove(); + return; + } + let noticesClass = ''; + for (const notice of noticeElements) { + const className = notice.replace('_', '-'); + noticesClass += 'e-' + className + '-notice '; + } + elementorFrontend.elements.$body.addClass(noticesClass.trim()); + noticesWrapper.addClass('elementor-loading'); + // Wait for the Ajax call to finish before the select2 can be changed again. + jQuery('.elementor-select2').attr('disabled', 'disabled'); + elementorPro.ajax.addRequest('woocommerce_mock_notices', { + data: { + notice_elements: noticeElements + }, + success(data) { + noticesWrapper.remove(); + elementor.$previewContents.find('.elementor-editor-preview').prepend(data); + noticesWrapper.removeClass('elementor-loading'); + // Enable the select2 again. + jQuery('.elementor-select2').removeAttr('disabled'); + } + }); + } + onTemplateIdChange(sectionActive) { + const editor = elementor.getPanelView().getCurrentPageView(), + model = editor.getOption('editedElementView').getEditModel(), + settingsModel = model.get('settings'), + templateID = settingsModel.get(sectionActive), + $editButton = editor.$el.find('.elementor-edit-template'); + if (!templateID) { + $editButton.addClass('e-control-tool-disabled').hide(); + } else { + const editUrl = ElementorConfig.home_url + '?p=' + templateID + '&elementor'; + $editButton.prop('href', editUrl).removeClass('e-control-tool-disabled').show(); + } + } + onCreateWidget(container) { + const widgetType = container.model.get('widgetType'); + if (undefined === this.createdPageSettingsWidgets[widgetType]) { + this.createdPageSettingsWidgets[widgetType] = 0; + } + this.createdPageSettingsWidgets[widgetType]++; + } + onDeleteWidget(container) { + const widgetType = container.model.get('widgetType'); + this.createdPageSettingsWidgets[widgetType]--; + if (!this.createdPageSettingsWidgets[widgetType]) { + delete this.createdPageSettingsWidgets[widgetType]; + } + } + onUpdateDocument() { + // On page Save trigger the 'added_to_cart' event so that the persistent cart cache can refresh so that the 'Preview' can be immediately updated without having to go and make a change in the Cart first. + elementorFrontend.elements.$body.trigger('added_to_cart', [{ + e_manually_triggered: true + }]); + const saveWoocommercePageSettingKeys = Object.keys(this.createdPageSettingsWidgets), + lastWidgetCreated = saveWoocommercePageSettingKeys[saveWoocommercePageSettingKeys.length - 1], + postId = elementor.documents.getCurrent().id; + if (1 !== saveWoocommercePageSettingKeys.length) { + return; + } + const lastWidgetCreatedOptions = this.pageSettingsWidgets[lastWidgetCreated]; + + // Bail if this page is already set as the corresponding WC page. + if (postId === elementorPro.config.woocommerce.woocommercePages[lastWidgetCreatedOptions.woocommercePageName]) { + return; + } + elementorCommon.dialogsManager.createWidget('confirm', { + id: 'elementor-woocommerce-save-pages', + className: 'e-global__confirm-add', + headerMessage: lastWidgetCreatedOptions.headerMessage, + message: lastWidgetCreatedOptions.message, + position: { + my: 'center center', + at: 'center center' + }, + strings: { + confirm: __('Save', 'elementor-pro'), + cancel: __('No thanks', 'elementor-pro') + }, + onConfirm: () => this.onConfirmModal(lastWidgetCreatedOptions), + onCancel: () => this.onCancelModal(lastWidgetCreatedOptions) + }).show(); + this.createdPageSettingsWidgets = []; + } + onConfirmModal(lastWidgetCreatedOptions) { + elementorPro.ajax.addRequest('woocommerce_update_page_option', { + data: { + option_name: lastWidgetCreatedOptions.optionName + }, + success: () => { + elementor.notifications.showToast({ + message: lastWidgetCreatedOptions.confirmMessage + }); + }, + error: () => this.showPagesSettingsToast(lastWidgetCreatedOptions.failedMessage) + }); + } + onCancelModal(lastWidgetCreatedOptions) { + this.showPagesSettingsToast(lastWidgetCreatedOptions.cancelMessage); + } + showPagesSettingsToast(message) { + const buttons = []; + elementor.notifications.initToast(); + buttons.push({ + name: 'take_me_there', + text: __('Take me there', 'elementor'), + callback: () => this.openSiteSettingsTab('settings-woocommerce') + }); + elementor.notifications.showToast({ + message, + buttons + }); + } + + // TODO: Add this as a reusable core function - to be able to open any settings tab. + openSiteSettingsTab(tabId = '', sectionId = '') { + const isWPPreviewMode = elementorCommon.elements.$body.hasClass('elementor-editor-preview'); + if (isWPPreviewMode) { + elementor.exitPreviewMode(); + } + const isInSettingsPanelActive = 'panel/global/menu' === elementor.documents.currentDocument.config.panel.default_route; + if (isInSettingsPanelActive) { + $e.run('panel/global/close'); + return; + } + $e.run('editor/documents/switch', { + id: elementor.config.kit_id, + mode: 'autosave' + }).then(() => { + if (tabId) { + $e.route('panel/global/' + tabId); + } + }) + // TODO: Replace with a standard routing solution once one is available + .then(() => { + if (sectionId) { + const sectionElement = jQuery('.elementor-control-' + sectionId); + if (sectionElement.length) { + sectionElement.trigger('click'); + } + } + }); + } +} +module.exports = WoocommerceModule; + +/***/ }), + +/***/ "@wordpress/i18n": +/*!**************************!*\ + !*** external "wp.i18n" ***! + \**************************/ +/***/ ((module) => { + +"use strict"; +module.exports = wp.i18n; + +/***/ }), + +/***/ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js": +/*!***********************************************************************!*\ + !*** ../node_modules/@babel/runtime/helpers/interopRequireDefault.js ***! + \***********************************************************************/ +/***/ ((module) => { + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + "default": obj + }; +} +module.exports = _interopRequireDefault, module.exports.__esModule = true, module.exports["default"] = module.exports; + +/***/ }), + +/***/ "../node_modules/core-js/internals/a-callable.js": +/*!*******************************************************!*\ + !*** ../node_modules/core-js/internals/a-callable.js ***! + \*******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var tryToString = __webpack_require__(/*! ../internals/try-to-string */ "../node_modules/core-js/internals/try-to-string.js"); + +var $TypeError = TypeError; + +// `Assert: IsCallable(argument) is true` +module.exports = function (argument) { + if (isCallable(argument)) return argument; + throw new $TypeError(tryToString(argument) + ' is not a function'); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/an-object.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/an-object.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var isObject = __webpack_require__(/*! ../internals/is-object */ "../node_modules/core-js/internals/is-object.js"); + +var $String = String; +var $TypeError = TypeError; + +// `Assert: Type(argument) is Object` +module.exports = function (argument) { + if (isObject(argument)) return argument; + throw new $TypeError($String(argument) + ' is not an object'); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/array-includes.js": +/*!***********************************************************!*\ + !*** ../node_modules/core-js/internals/array-includes.js ***! + \***********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var toIndexedObject = __webpack_require__(/*! ../internals/to-indexed-object */ "../node_modules/core-js/internals/to-indexed-object.js"); +var toAbsoluteIndex = __webpack_require__(/*! ../internals/to-absolute-index */ "../node_modules/core-js/internals/to-absolute-index.js"); +var lengthOfArrayLike = __webpack_require__(/*! ../internals/length-of-array-like */ "../node_modules/core-js/internals/length-of-array-like.js"); + +// `Array.prototype.{ indexOf, includes }` methods implementation +var createMethod = function (IS_INCLUDES) { + return function ($this, el, fromIndex) { + var O = toIndexedObject($this); + var length = lengthOfArrayLike(O); + var index = toAbsoluteIndex(fromIndex, length); + var value; + // Array#includes uses SameValueZero equality algorithm + // eslint-disable-next-line no-self-compare -- NaN check + if (IS_INCLUDES && el !== el) while (length > index) { + value = O[index++]; + // eslint-disable-next-line no-self-compare -- NaN check + if (value !== value) return true; + // Array#indexOf ignores holes, Array#includes - not + } else for (;length > index; index++) { + if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0; + } return !IS_INCLUDES && -1; + }; +}; + +module.exports = { + // `Array.prototype.includes` method + // https://tc39.es/ecma262/#sec-array.prototype.includes + includes: createMethod(true), + // `Array.prototype.indexOf` method + // https://tc39.es/ecma262/#sec-array.prototype.indexof + indexOf: createMethod(false) +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/array-set-length.js": +/*!*************************************************************!*\ + !*** ../node_modules/core-js/internals/array-set-length.js ***! + \*************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var isArray = __webpack_require__(/*! ../internals/is-array */ "../node_modules/core-js/internals/is-array.js"); + +var $TypeError = TypeError; +// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe +var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + +// Safari < 13 does not throw an error in this case +var SILENT_ON_NON_WRITABLE_LENGTH_SET = DESCRIPTORS && !function () { + // makes no sense without proper strict mode support + if (this !== undefined) return true; + try { + // eslint-disable-next-line es/no-object-defineproperty -- safe + Object.defineProperty([], 'length', { writable: false }).length = 1; + } catch (error) { + return error instanceof TypeError; + } +}(); + +module.exports = SILENT_ON_NON_WRITABLE_LENGTH_SET ? function (O, length) { + if (isArray(O) && !getOwnPropertyDescriptor(O, 'length').writable) { + throw new $TypeError('Cannot set read only .length'); + } return O.length = length; +} : function (O, length) { + return O.length = length; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/classof-raw.js": +/*!********************************************************!*\ + !*** ../node_modules/core-js/internals/classof-raw.js ***! + \********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); + +var toString = uncurryThis({}.toString); +var stringSlice = uncurryThis(''.slice); + +module.exports = function (it) { + return stringSlice(toString(it), 8, -1); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/copy-constructor-properties.js": +/*!************************************************************************!*\ + !*** ../node_modules/core-js/internals/copy-constructor-properties.js ***! + \************************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var ownKeys = __webpack_require__(/*! ../internals/own-keys */ "../node_modules/core-js/internals/own-keys.js"); +var getOwnPropertyDescriptorModule = __webpack_require__(/*! ../internals/object-get-own-property-descriptor */ "../node_modules/core-js/internals/object-get-own-property-descriptor.js"); +var definePropertyModule = __webpack_require__(/*! ../internals/object-define-property */ "../node_modules/core-js/internals/object-define-property.js"); + +module.exports = function (target, source, exceptions) { + var keys = ownKeys(source); + var defineProperty = definePropertyModule.f; + var getOwnPropertyDescriptor = getOwnPropertyDescriptorModule.f; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (!hasOwn(target, key) && !(exceptions && hasOwn(exceptions, key))) { + defineProperty(target, key, getOwnPropertyDescriptor(source, key)); + } + } +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/create-non-enumerable-property.js": +/*!***************************************************************************!*\ + !*** ../node_modules/core-js/internals/create-non-enumerable-property.js ***! + \***************************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var definePropertyModule = __webpack_require__(/*! ../internals/object-define-property */ "../node_modules/core-js/internals/object-define-property.js"); +var createPropertyDescriptor = __webpack_require__(/*! ../internals/create-property-descriptor */ "../node_modules/core-js/internals/create-property-descriptor.js"); + +module.exports = DESCRIPTORS ? function (object, key, value) { + return definePropertyModule.f(object, key, createPropertyDescriptor(1, value)); +} : function (object, key, value) { + object[key] = value; + return object; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/create-property-descriptor.js": +/*!***********************************************************************!*\ + !*** ../node_modules/core-js/internals/create-property-descriptor.js ***! + \***********************************************************************/ +/***/ ((module) => { + +"use strict"; + +module.exports = function (bitmap, value) { + return { + enumerable: !(bitmap & 1), + configurable: !(bitmap & 2), + writable: !(bitmap & 4), + value: value + }; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/define-built-in.js": +/*!************************************************************!*\ + !*** ../node_modules/core-js/internals/define-built-in.js ***! + \************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var definePropertyModule = __webpack_require__(/*! ../internals/object-define-property */ "../node_modules/core-js/internals/object-define-property.js"); +var makeBuiltIn = __webpack_require__(/*! ../internals/make-built-in */ "../node_modules/core-js/internals/make-built-in.js"); +var defineGlobalProperty = __webpack_require__(/*! ../internals/define-global-property */ "../node_modules/core-js/internals/define-global-property.js"); + +module.exports = function (O, key, value, options) { + if (!options) options = {}; + var simple = options.enumerable; + var name = options.name !== undefined ? options.name : key; + if (isCallable(value)) makeBuiltIn(value, name, options); + if (options.global) { + if (simple) O[key] = value; + else defineGlobalProperty(key, value); + } else { + try { + if (!options.unsafe) delete O[key]; + else if (O[key]) simple = true; + } catch (error) { /* empty */ } + if (simple) O[key] = value; + else definePropertyModule.f(O, key, { + value: value, + enumerable: false, + configurable: !options.nonConfigurable, + writable: !options.nonWritable + }); + } return O; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/define-global-property.js": +/*!*******************************************************************!*\ + !*** ../node_modules/core-js/internals/define-global-property.js ***! + \*******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); + +// eslint-disable-next-line es/no-object-defineproperty -- safe +var defineProperty = Object.defineProperty; + +module.exports = function (key, value) { + try { + defineProperty(global, key, { value: value, configurable: true, writable: true }); + } catch (error) { + global[key] = value; + } return value; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/descriptors.js": +/*!********************************************************!*\ + !*** ../node_modules/core-js/internals/descriptors.js ***! + \********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); + +// Detect IE8's incomplete defineProperty implementation +module.exports = !fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] !== 7; +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/document-all.js": +/*!*********************************************************!*\ + !*** ../node_modules/core-js/internals/document-all.js ***! + \*********************************************************/ +/***/ ((module) => { + +"use strict"; + +var documentAll = typeof document == 'object' && document.all; + +// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot +// eslint-disable-next-line unicorn/no-typeof-undefined -- required for testing +var IS_HTMLDDA = typeof documentAll == 'undefined' && documentAll !== undefined; + +module.exports = { + all: documentAll, + IS_HTMLDDA: IS_HTMLDDA +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/document-create-element.js": +/*!********************************************************************!*\ + !*** ../node_modules/core-js/internals/document-create-element.js ***! + \********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var isObject = __webpack_require__(/*! ../internals/is-object */ "../node_modules/core-js/internals/is-object.js"); + +var document = global.document; +// typeof document.createElement is 'object' in old IE +var EXISTS = isObject(document) && isObject(document.createElement); + +module.exports = function (it) { + return EXISTS ? document.createElement(it) : {}; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/does-not-exceed-safe-integer.js": +/*!*************************************************************************!*\ + !*** ../node_modules/core-js/internals/does-not-exceed-safe-integer.js ***! + \*************************************************************************/ +/***/ ((module) => { + +"use strict"; + +var $TypeError = TypeError; +var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF; // 2 ** 53 - 1 == 9007199254740991 + +module.exports = function (it) { + if (it > MAX_SAFE_INTEGER) throw $TypeError('Maximum allowed index exceeded'); + return it; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/engine-user-agent.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/engine-user-agent.js ***! + \**************************************************************/ +/***/ ((module) => { + +"use strict"; + +module.exports = typeof navigator != 'undefined' && String(navigator.userAgent) || ''; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/engine-v8-version.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/engine-v8-version.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var userAgent = __webpack_require__(/*! ../internals/engine-user-agent */ "../node_modules/core-js/internals/engine-user-agent.js"); + +var process = global.process; +var Deno = global.Deno; +var versions = process && process.versions || Deno && Deno.version; +var v8 = versions && versions.v8; +var match, version; + +if (v8) { + match = v8.split('.'); + // in old Chrome, versions of V8 isn't V8 = Chrome / 10 + // but their correct versions are not interesting for us + version = match[0] > 0 && match[0] < 4 ? 1 : +(match[0] + match[1]); +} + +// BrowserFS NodeJS `process` polyfill incorrectly set `.v8` to `0.0` +// so check `userAgent` even if `.v8` exists, but 0 +if (!version && userAgent) { + match = userAgent.match(/Edge\/(\d+)/); + if (!match || match[1] >= 74) { + match = userAgent.match(/Chrome\/(\d+)/); + if (match) version = +match[1]; + } +} + +module.exports = version; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/enum-bug-keys.js": +/*!**********************************************************!*\ + !*** ../node_modules/core-js/internals/enum-bug-keys.js ***! + \**********************************************************/ +/***/ ((module) => { + +"use strict"; + +// IE8- don't enum bug keys +module.exports = [ + 'constructor', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'toLocaleString', + 'toString', + 'valueOf' +]; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/export.js": +/*!***************************************************!*\ + !*** ../node_modules/core-js/internals/export.js ***! + \***************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var getOwnPropertyDescriptor = (__webpack_require__(/*! ../internals/object-get-own-property-descriptor */ "../node_modules/core-js/internals/object-get-own-property-descriptor.js").f); +var createNonEnumerableProperty = __webpack_require__(/*! ../internals/create-non-enumerable-property */ "../node_modules/core-js/internals/create-non-enumerable-property.js"); +var defineBuiltIn = __webpack_require__(/*! ../internals/define-built-in */ "../node_modules/core-js/internals/define-built-in.js"); +var defineGlobalProperty = __webpack_require__(/*! ../internals/define-global-property */ "../node_modules/core-js/internals/define-global-property.js"); +var copyConstructorProperties = __webpack_require__(/*! ../internals/copy-constructor-properties */ "../node_modules/core-js/internals/copy-constructor-properties.js"); +var isForced = __webpack_require__(/*! ../internals/is-forced */ "../node_modules/core-js/internals/is-forced.js"); + +/* + options.target - name of the target object + options.global - target is the global object + options.stat - export as static methods of target + options.proto - export as prototype methods of target + options.real - real prototype method for the `pure` version + options.forced - export even if the native feature is available + options.bind - bind methods to the target, required for the `pure` version + options.wrap - wrap constructors to preventing global pollution, required for the `pure` version + options.unsafe - use the simple assignment of property instead of delete + defineProperty + options.sham - add a flag to not completely full polyfills + options.enumerable - export as enumerable property + options.dontCallGetSet - prevent calling a getter on target + options.name - the .name of the function if it does not match the key +*/ +module.exports = function (options, source) { + var TARGET = options.target; + var GLOBAL = options.global; + var STATIC = options.stat; + var FORCED, target, key, targetProperty, sourceProperty, descriptor; + if (GLOBAL) { + target = global; + } else if (STATIC) { + target = global[TARGET] || defineGlobalProperty(TARGET, {}); + } else { + target = (global[TARGET] || {}).prototype; + } + if (target) for (key in source) { + sourceProperty = source[key]; + if (options.dontCallGetSet) { + descriptor = getOwnPropertyDescriptor(target, key); + targetProperty = descriptor && descriptor.value; + } else targetProperty = target[key]; + FORCED = isForced(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced); + // contained in target + if (!FORCED && targetProperty !== undefined) { + if (typeof sourceProperty == typeof targetProperty) continue; + copyConstructorProperties(sourceProperty, targetProperty); + } + // add a flag to not completely full polyfills + if (options.sham || (targetProperty && targetProperty.sham)) { + createNonEnumerableProperty(sourceProperty, 'sham', true); + } + defineBuiltIn(target, key, sourceProperty, options); + } +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/fails.js": +/*!**************************************************!*\ + !*** ../node_modules/core-js/internals/fails.js ***! + \**************************************************/ +/***/ ((module) => { + +"use strict"; + +module.exports = function (exec) { + try { + return !!exec(); + } catch (error) { + return true; + } +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/function-bind-native.js": +/*!*****************************************************************!*\ + !*** ../node_modules/core-js/internals/function-bind-native.js ***! + \*****************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); + +module.exports = !fails(function () { + // eslint-disable-next-line es/no-function-prototype-bind -- safe + var test = (function () { /* empty */ }).bind(); + // eslint-disable-next-line no-prototype-builtins -- safe + return typeof test != 'function' || test.hasOwnProperty('prototype'); +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/function-call.js": +/*!**********************************************************!*\ + !*** ../node_modules/core-js/internals/function-call.js ***! + \**********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var NATIVE_BIND = __webpack_require__(/*! ../internals/function-bind-native */ "../node_modules/core-js/internals/function-bind-native.js"); + +var call = Function.prototype.call; + +module.exports = NATIVE_BIND ? call.bind(call) : function () { + return call.apply(call, arguments); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/function-name.js": +/*!**********************************************************!*\ + !*** ../node_modules/core-js/internals/function-name.js ***! + \**********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); + +var FunctionPrototype = Function.prototype; +// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe +var getDescriptor = DESCRIPTORS && Object.getOwnPropertyDescriptor; + +var EXISTS = hasOwn(FunctionPrototype, 'name'); +// additional protection from minified / mangled / dropped function names +var PROPER = EXISTS && (function something() { /* empty */ }).name === 'something'; +var CONFIGURABLE = EXISTS && (!DESCRIPTORS || (DESCRIPTORS && getDescriptor(FunctionPrototype, 'name').configurable)); + +module.exports = { + EXISTS: EXISTS, + PROPER: PROPER, + CONFIGURABLE: CONFIGURABLE +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/function-uncurry-this.js": +/*!******************************************************************!*\ + !*** ../node_modules/core-js/internals/function-uncurry-this.js ***! + \******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var NATIVE_BIND = __webpack_require__(/*! ../internals/function-bind-native */ "../node_modules/core-js/internals/function-bind-native.js"); + +var FunctionPrototype = Function.prototype; +var call = FunctionPrototype.call; +var uncurryThisWithBind = NATIVE_BIND && FunctionPrototype.bind.bind(call, call); + +module.exports = NATIVE_BIND ? uncurryThisWithBind : function (fn) { + return function () { + return call.apply(fn, arguments); + }; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/get-built-in.js": +/*!*********************************************************!*\ + !*** ../node_modules/core-js/internals/get-built-in.js ***! + \*********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); + +var aFunction = function (argument) { + return isCallable(argument) ? argument : undefined; +}; + +module.exports = function (namespace, method) { + return arguments.length < 2 ? aFunction(global[namespace]) : global[namespace] && global[namespace][method]; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/get-method.js": +/*!*******************************************************!*\ + !*** ../node_modules/core-js/internals/get-method.js ***! + \*******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var aCallable = __webpack_require__(/*! ../internals/a-callable */ "../node_modules/core-js/internals/a-callable.js"); +var isNullOrUndefined = __webpack_require__(/*! ../internals/is-null-or-undefined */ "../node_modules/core-js/internals/is-null-or-undefined.js"); + +// `GetMethod` abstract operation +// https://tc39.es/ecma262/#sec-getmethod +module.exports = function (V, P) { + var func = V[P]; + return isNullOrUndefined(func) ? undefined : aCallable(func); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/global.js": +/*!***************************************************!*\ + !*** ../node_modules/core-js/internals/global.js ***! + \***************************************************/ +/***/ (function(module, __unused_webpack_exports, __webpack_require__) { + +"use strict"; + +var check = function (it) { + return it && it.Math === Math && it; +}; + +// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 +module.exports = + // eslint-disable-next-line es/no-global-this -- safe + check(typeof globalThis == 'object' && globalThis) || + check(typeof window == 'object' && window) || + // eslint-disable-next-line no-restricted-globals -- safe + check(typeof self == 'object' && self) || + check(typeof __webpack_require__.g == 'object' && __webpack_require__.g) || + // eslint-disable-next-line no-new-func -- fallback + (function () { return this; })() || this || Function('return this')(); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/has-own-property.js": +/*!*************************************************************!*\ + !*** ../node_modules/core-js/internals/has-own-property.js ***! + \*************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var toObject = __webpack_require__(/*! ../internals/to-object */ "../node_modules/core-js/internals/to-object.js"); + +var hasOwnProperty = uncurryThis({}.hasOwnProperty); + +// `HasOwnProperty` abstract operation +// https://tc39.es/ecma262/#sec-hasownproperty +// eslint-disable-next-line es/no-object-hasown -- safe +module.exports = Object.hasOwn || function hasOwn(it, key) { + return hasOwnProperty(toObject(it), key); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/hidden-keys.js": +/*!********************************************************!*\ + !*** ../node_modules/core-js/internals/hidden-keys.js ***! + \********************************************************/ +/***/ ((module) => { + +"use strict"; + +module.exports = {}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/ie8-dom-define.js": +/*!***********************************************************!*\ + !*** ../node_modules/core-js/internals/ie8-dom-define.js ***! + \***********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); +var createElement = __webpack_require__(/*! ../internals/document-create-element */ "../node_modules/core-js/internals/document-create-element.js"); + +// Thanks to IE8 for its funny defineProperty +module.exports = !DESCRIPTORS && !fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty(createElement('div'), 'a', { + get: function () { return 7; } + }).a !== 7; +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/indexed-object.js": +/*!***********************************************************!*\ + !*** ../node_modules/core-js/internals/indexed-object.js ***! + \***********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); +var classof = __webpack_require__(/*! ../internals/classof-raw */ "../node_modules/core-js/internals/classof-raw.js"); + +var $Object = Object; +var split = uncurryThis(''.split); + +// fallback for non-array-like ES3 and non-enumerable old V8 strings +module.exports = fails(function () { + // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346 + // eslint-disable-next-line no-prototype-builtins -- safe + return !$Object('z').propertyIsEnumerable(0); +}) ? function (it) { + return classof(it) === 'String' ? split(it, '') : $Object(it); +} : $Object; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/inspect-source.js": +/*!***********************************************************!*\ + !*** ../node_modules/core-js/internals/inspect-source.js ***! + \***********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var store = __webpack_require__(/*! ../internals/shared-store */ "../node_modules/core-js/internals/shared-store.js"); + +var functionToString = uncurryThis(Function.toString); + +// this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper +if (!isCallable(store.inspectSource)) { + store.inspectSource = function (it) { + return functionToString(it); + }; +} + +module.exports = store.inspectSource; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/internal-state.js": +/*!***********************************************************!*\ + !*** ../node_modules/core-js/internals/internal-state.js ***! + \***********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var NATIVE_WEAK_MAP = __webpack_require__(/*! ../internals/weak-map-basic-detection */ "../node_modules/core-js/internals/weak-map-basic-detection.js"); +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var isObject = __webpack_require__(/*! ../internals/is-object */ "../node_modules/core-js/internals/is-object.js"); +var createNonEnumerableProperty = __webpack_require__(/*! ../internals/create-non-enumerable-property */ "../node_modules/core-js/internals/create-non-enumerable-property.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var shared = __webpack_require__(/*! ../internals/shared-store */ "../node_modules/core-js/internals/shared-store.js"); +var sharedKey = __webpack_require__(/*! ../internals/shared-key */ "../node_modules/core-js/internals/shared-key.js"); +var hiddenKeys = __webpack_require__(/*! ../internals/hidden-keys */ "../node_modules/core-js/internals/hidden-keys.js"); + +var OBJECT_ALREADY_INITIALIZED = 'Object already initialized'; +var TypeError = global.TypeError; +var WeakMap = global.WeakMap; +var set, get, has; + +var enforce = function (it) { + return has(it) ? get(it) : set(it, {}); +}; + +var getterFor = function (TYPE) { + return function (it) { + var state; + if (!isObject(it) || (state = get(it)).type !== TYPE) { + throw new TypeError('Incompatible receiver, ' + TYPE + ' required'); + } return state; + }; +}; + +if (NATIVE_WEAK_MAP || shared.state) { + var store = shared.state || (shared.state = new WeakMap()); + /* eslint-disable no-self-assign -- prototype methods protection */ + store.get = store.get; + store.has = store.has; + store.set = store.set; + /* eslint-enable no-self-assign -- prototype methods protection */ + set = function (it, metadata) { + if (store.has(it)) throw new TypeError(OBJECT_ALREADY_INITIALIZED); + metadata.facade = it; + store.set(it, metadata); + return metadata; + }; + get = function (it) { + return store.get(it) || {}; + }; + has = function (it) { + return store.has(it); + }; +} else { + var STATE = sharedKey('state'); + hiddenKeys[STATE] = true; + set = function (it, metadata) { + if (hasOwn(it, STATE)) throw new TypeError(OBJECT_ALREADY_INITIALIZED); + metadata.facade = it; + createNonEnumerableProperty(it, STATE, metadata); + return metadata; + }; + get = function (it) { + return hasOwn(it, STATE) ? it[STATE] : {}; + }; + has = function (it) { + return hasOwn(it, STATE); + }; +} + +module.exports = { + set: set, + get: get, + has: has, + enforce: enforce, + getterFor: getterFor +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-array.js": +/*!*****************************************************!*\ + !*** ../node_modules/core-js/internals/is-array.js ***! + \*****************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var classof = __webpack_require__(/*! ../internals/classof-raw */ "../node_modules/core-js/internals/classof-raw.js"); + +// `IsArray` abstract operation +// https://tc39.es/ecma262/#sec-isarray +// eslint-disable-next-line es/no-array-isarray -- safe +module.exports = Array.isArray || function isArray(argument) { + return classof(argument) === 'Array'; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-callable.js": +/*!********************************************************!*\ + !*** ../node_modules/core-js/internals/is-callable.js ***! + \********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var $documentAll = __webpack_require__(/*! ../internals/document-all */ "../node_modules/core-js/internals/document-all.js"); + +var documentAll = $documentAll.all; + +// `IsCallable` abstract operation +// https://tc39.es/ecma262/#sec-iscallable +module.exports = $documentAll.IS_HTMLDDA ? function (argument) { + return typeof argument == 'function' || argument === documentAll; +} : function (argument) { + return typeof argument == 'function'; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-forced.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/is-forced.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); + +var replacement = /#|\.prototype\./; + +var isForced = function (feature, detection) { + var value = data[normalize(feature)]; + return value === POLYFILL ? true + : value === NATIVE ? false + : isCallable(detection) ? fails(detection) + : !!detection; +}; + +var normalize = isForced.normalize = function (string) { + return String(string).replace(replacement, '.').toLowerCase(); +}; + +var data = isForced.data = {}; +var NATIVE = isForced.NATIVE = 'N'; +var POLYFILL = isForced.POLYFILL = 'P'; + +module.exports = isForced; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-null-or-undefined.js": +/*!*****************************************************************!*\ + !*** ../node_modules/core-js/internals/is-null-or-undefined.js ***! + \*****************************************************************/ +/***/ ((module) => { + +"use strict"; + +// we can't use just `it == null` since of `document.all` special case +// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot-aec +module.exports = function (it) { + return it === null || it === undefined; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-object.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/is-object.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var $documentAll = __webpack_require__(/*! ../internals/document-all */ "../node_modules/core-js/internals/document-all.js"); + +var documentAll = $documentAll.all; + +module.exports = $documentAll.IS_HTMLDDA ? function (it) { + return typeof it == 'object' ? it !== null : isCallable(it) || it === documentAll; +} : function (it) { + return typeof it == 'object' ? it !== null : isCallable(it); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-pure.js": +/*!****************************************************!*\ + !*** ../node_modules/core-js/internals/is-pure.js ***! + \****************************************************/ +/***/ ((module) => { + +"use strict"; + +module.exports = false; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/is-symbol.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/is-symbol.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var getBuiltIn = __webpack_require__(/*! ../internals/get-built-in */ "../node_modules/core-js/internals/get-built-in.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var isPrototypeOf = __webpack_require__(/*! ../internals/object-is-prototype-of */ "../node_modules/core-js/internals/object-is-prototype-of.js"); +var USE_SYMBOL_AS_UID = __webpack_require__(/*! ../internals/use-symbol-as-uid */ "../node_modules/core-js/internals/use-symbol-as-uid.js"); + +var $Object = Object; + +module.exports = USE_SYMBOL_AS_UID ? function (it) { + return typeof it == 'symbol'; +} : function (it) { + var $Symbol = getBuiltIn('Symbol'); + return isCallable($Symbol) && isPrototypeOf($Symbol.prototype, $Object(it)); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/length-of-array-like.js": +/*!*****************************************************************!*\ + !*** ../node_modules/core-js/internals/length-of-array-like.js ***! + \*****************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var toLength = __webpack_require__(/*! ../internals/to-length */ "../node_modules/core-js/internals/to-length.js"); + +// `LengthOfArrayLike` abstract operation +// https://tc39.es/ecma262/#sec-lengthofarraylike +module.exports = function (obj) { + return toLength(obj.length); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/make-built-in.js": +/*!**********************************************************!*\ + !*** ../node_modules/core-js/internals/make-built-in.js ***! + \**********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var CONFIGURABLE_FUNCTION_NAME = (__webpack_require__(/*! ../internals/function-name */ "../node_modules/core-js/internals/function-name.js").CONFIGURABLE); +var inspectSource = __webpack_require__(/*! ../internals/inspect-source */ "../node_modules/core-js/internals/inspect-source.js"); +var InternalStateModule = __webpack_require__(/*! ../internals/internal-state */ "../node_modules/core-js/internals/internal-state.js"); + +var enforceInternalState = InternalStateModule.enforce; +var getInternalState = InternalStateModule.get; +var $String = String; +// eslint-disable-next-line es/no-object-defineproperty -- safe +var defineProperty = Object.defineProperty; +var stringSlice = uncurryThis(''.slice); +var replace = uncurryThis(''.replace); +var join = uncurryThis([].join); + +var CONFIGURABLE_LENGTH = DESCRIPTORS && !fails(function () { + return defineProperty(function () { /* empty */ }, 'length', { value: 8 }).length !== 8; +}); + +var TEMPLATE = String(String).split('String'); + +var makeBuiltIn = module.exports = function (value, name, options) { + if (stringSlice($String(name), 0, 7) === 'Symbol(') { + name = '[' + replace($String(name), /^Symbol\(([^)]*)\)/, '$1') + ']'; + } + if (options && options.getter) name = 'get ' + name; + if (options && options.setter) name = 'set ' + name; + if (!hasOwn(value, 'name') || (CONFIGURABLE_FUNCTION_NAME && value.name !== name)) { + if (DESCRIPTORS) defineProperty(value, 'name', { value: name, configurable: true }); + else value.name = name; + } + if (CONFIGURABLE_LENGTH && options && hasOwn(options, 'arity') && value.length !== options.arity) { + defineProperty(value, 'length', { value: options.arity }); + } + try { + if (options && hasOwn(options, 'constructor') && options.constructor) { + if (DESCRIPTORS) defineProperty(value, 'prototype', { writable: false }); + // in V8 ~ Chrome 53, prototypes of some methods, like `Array.prototype.values`, are non-writable + } else if (value.prototype) value.prototype = undefined; + } catch (error) { /* empty */ } + var state = enforceInternalState(value); + if (!hasOwn(state, 'source')) { + state.source = join(TEMPLATE, typeof name == 'string' ? name : ''); + } return value; +}; + +// add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative +// eslint-disable-next-line no-extend-native -- required +Function.prototype.toString = makeBuiltIn(function toString() { + return isCallable(this) && getInternalState(this).source || inspectSource(this); +}, 'toString'); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/math-trunc.js": +/*!*******************************************************!*\ + !*** ../node_modules/core-js/internals/math-trunc.js ***! + \*******************************************************/ +/***/ ((module) => { + +"use strict"; + +var ceil = Math.ceil; +var floor = Math.floor; + +// `Math.trunc` method +// https://tc39.es/ecma262/#sec-math.trunc +// eslint-disable-next-line es/no-math-trunc -- safe +module.exports = Math.trunc || function trunc(x) { + var n = +x; + return (n > 0 ? floor : ceil)(n); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-define-property.js": +/*!*******************************************************************!*\ + !*** ../node_modules/core-js/internals/object-define-property.js ***! + \*******************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var IE8_DOM_DEFINE = __webpack_require__(/*! ../internals/ie8-dom-define */ "../node_modules/core-js/internals/ie8-dom-define.js"); +var V8_PROTOTYPE_DEFINE_BUG = __webpack_require__(/*! ../internals/v8-prototype-define-bug */ "../node_modules/core-js/internals/v8-prototype-define-bug.js"); +var anObject = __webpack_require__(/*! ../internals/an-object */ "../node_modules/core-js/internals/an-object.js"); +var toPropertyKey = __webpack_require__(/*! ../internals/to-property-key */ "../node_modules/core-js/internals/to-property-key.js"); + +var $TypeError = TypeError; +// eslint-disable-next-line es/no-object-defineproperty -- safe +var $defineProperty = Object.defineProperty; +// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe +var $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; +var ENUMERABLE = 'enumerable'; +var CONFIGURABLE = 'configurable'; +var WRITABLE = 'writable'; + +// `Object.defineProperty` method +// https://tc39.es/ecma262/#sec-object.defineproperty +exports.f = DESCRIPTORS ? V8_PROTOTYPE_DEFINE_BUG ? function defineProperty(O, P, Attributes) { + anObject(O); + P = toPropertyKey(P); + anObject(Attributes); + if (typeof O === 'function' && P === 'prototype' && 'value' in Attributes && WRITABLE in Attributes && !Attributes[WRITABLE]) { + var current = $getOwnPropertyDescriptor(O, P); + if (current && current[WRITABLE]) { + O[P] = Attributes.value; + Attributes = { + configurable: CONFIGURABLE in Attributes ? Attributes[CONFIGURABLE] : current[CONFIGURABLE], + enumerable: ENUMERABLE in Attributes ? Attributes[ENUMERABLE] : current[ENUMERABLE], + writable: false + }; + } + } return $defineProperty(O, P, Attributes); +} : $defineProperty : function defineProperty(O, P, Attributes) { + anObject(O); + P = toPropertyKey(P); + anObject(Attributes); + if (IE8_DOM_DEFINE) try { + return $defineProperty(O, P, Attributes); + } catch (error) { /* empty */ } + if ('get' in Attributes || 'set' in Attributes) throw new $TypeError('Accessors not supported'); + if ('value' in Attributes) O[P] = Attributes.value; + return O; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-get-own-property-descriptor.js": +/*!*******************************************************************************!*\ + !*** ../node_modules/core-js/internals/object-get-own-property-descriptor.js ***! + \*******************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var call = __webpack_require__(/*! ../internals/function-call */ "../node_modules/core-js/internals/function-call.js"); +var propertyIsEnumerableModule = __webpack_require__(/*! ../internals/object-property-is-enumerable */ "../node_modules/core-js/internals/object-property-is-enumerable.js"); +var createPropertyDescriptor = __webpack_require__(/*! ../internals/create-property-descriptor */ "../node_modules/core-js/internals/create-property-descriptor.js"); +var toIndexedObject = __webpack_require__(/*! ../internals/to-indexed-object */ "../node_modules/core-js/internals/to-indexed-object.js"); +var toPropertyKey = __webpack_require__(/*! ../internals/to-property-key */ "../node_modules/core-js/internals/to-property-key.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var IE8_DOM_DEFINE = __webpack_require__(/*! ../internals/ie8-dom-define */ "../node_modules/core-js/internals/ie8-dom-define.js"); + +// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe +var $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + +// `Object.getOwnPropertyDescriptor` method +// https://tc39.es/ecma262/#sec-object.getownpropertydescriptor +exports.f = DESCRIPTORS ? $getOwnPropertyDescriptor : function getOwnPropertyDescriptor(O, P) { + O = toIndexedObject(O); + P = toPropertyKey(P); + if (IE8_DOM_DEFINE) try { + return $getOwnPropertyDescriptor(O, P); + } catch (error) { /* empty */ } + if (hasOwn(O, P)) return createPropertyDescriptor(!call(propertyIsEnumerableModule.f, O, P), O[P]); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-get-own-property-names.js": +/*!**************************************************************************!*\ + !*** ../node_modules/core-js/internals/object-get-own-property-names.js ***! + \**************************************************************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +"use strict"; + +var internalObjectKeys = __webpack_require__(/*! ../internals/object-keys-internal */ "../node_modules/core-js/internals/object-keys-internal.js"); +var enumBugKeys = __webpack_require__(/*! ../internals/enum-bug-keys */ "../node_modules/core-js/internals/enum-bug-keys.js"); + +var hiddenKeys = enumBugKeys.concat('length', 'prototype'); + +// `Object.getOwnPropertyNames` method +// https://tc39.es/ecma262/#sec-object.getownpropertynames +// eslint-disable-next-line es/no-object-getownpropertynames -- safe +exports.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) { + return internalObjectKeys(O, hiddenKeys); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-get-own-property-symbols.js": +/*!****************************************************************************!*\ + !*** ../node_modules/core-js/internals/object-get-own-property-symbols.js ***! + \****************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +// eslint-disable-next-line es/no-object-getownpropertysymbols -- safe +exports.f = Object.getOwnPropertySymbols; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-is-prototype-of.js": +/*!*******************************************************************!*\ + !*** ../node_modules/core-js/internals/object-is-prototype-of.js ***! + \*******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); + +module.exports = uncurryThis({}.isPrototypeOf); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-keys-internal.js": +/*!*****************************************************************!*\ + !*** ../node_modules/core-js/internals/object-keys-internal.js ***! + \*****************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var toIndexedObject = __webpack_require__(/*! ../internals/to-indexed-object */ "../node_modules/core-js/internals/to-indexed-object.js"); +var indexOf = (__webpack_require__(/*! ../internals/array-includes */ "../node_modules/core-js/internals/array-includes.js").indexOf); +var hiddenKeys = __webpack_require__(/*! ../internals/hidden-keys */ "../node_modules/core-js/internals/hidden-keys.js"); + +var push = uncurryThis([].push); + +module.exports = function (object, names) { + var O = toIndexedObject(object); + var i = 0; + var result = []; + var key; + for (key in O) !hasOwn(hiddenKeys, key) && hasOwn(O, key) && push(result, key); + // Don't enum bug & hidden keys + while (names.length > i) if (hasOwn(O, key = names[i++])) { + ~indexOf(result, key) || push(result, key); + } + return result; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/object-property-is-enumerable.js": +/*!**************************************************************************!*\ + !*** ../node_modules/core-js/internals/object-property-is-enumerable.js ***! + \**************************************************************************/ +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +var $propertyIsEnumerable = {}.propertyIsEnumerable; +// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe +var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + +// Nashorn ~ JDK8 bug +var NASHORN_BUG = getOwnPropertyDescriptor && !$propertyIsEnumerable.call({ 1: 2 }, 1); + +// `Object.prototype.propertyIsEnumerable` method implementation +// https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable +exports.f = NASHORN_BUG ? function propertyIsEnumerable(V) { + var descriptor = getOwnPropertyDescriptor(this, V); + return !!descriptor && descriptor.enumerable; +} : $propertyIsEnumerable; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/ordinary-to-primitive.js": +/*!******************************************************************!*\ + !*** ../node_modules/core-js/internals/ordinary-to-primitive.js ***! + \******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var call = __webpack_require__(/*! ../internals/function-call */ "../node_modules/core-js/internals/function-call.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); +var isObject = __webpack_require__(/*! ../internals/is-object */ "../node_modules/core-js/internals/is-object.js"); + +var $TypeError = TypeError; + +// `OrdinaryToPrimitive` abstract operation +// https://tc39.es/ecma262/#sec-ordinarytoprimitive +module.exports = function (input, pref) { + var fn, val; + if (pref === 'string' && isCallable(fn = input.toString) && !isObject(val = call(fn, input))) return val; + if (isCallable(fn = input.valueOf) && !isObject(val = call(fn, input))) return val; + if (pref !== 'string' && isCallable(fn = input.toString) && !isObject(val = call(fn, input))) return val; + throw new $TypeError("Can't convert object to primitive value"); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/own-keys.js": +/*!*****************************************************!*\ + !*** ../node_modules/core-js/internals/own-keys.js ***! + \*****************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var getBuiltIn = __webpack_require__(/*! ../internals/get-built-in */ "../node_modules/core-js/internals/get-built-in.js"); +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); +var getOwnPropertyNamesModule = __webpack_require__(/*! ../internals/object-get-own-property-names */ "../node_modules/core-js/internals/object-get-own-property-names.js"); +var getOwnPropertySymbolsModule = __webpack_require__(/*! ../internals/object-get-own-property-symbols */ "../node_modules/core-js/internals/object-get-own-property-symbols.js"); +var anObject = __webpack_require__(/*! ../internals/an-object */ "../node_modules/core-js/internals/an-object.js"); + +var concat = uncurryThis([].concat); + +// all object keys, includes non-enumerable and symbols +module.exports = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) { + var keys = getOwnPropertyNamesModule.f(anObject(it)); + var getOwnPropertySymbols = getOwnPropertySymbolsModule.f; + return getOwnPropertySymbols ? concat(keys, getOwnPropertySymbols(it)) : keys; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/require-object-coercible.js": +/*!*********************************************************************!*\ + !*** ../node_modules/core-js/internals/require-object-coercible.js ***! + \*********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var isNullOrUndefined = __webpack_require__(/*! ../internals/is-null-or-undefined */ "../node_modules/core-js/internals/is-null-or-undefined.js"); + +var $TypeError = TypeError; + +// `RequireObjectCoercible` abstract operation +// https://tc39.es/ecma262/#sec-requireobjectcoercible +module.exports = function (it) { + if (isNullOrUndefined(it)) throw new $TypeError("Can't call method on " + it); + return it; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/shared-key.js": +/*!*******************************************************!*\ + !*** ../node_modules/core-js/internals/shared-key.js ***! + \*******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var shared = __webpack_require__(/*! ../internals/shared */ "../node_modules/core-js/internals/shared.js"); +var uid = __webpack_require__(/*! ../internals/uid */ "../node_modules/core-js/internals/uid.js"); + +var keys = shared('keys'); + +module.exports = function (key) { + return keys[key] || (keys[key] = uid(key)); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/shared-store.js": +/*!*********************************************************!*\ + !*** ../node_modules/core-js/internals/shared-store.js ***! + \*********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var defineGlobalProperty = __webpack_require__(/*! ../internals/define-global-property */ "../node_modules/core-js/internals/define-global-property.js"); + +var SHARED = '__core-js_shared__'; +var store = global[SHARED] || defineGlobalProperty(SHARED, {}); + +module.exports = store; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/shared.js": +/*!***************************************************!*\ + !*** ../node_modules/core-js/internals/shared.js ***! + \***************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var IS_PURE = __webpack_require__(/*! ../internals/is-pure */ "../node_modules/core-js/internals/is-pure.js"); +var store = __webpack_require__(/*! ../internals/shared-store */ "../node_modules/core-js/internals/shared-store.js"); + +(module.exports = function (key, value) { + return store[key] || (store[key] = value !== undefined ? value : {}); +})('versions', []).push({ + version: '3.33.2', + mode: IS_PURE ? 'pure' : 'global', + copyright: '© 2014-2023 Denis Pushkarev (zloirock.ru)', + license: 'https://github.com/zloirock/core-js/blob/v3.33.2/LICENSE', + source: 'https://github.com/zloirock/core-js' +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/symbol-constructor-detection.js": +/*!*************************************************************************!*\ + !*** ../node_modules/core-js/internals/symbol-constructor-detection.js ***! + \*************************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +/* eslint-disable es/no-symbol -- required for testing */ +var V8_VERSION = __webpack_require__(/*! ../internals/engine-v8-version */ "../node_modules/core-js/internals/engine-v8-version.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); + +var $String = global.String; + +// eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing +module.exports = !!Object.getOwnPropertySymbols && !fails(function () { + var symbol = Symbol('symbol detection'); + // Chrome 38 Symbol has incorrect toString conversion + // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances + // nb: Do not call `String` directly to avoid this being optimized out to `symbol+''` which will, + // of course, fail. + return !$String(symbol) || !(Object(symbol) instanceof Symbol) || + // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances + !Symbol.sham && V8_VERSION && V8_VERSION < 41; +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-absolute-index.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/to-absolute-index.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var toIntegerOrInfinity = __webpack_require__(/*! ../internals/to-integer-or-infinity */ "../node_modules/core-js/internals/to-integer-or-infinity.js"); + +var max = Math.max; +var min = Math.min; + +// Helper for a popular repeating case of the spec: +// Let integer be ? ToInteger(index). +// If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length). +module.exports = function (index, length) { + var integer = toIntegerOrInfinity(index); + return integer < 0 ? max(integer + length, 0) : min(integer, length); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-indexed-object.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/to-indexed-object.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +// toObject with fallback for non-array-like ES3 strings +var IndexedObject = __webpack_require__(/*! ../internals/indexed-object */ "../node_modules/core-js/internals/indexed-object.js"); +var requireObjectCoercible = __webpack_require__(/*! ../internals/require-object-coercible */ "../node_modules/core-js/internals/require-object-coercible.js"); + +module.exports = function (it) { + return IndexedObject(requireObjectCoercible(it)); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-integer-or-infinity.js": +/*!*******************************************************************!*\ + !*** ../node_modules/core-js/internals/to-integer-or-infinity.js ***! + \*******************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var trunc = __webpack_require__(/*! ../internals/math-trunc */ "../node_modules/core-js/internals/math-trunc.js"); + +// `ToIntegerOrInfinity` abstract operation +// https://tc39.es/ecma262/#sec-tointegerorinfinity +module.exports = function (argument) { + var number = +argument; + // eslint-disable-next-line no-self-compare -- NaN check + return number !== number || number === 0 ? 0 : trunc(number); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-length.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/to-length.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var toIntegerOrInfinity = __webpack_require__(/*! ../internals/to-integer-or-infinity */ "../node_modules/core-js/internals/to-integer-or-infinity.js"); + +var min = Math.min; + +// `ToLength` abstract operation +// https://tc39.es/ecma262/#sec-tolength +module.exports = function (argument) { + return argument > 0 ? min(toIntegerOrInfinity(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991 +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-object.js": +/*!******************************************************!*\ + !*** ../node_modules/core-js/internals/to-object.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var requireObjectCoercible = __webpack_require__(/*! ../internals/require-object-coercible */ "../node_modules/core-js/internals/require-object-coercible.js"); + +var $Object = Object; + +// `ToObject` abstract operation +// https://tc39.es/ecma262/#sec-toobject +module.exports = function (argument) { + return $Object(requireObjectCoercible(argument)); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-primitive.js": +/*!*********************************************************!*\ + !*** ../node_modules/core-js/internals/to-primitive.js ***! + \*********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var call = __webpack_require__(/*! ../internals/function-call */ "../node_modules/core-js/internals/function-call.js"); +var isObject = __webpack_require__(/*! ../internals/is-object */ "../node_modules/core-js/internals/is-object.js"); +var isSymbol = __webpack_require__(/*! ../internals/is-symbol */ "../node_modules/core-js/internals/is-symbol.js"); +var getMethod = __webpack_require__(/*! ../internals/get-method */ "../node_modules/core-js/internals/get-method.js"); +var ordinaryToPrimitive = __webpack_require__(/*! ../internals/ordinary-to-primitive */ "../node_modules/core-js/internals/ordinary-to-primitive.js"); +var wellKnownSymbol = __webpack_require__(/*! ../internals/well-known-symbol */ "../node_modules/core-js/internals/well-known-symbol.js"); + +var $TypeError = TypeError; +var TO_PRIMITIVE = wellKnownSymbol('toPrimitive'); + +// `ToPrimitive` abstract operation +// https://tc39.es/ecma262/#sec-toprimitive +module.exports = function (input, pref) { + if (!isObject(input) || isSymbol(input)) return input; + var exoticToPrim = getMethod(input, TO_PRIMITIVE); + var result; + if (exoticToPrim) { + if (pref === undefined) pref = 'default'; + result = call(exoticToPrim, input, pref); + if (!isObject(result) || isSymbol(result)) return result; + throw new $TypeError("Can't convert object to primitive value"); + } + if (pref === undefined) pref = 'number'; + return ordinaryToPrimitive(input, pref); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/to-property-key.js": +/*!************************************************************!*\ + !*** ../node_modules/core-js/internals/to-property-key.js ***! + \************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var toPrimitive = __webpack_require__(/*! ../internals/to-primitive */ "../node_modules/core-js/internals/to-primitive.js"); +var isSymbol = __webpack_require__(/*! ../internals/is-symbol */ "../node_modules/core-js/internals/is-symbol.js"); + +// `ToPropertyKey` abstract operation +// https://tc39.es/ecma262/#sec-topropertykey +module.exports = function (argument) { + var key = toPrimitive(argument, 'string'); + return isSymbol(key) ? key : key + ''; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/try-to-string.js": +/*!**********************************************************!*\ + !*** ../node_modules/core-js/internals/try-to-string.js ***! + \**********************************************************/ +/***/ ((module) => { + +"use strict"; + +var $String = String; + +module.exports = function (argument) { + try { + return $String(argument); + } catch (error) { + return 'Object'; + } +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/uid.js": +/*!************************************************!*\ + !*** ../node_modules/core-js/internals/uid.js ***! + \************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var uncurryThis = __webpack_require__(/*! ../internals/function-uncurry-this */ "../node_modules/core-js/internals/function-uncurry-this.js"); + +var id = 0; +var postfix = Math.random(); +var toString = uncurryThis(1.0.toString); + +module.exports = function (key) { + return 'Symbol(' + (key === undefined ? '' : key) + ')_' + toString(++id + postfix, 36); +}; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/use-symbol-as-uid.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/use-symbol-as-uid.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +/* eslint-disable es/no-symbol -- required for testing */ +var NATIVE_SYMBOL = __webpack_require__(/*! ../internals/symbol-constructor-detection */ "../node_modules/core-js/internals/symbol-constructor-detection.js"); + +module.exports = NATIVE_SYMBOL + && !Symbol.sham + && typeof Symbol.iterator == 'symbol'; + + +/***/ }), + +/***/ "../node_modules/core-js/internals/v8-prototype-define-bug.js": +/*!********************************************************************!*\ + !*** ../node_modules/core-js/internals/v8-prototype-define-bug.js ***! + \********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var DESCRIPTORS = __webpack_require__(/*! ../internals/descriptors */ "../node_modules/core-js/internals/descriptors.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); + +// V8 ~ Chrome 36- +// https://bugs.chromium.org/p/v8/issues/detail?id=3334 +module.exports = DESCRIPTORS && fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty(function () { /* empty */ }, 'prototype', { + value: 42, + writable: false + }).prototype !== 42; +}); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/weak-map-basic-detection.js": +/*!*********************************************************************!*\ + !*** ../node_modules/core-js/internals/weak-map-basic-detection.js ***! + \*********************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var isCallable = __webpack_require__(/*! ../internals/is-callable */ "../node_modules/core-js/internals/is-callable.js"); + +var WeakMap = global.WeakMap; + +module.exports = isCallable(WeakMap) && /native code/.test(String(WeakMap)); + + +/***/ }), + +/***/ "../node_modules/core-js/internals/well-known-symbol.js": +/*!**************************************************************!*\ + !*** ../node_modules/core-js/internals/well-known-symbol.js ***! + \**************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var global = __webpack_require__(/*! ../internals/global */ "../node_modules/core-js/internals/global.js"); +var shared = __webpack_require__(/*! ../internals/shared */ "../node_modules/core-js/internals/shared.js"); +var hasOwn = __webpack_require__(/*! ../internals/has-own-property */ "../node_modules/core-js/internals/has-own-property.js"); +var uid = __webpack_require__(/*! ../internals/uid */ "../node_modules/core-js/internals/uid.js"); +var NATIVE_SYMBOL = __webpack_require__(/*! ../internals/symbol-constructor-detection */ "../node_modules/core-js/internals/symbol-constructor-detection.js"); +var USE_SYMBOL_AS_UID = __webpack_require__(/*! ../internals/use-symbol-as-uid */ "../node_modules/core-js/internals/use-symbol-as-uid.js"); + +var Symbol = global.Symbol; +var WellKnownSymbolsStore = shared('wks'); +var createWellKnownSymbol = USE_SYMBOL_AS_UID ? Symbol['for'] || Symbol : Symbol && Symbol.withoutSetter || uid; + +module.exports = function (name) { + if (!hasOwn(WellKnownSymbolsStore, name)) { + WellKnownSymbolsStore[name] = NATIVE_SYMBOL && hasOwn(Symbol, name) + ? Symbol[name] + : createWellKnownSymbol('Symbol.' + name); + } return WellKnownSymbolsStore[name]; +}; + + +/***/ }), + +/***/ "../node_modules/core-js/modules/es.array.push.js": +/*!********************************************************!*\ + !*** ../node_modules/core-js/modules/es.array.push.js ***! + \********************************************************/ +/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + +var $ = __webpack_require__(/*! ../internals/export */ "../node_modules/core-js/internals/export.js"); +var toObject = __webpack_require__(/*! ../internals/to-object */ "../node_modules/core-js/internals/to-object.js"); +var lengthOfArrayLike = __webpack_require__(/*! ../internals/length-of-array-like */ "../node_modules/core-js/internals/length-of-array-like.js"); +var setArrayLength = __webpack_require__(/*! ../internals/array-set-length */ "../node_modules/core-js/internals/array-set-length.js"); +var doesNotExceedSafeInteger = __webpack_require__(/*! ../internals/does-not-exceed-safe-integer */ "../node_modules/core-js/internals/does-not-exceed-safe-integer.js"); +var fails = __webpack_require__(/*! ../internals/fails */ "../node_modules/core-js/internals/fails.js"); + +var INCORRECT_TO_LENGTH = fails(function () { + return [].push.call({ length: 0x100000000 }, 1) !== 4294967297; +}); + +// V8 and Safari <= 15.4, FF < 23 throws InternalError +// https://bugs.chromium.org/p/v8/issues/detail?id=12681 +var properErrorOnNonWritableLength = function () { + try { + // eslint-disable-next-line es/no-object-defineproperty -- safe + Object.defineProperty([], 'length', { writable: false }).push(); + } catch (error) { + return error instanceof TypeError; + } +}; + +var FORCED = INCORRECT_TO_LENGTH || !properErrorOnNonWritableLength(); + +// `Array.prototype.push` method +// https://tc39.es/ecma262/#sec-array.prototype.push +$({ target: 'Array', proto: true, arity: 1, forced: FORCED }, { + // eslint-disable-next-line no-unused-vars -- required for `.length` + push: function push(item) { + var O = toObject(this); + var len = lengthOfArrayLike(O); + var argCount = arguments.length; + doesNotExceedSafeInteger(len + argCount); + for (var i = 0; i < argCount; i++) { + O[len] = arguments[i]; + len++; + } + setArrayLength(O, len); + return len; + } +}); + + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = __webpack_modules__; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/ensure chunk */ +/******/ (() => { +/******/ __webpack_require__.f = {}; +/******/ // This file contains only the entry chunk. +/******/ // The chunk loading function for additional chunks +/******/ __webpack_require__.e = (chunkId) => { +/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => { +/******/ __webpack_require__.f[key](chunkId, promises); +/******/ return promises; +/******/ }, [])); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/get javascript chunk filename */ +/******/ (() => { +/******/ // This function allow to reference async chunks +/******/ __webpack_require__.u = (chunkId) => { +/******/ // return url for filenames not based on template +/******/ if (chunkId === "mega-menu-editor") return "" + chunkId + ".bbef3f7412481cbce555.bundle.js"; +/******/ if (chunkId === "nested-carousel-editor") return "" + chunkId + ".2fdc278ce6bc9f6ec2e0.bundle.js"; +/******/ if (chunkId === "loop-filter-editor") return "" + chunkId + ".d1bae86a5ed21c0e9981.bundle.js"; +/******/ if (chunkId === "modules_query-control_assets_js_editor_template-query-control_js") return "4abfbfd970d6f7680bc7.bundle.js"; +/******/ // return url for filenames based on template +/******/ return undefined; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/global */ +/******/ (() => { +/******/ __webpack_require__.g = (function() { +/******/ if (typeof globalThis === 'object') return globalThis; +/******/ try { +/******/ return this || new Function('return this')(); +/******/ } catch (e) { +/******/ if (typeof window === 'object') return window; +/******/ } +/******/ })(); +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/load script */ +/******/ (() => { +/******/ var inProgress = {}; +/******/ var dataWebpackPrefix = "elementor-pro:"; +/******/ // loadScript function to load a script via script tag +/******/ __webpack_require__.l = (url, done, key, chunkId) => { +/******/ if(inProgress[url]) { inProgress[url].push(done); return; } +/******/ var script, needAttach; +/******/ if(key !== undefined) { +/******/ var scripts = document.getElementsByTagName("script"); +/******/ for(var i = 0; i < scripts.length; i++) { +/******/ var s = scripts[i]; +/******/ if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; } +/******/ } +/******/ } +/******/ if(!script) { +/******/ needAttach = true; +/******/ script = document.createElement('script'); +/******/ +/******/ script.charset = 'utf-8'; +/******/ script.timeout = 120; +/******/ if (__webpack_require__.nc) { +/******/ script.setAttribute("nonce", __webpack_require__.nc); +/******/ } +/******/ script.setAttribute("data-webpack", dataWebpackPrefix + key); +/******/ +/******/ script.src = url; +/******/ } +/******/ inProgress[url] = [done]; +/******/ var onScriptComplete = (prev, event) => { +/******/ // avoid mem leaks in IE. +/******/ script.onerror = script.onload = null; +/******/ clearTimeout(timeout); +/******/ var doneFns = inProgress[url]; +/******/ delete inProgress[url]; +/******/ script.parentNode && script.parentNode.removeChild(script); +/******/ doneFns && doneFns.forEach((fn) => (fn(event))); +/******/ if(prev) return prev(event); +/******/ } +/******/ var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000); +/******/ script.onerror = onScriptComplete.bind(null, script.onerror); +/******/ script.onload = onScriptComplete.bind(null, script.onload); +/******/ needAttach && document.head.appendChild(script); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/publicPath */ +/******/ (() => { +/******/ var scriptUrl; +/******/ if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + ""; +/******/ var document = __webpack_require__.g.document; +/******/ if (!scriptUrl && document) { +/******/ if (document.currentScript) +/******/ scriptUrl = document.currentScript.src; +/******/ if (!scriptUrl) { +/******/ var scripts = document.getElementsByTagName("script"); +/******/ if(scripts.length) { +/******/ var i = scripts.length - 1; +/******/ while (i > -1 && !scriptUrl) scriptUrl = scripts[i--].src; +/******/ } +/******/ } +/******/ } +/******/ // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration +/******/ // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic. +/******/ if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser"); +/******/ scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/"); +/******/ __webpack_require__.p = scriptUrl; +/******/ })(); +/******/ +/******/ /* webpack/runtime/jsonp chunk loading */ +/******/ (() => { +/******/ // no baseURI +/******/ +/******/ // object to store loaded and loading chunks +/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched +/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded +/******/ var installedChunks = { +/******/ "editor": 0 +/******/ }; +/******/ +/******/ __webpack_require__.f.j = (chunkId, promises) => { +/******/ // JSONP chunk loading for javascript +/******/ var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined; +/******/ if(installedChunkData !== 0) { // 0 means "already installed". +/******/ +/******/ // a Promise means "currently loading". +/******/ if(installedChunkData) { +/******/ promises.push(installedChunkData[2]); +/******/ } else { +/******/ if(true) { // all chunks have JS +/******/ // setup Promise in chunk cache +/******/ var promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject])); +/******/ promises.push(installedChunkData[2] = promise); +/******/ +/******/ // start chunk loading +/******/ var url = __webpack_require__.p + __webpack_require__.u(chunkId); +/******/ // create error before stack unwound to get useful stacktrace later +/******/ var error = new Error(); +/******/ var loadingEnded = (event) => { +/******/ if(__webpack_require__.o(installedChunks, chunkId)) { +/******/ installedChunkData = installedChunks[chunkId]; +/******/ if(installedChunkData !== 0) installedChunks[chunkId] = undefined; +/******/ if(installedChunkData) { +/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type); +/******/ var realSrc = event && event.target && event.target.src; +/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'; +/******/ error.name = 'ChunkLoadError'; +/******/ error.type = errorType; +/******/ error.request = realSrc; +/******/ installedChunkData[1](error); +/******/ } +/******/ } +/******/ }; +/******/ __webpack_require__.l(url, loadingEnded, "chunk-" + chunkId, chunkId); +/******/ } +/******/ } +/******/ } +/******/ }; +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded +/******/ +/******/ // no HMR +/******/ +/******/ // no HMR manifest +/******/ +/******/ // no on chunks loaded +/******/ +/******/ // install a JSONP callback for chunk loading +/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => { +/******/ var [chunkIds, moreModules, runtime] = data; +/******/ // add "moreModules" to the modules object, +/******/ // then flag all "chunkIds" as loaded and fire callback +/******/ var moduleId, chunkId, i = 0; +/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) { +/******/ for(moduleId in moreModules) { +/******/ if(__webpack_require__.o(moreModules, moduleId)) { +/******/ __webpack_require__.m[moduleId] = moreModules[moduleId]; +/******/ } +/******/ } +/******/ if(runtime) var result = runtime(__webpack_require__); +/******/ } +/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data); +/******/ for(;i < chunkIds.length; i++) { +/******/ chunkId = chunkIds[i]; +/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) { +/******/ installedChunks[chunkId][0](); +/******/ } +/******/ installedChunks[chunkId] = 0; +/******/ } +/******/ +/******/ } +/******/ +/******/ var chunkLoadingGlobal = self["webpackChunkelementor_pro"] = self["webpackChunkelementor_pro"] || []; +/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0)); +/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal)); +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be in strict mode. +(() => { +"use strict"; +/*!*****************************************!*\ + !*** ../assets/dev/js/editor/editor.js ***! + \*****************************************/ +/* provided dependency */ var __ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n")["__"]; + + +var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); +var _editor = _interopRequireDefault(__webpack_require__(/*! ../../../../modules/custom-css/assets/js/editor/editor */ "../modules/custom-css/assets/js/editor/editor.js")); +var _editor2 = _interopRequireDefault(__webpack_require__(/*! ../../../../modules/motion-fx/assets/js/editor/editor */ "../modules/motion-fx/assets/js/editor/editor.js")); +var _module = _interopRequireDefault(__webpack_require__(/*! modules/popup/assets/js/editor/module */ "../modules/popup/assets/js/editor/module.js")); +var _module2 = _interopRequireDefault(__webpack_require__(/*! modules/global-widget/assets/js/editor/module */ "../modules/global-widget/assets/js/editor/module.js")); +var _module3 = _interopRequireDefault(__webpack_require__(/*! modules/theme-builder/assets/js/editor/module */ "../modules/theme-builder/assets/js/editor/module.js")); +var _module4 = _interopRequireDefault(__webpack_require__(/*! modules/forms/assets/js/editor/module */ "../modules/forms/assets/js/editor/module.js")); +var _module5 = _interopRequireDefault(__webpack_require__(/*! modules/screenshots/assets/js/editor/module */ "../modules/screenshots/assets/js/editor/module.js")); +var _editor3 = _interopRequireDefault(__webpack_require__(/*! ../../../../core/app/modules/site-editor/assets/js/editor */ "../core/app/modules/site-editor/assets/js/editor.js")); +var _module6 = _interopRequireDefault(__webpack_require__(/*! modules/video-playlist/assets/js/editor/module */ "../modules/video-playlist/assets/js/editor/module.js")); +var _module7 = _interopRequireDefault(__webpack_require__(/*! modules/woocommerce/assets/js/editor/module */ "../modules/woocommerce/assets/js/editor/module.js")); +var _module8 = _interopRequireDefault(__webpack_require__(/*! modules/scroll-snap/assets/js/editor/module */ "../modules/scroll-snap/assets/js/editor/module.js")); +var _module9 = _interopRequireDefault(__webpack_require__(/*! modules/payments/assets/js/editor/module */ "../modules/payments/assets/js/editor/module.js")); +var _module10 = _interopRequireDefault(__webpack_require__(/*! modules/loop-builder/assets/js/editor/module */ "../modules/loop-builder/assets/js/editor/module.js")); +var _tiers = __webpack_require__(/*! ./tiers */ "../assets/dev/js/editor/tiers.js"); +var _notesContextMenu = _interopRequireDefault(__webpack_require__(/*! modules/notes/assets/js/notes-context-menu */ "../modules/notes/assets/js/notes-context-menu.js")); +var _module11 = _interopRequireDefault(__webpack_require__(/*! modules/page-transitions/assets/js/editor/module */ "../modules/page-transitions/assets/js/editor/module.js")); +var ElementorPro = Marionette.Application.extend({ + config: {}, + modules: {}, + initModules() { + var QueryControl = __webpack_require__(/*! modules/query-control/assets/js/editor */ "../modules/query-control/assets/js/editor.js"), + Library = __webpack_require__(/*! modules/library/assets/js/editor */ "../modules/library/assets/js/editor.js"), + FlipBox = __webpack_require__(/*! modules/flip-box/assets/js/editor/editor */ "../modules/flip-box/assets/js/editor/editor.js"), + ShareButtons = __webpack_require__(/*! modules/share-buttons/assets/js/editor/editor */ "../modules/share-buttons/assets/js/editor/editor.js"), + AssetsManager = __webpack_require__(/*! modules/assets-manager/assets/js/editor/editor */ "../modules/assets-manager/assets/js/editor/editor.js"), + ThemeElements = __webpack_require__(/*! modules/theme-elements/assets/js/editor/editor */ "../modules/theme-elements/assets/js/editor/editor.js"); + this.modules = { + queryControl: new QueryControl(), + forms: new _module4.default(), + library: new Library(), + customCSS: new _editor.default(), + globalWidget: new _module2.default(), + flipBox: new FlipBox(), + motionFX: new _editor2.default(), + shareButtons: new ShareButtons(), + assetsManager: new AssetsManager(), + themeElements: new ThemeElements(), + themeBuilder: new _module3.default(), + siteEditor: new _editor3.default(), + screenshots: new _module5.default(), + woocommerce: new _module7.default(), + stripe: new _module9.default(), + loopBuilder: new _module10.default(), + pageTransitions: new _module11.default(), + // Popup is depended on Theme Builder. + popup: new _module.default(), + videoPlaylistModule: new _module6.default(), + ScrollSnapModule: new _module8.default() + }; + if (elementorCommon.config.experimentalFeatures['mega-menu']) { + elementorCommon.elements.$window.on('elementor/nested-element-type-loaded', async () => { + // The module should be loaded only when `nestedElements` is available. + this.modules.megaMenu = new (await __webpack_require__.e(/*! import() | mega-menu-editor */ "mega-menu-editor").then(__webpack_require__.bind(__webpack_require__, /*! modules/mega-menu/assets/js/editor/module */ "../modules/mega-menu/assets/js/editor/module.js"))).default(); + }); + } + if (elementorCommon.config.experimentalFeatures['nested-elements']) { + elementorCommon.elements.$window.on('elementor/nested-element-type-loaded', async () => { + // The module should be loaded only when `nestedElements` is available. + this.modules.nestedCarousel = new (await __webpack_require__.e(/*! import() | nested-carousel-editor */ "nested-carousel-editor").then(__webpack_require__.bind(__webpack_require__, /*! modules/nested-carousel/assets/js/editor/module */ "../modules/nested-carousel/assets/js/editor/module.js"))).default(); + }); + } + if (elementorCommon.config.experimentalFeatures['taxonomy-filter']) { + __webpack_require__.e(/*! import() | loop-filter-editor */ "loop-filter-editor").then(__webpack_require__.bind(__webpack_require__, /*! modules/loop-filter/assets/js/editor/module */ "../modules/loop-filter/assets/js/editor/module.js")).then(({ + default: LoopFilter + }) => { + this.modules.loopFilter = new LoopFilter(); + }); + } + }, + ajax: { + prepareArgs(args) { + args[0] = 'pro_' + args[0]; + return args; + }, + send() { + return elementorCommon.ajax.send.apply(elementorCommon.ajax, this.prepareArgs(arguments)); + }, + addRequest() { + return elementorCommon.ajax.addRequest.apply(elementorCommon.ajax, this.prepareArgs(arguments)); + } + }, + translate(stringKey, templateArgs) { + return elementorCommon.translate(stringKey, null, templateArgs, this.config.i18n); + }, + onStart() { + this.config = elementorProEditorConfig; + this.initModules(); + jQuery(window).on('elementor:init', () => this.onElementorInit()).on('elementor/connect/success/editor-pro-activate', this.onActivateSuccess); + }, + onElementorInit() { + elementor.on('preview:loaded', () => this.onElementorPreviewLoaded()); + elementorPro.libraryRemoveGetProButtons(); + elementorCommon.debug.addURLToWatch('elementor-pro/assets'); + if (elementorPro.config.should_show_promotion) { + new _notesContextMenu.default(); + } + }, + onElementorPreviewLoaded() { + elementor.$preview[0].contentWindow.elementorPro = this; + }, + libraryRemoveGetProButtons() { + elementor.hooks.addFilter('elementor/editor/template-library/template/action-button', (viewID, templateData) => { + if (!templateData.accessTier || !elementor.config?.library_connect?.current_access_tier) { + // BC support. + return this.getProButtonViewIdBC(viewID, templateData); + } + const isProTemplate = templateData.accessTier !== elementor.config.library_connect.base_access_tier; + if (isProTemplate && !elementorPro.config.isActive) { + return '#tmpl-elementor-pro-template-library-activate-license-button'; + } + const canInsert = (0, _tiers.isTierAtLeast)(elementor.config.library_connect.current_access_tier, templateData.accessTier); + return canInsert ? '#tmpl-elementor-template-library-insert-button' : viewID; + }); + }, + getProButtonViewIdBC(viewID, templateData) { + // When the template should be at least "pro" and the license is not active. + if (templateData.accessLevel > 0 && !elementorPro.config.isActive) { + return '#tmpl-elementor-pro-template-library-activate-license-button'; + } + + // When the template access levels is greater than the current license access level it should + // return the "core" view template which is by default "go pro" or "go expert" button. + if (templateData.accessLevel > elementor.config.library_connect.current_access_level) { + return viewID; + } + + // When the current license can insert the template. + return '#tmpl-elementor-template-library-insert-button'; + }, + onActivateSuccess() { + // Hide notice. + elementor.noticeBar.onCloseClick(); + + // Mark site connect for insert templates connect screen. + elementor.config.library_connect.is_connected = true; + + // Mark pro is active - for `this.libraryRemoveGetProButtons`. + elementorPro.config.isActive = true; + elementor.notifications.showToast({ + message: __('Connected Successfully', 'elementor') + }); + } +}); +window.elementorPro = new ElementorPro(); +elementorPro.start(); +})(); + +/******/ })() +; +//# sourceMappingURL=editor.js.map \ No newline at end of file diff --git a/assets/js/editor.min.js b/assets/js/editor.min.js new file mode 100644 index 0000000..564e7fb --- /dev/null +++ b/assets/js/editor.min.js @@ -0,0 +1,2 @@ +/*! elementor-pro - v3.21.0 - 30-04-2024 */ +(()=>{var e,t,o={8200:e=>{"use strict";e.exports=elementorModules.editor.utils.Module.extend({elementType:null,__construct(e){this.elementType=e,this.addEditorListener()},updateOptions(e,t){const o=this.getEditorControlView(e);o&&(this.getEditorControlModel(e).set("options",t),o.render())},addEditorListener(){var e=this;if(e.onElementChange){var t="change";"global"!==e.elementType&&(t+=":"+e.elementType),elementor.channels.editor.on(t,(function(t,o){e.onElementChange(t.model.get("name"),t,o)}))}},addControlSpinner(e){const t=this.getEditorControlView(e).$el;if(t.find(".elementor-control-spinner").length)return;t.find(":input").attr("disabled",!0),t.find(".elementor-control-title").after(' ')},removeControlSpinner(e){const t=this.getEditorControlView(e).$el;t.find(":input").attr("disabled",!1),t.find(".elementor-control-spinner").remove()},addControlError(e,t,o=".elementor-control-content"){const r=this.getEditorControlView(e).$el;r.find(".e-control-error").length&&r.find(".e-control-error").remove(),r.find(o).first().after(`${t}`)},removeControlError(e){this.getEditorControlView(e).$el.find(".e-control-error").remove()},resetControlIndicators(e){this.removeControlSpinner(e),this.removeControlError(e)},addSectionListener(e,t){const o=this;elementor.channels.editor.on("section:activated",(function(r,n){var i=n.getOption("editedElementView").getEditModel(),a=i.get("elType"),s=arguments;"widget"===a&&(a=i.get("widgetType")),o.elementType===a&&e===r&&setTimeout((function(){t.apply(o,s)}),10)}))}})},8865:e=>{"use strict";e.exports=elementorModules.editor.views.ControlsStack.extend({activeTab:"content",activeSection:"settings",initialize(){this.collection=new Backbone.Collection(_.values(this.options.controls))},filter(e){if("section"===e.get("type"))return!0;var t=e.get("section");return!t||t===this.activeSection},childViewOptions(){return{elementSettingsModel:this.model}}})},945:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isTierAtLeast=t.TIERS_PRIORITY=void 0;const o=t.TIERS_PRIORITY=Object.freeze(["free","essential","essential-oct2023","advanced","expert","agency"]);t.isTierAtLeast=(e,t)=>{const r=o.indexOf(e),n=o.indexOf(t);return-1!==r&&-1!==n&&r>=n}},5030:(e,t,o)=>{"use strict";var r=o(8003).__;Object.defineProperty(t,"__esModule",{value:!0}),t.SAVE_CONTEXT=t.EDIT_CONTEXT=void 0,t.createElement=createElement,t.default=function addDocumentHandle({element:e,id:t,title:o=r("Template","elementor-pro")},l=a,d=null,u=null){if(a===l){if(!t||!e)throw Error("`id` and `element` are required.");if(function isCurrentlyEditing(e){return e.classList.contains(i)}(e)||function hasHandle(e){return!!e.querySelector(`:scope > .${n}`)}(e))return}const c=function createHandleElement({title:e,onClick:t},o,i=null){const l=["header","footer"].includes(i?.dataset.elementorType)?"%s":r("Edit %s","elementor-pro"),d=createElement({tag:"div",classNames:[`${n}__inner`],children:[createElement({tag:"i",classNames:[getHandleIcon(o)]}),createElement({tag:"div",classNames:[`${a===o?n:s}__title`],children:[document.createTextNode(a===o?l.replace("%s",e):r("Save %s","elementor-pro").replace("%s",e))]})]}),u=[n];a!==o&&u.push(s);const c=createElement({tag:"div",classNames:u,children:[d]});return c.addEventListener("click",t),c}({title:o,onClick:()=>async function onDocumentClick(e,t,o=null,r=null){a===t?(window.top.$e.internal("panel/state-loading"),await window.top.$e.run("editor/documents/switch",{id:parseInt(e),onClose:o,selector:r}),window.top.$e.internal("panel/state-ready")):(elementorCommon.api.internal("panel/state-loading"),elementorCommon.api.run("editor/documents/switch",{id:elementor.config.initial_document.id,mode:"save",shouldScroll:!1,selector:r}).finally((()=>elementorCommon.api.internal("panel/state-ready"))))}(t,l,d,u)},l,e);e.prepend(c),a===l&&(e.dataset.editableElementorDocument=t)},o(3517);const n="elementor-document-handle",i="elementor-edit-mode",a=t.EDIT_CONTEXT="edit",s="elementor-document-save-back-handle",l=t.SAVE_CONTEXT="save";function getHandleIcon(e){let t="eicon-edit";return l===e&&(t=elementorFrontend.config.is_rtl?"eicon-arrow-right":"eicon-arrow-left"),t}function createElement({tag:e,classNames:t=[],children:o=[]}){const r=document.createElement(e);return r.classList.add(...t),o.forEach((e=>r.appendChild(e))),r}},1471:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.ConditionsConfig=void 0;class ConditionsConfig extends $e.modules.CommandData{static signature="site-editor/conditions-config";static getEndpointFormat(){return"site-editor/conditions-config/{id}"}}t.ConditionsConfig=ConditionsConfig;t.default=ConditionsConfig},6338:(e,t,o)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"ConditionsConfig",{enumerable:!0,get:function(){return n.ConditionsConfig}}),Object.defineProperty(t,"Templates",{enumerable:!0,get:function(){return r.Templates}}),Object.defineProperty(t,"TemplatesConditions",{enumerable:!0,get:function(){return i.TemplatesConditions}}),Object.defineProperty(t,"TemplatesConditionsConflicts",{enumerable:!0,get:function(){return a.TemplatesConditionsConflicts}});var r=o(4837),n=o(1471),i=o(801),a=o(7691)},7691:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.TemplatesConditionsConflicts=void 0;class TemplatesConditionsConflicts extends $e.modules.CommandData{static signature="site-editor/templates-conditions-conflicts";static getEndpointFormat(){return`${TemplatesConditionsConflicts.signature}/{id}`}}t.TemplatesConditionsConflicts=TemplatesConditionsConflicts;t.default=TemplatesConditionsConflicts},801:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.TemplatesConditions=void 0;class TemplatesConditions extends $e.modules.CommandData{static signature="site-editor/templates-conditions";static getEndpointFormat(){return"site-editor/templates-conditions/{id}"}}t.TemplatesConditions=TemplatesConditions;t.default=TemplatesConditions},4837:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Templates=void 0;class Templates extends $e.modules.CommandData{static signature="site-editor/templates";static getEndpointFormat(){return"site-editor/templates/{id}"}}t.Templates=Templates;t.default=Templates},1717:(e,t,o)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function _interopRequireWildcard(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var o=_getRequireWildcardCache(t);if(o&&o.has(e))return o.get(e);var r={__proto__:null},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var a=n?Object.getOwnPropertyDescriptor(e,i):null;a&&(a.get||a.set)?Object.defineProperty(r,i,a):r[i]=e[i]}return r.default=e,o&&o.set(e,r),r}(o(6338));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,o=new WeakMap;return(_getRequireWildcardCache=function(e){return e?o:t})(e)}class Component extends $e.modules.ComponentBase{static namespace="site-editor";getNamespace(){return this.constructor.namespace}defaultData(){return this.importCommands(r)}}t.default=Component},3118:(e,t,o)=>{"use strict";var r=o(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=r(o(1717)),i=o(6338);class Module extends elementorModules.editor.utils.Module{onElementorInit(){elementor.documents.getCurrent().config.support_site_editor&&($e.components.register(new n.default),$e.data.deleteCache($e.components.get(n.default.namespace),i.Templates.signature))}}t.default=Module},716:(e,t,o)=>{"use strict";e.exports=elementorModules.editor.utils.Module.extend({onElementorInit(){var e=o(2211);this.assets={font:new e}}})},2211:(e,t,o)=>{"use strict";o(3517),e.exports=elementorModules.Module.extend({_enqueuedFonts:[],_enqueuedTypekit:!1,onFontChange(e,t){"custom"!==e&&"typekit"!==e||-1===this._enqueuedFonts.indexOf(t)&&("typekit"===e&&this._enqueuedTypekit||this.getCustomFont(e,t))},getCustomFont(e,t){elementorPro.ajax.addRequest("assets_manager_panel_action_data",{unique_id:"font_"+e+t,data:{service:"font",type:e,font:t},success(e){e.font_face&&elementor.$previewContents.find("style").last().after('"),e.font_url&&elementor.$previewContents.find("link").last().after('')}}),this._enqueuedFonts.push(t),"typekit"===e&&(this._enqueuedTypekit=!0)},onInit(){elementor.channels.editor.on("font:insertion",this.onFontChange.bind(this))}})},9206:(e,t,o)=>{"use strict";var r=o(8003).__;Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;class _default extends elementorModules.editor.utils.Module{addCustomCss(e,t){if(!t)return;const o=t.model,r=o.get("settings").get("custom_css");let n=".elementor-element.elementor-element-"+o.get("id");return"document"===o.get("elType")&&(n=elementor.config.document.settings.cssWrapperSelector),r&&(e+=r.replace(/selector/g,n)),e}onElementorInit(){elementor.hooks.addFilter("editor/style/styleText",this.addCustomCss),elementor.on("navigator:init",this.onNavigatorInit.bind(this))}onNavigatorInit(){elementor.navigator.indicators.customCSS={icon:"code-bold",settingKeys:["custom_css"],title:r("Custom CSS","elementor-pro"),section:"section_custom_css"}}}t.default=_default},5795:e=>{"use strict";e.exports=elementorModules.editor.utils.Module.extend({onElementorInit(){elementor.channels.editor.on("section:activated",this.onSectionActivated)},onSectionActivated(e,t){var o=t.getOption("editedElementView");if("flip-box"===o.model.get("widgetType")){var r=-1!==["section_side_b_content","section_style_b"].indexOf(e);o.$el.toggleClass("elementor-flip-box--flipped",r);var n=o.$el.find(".elementor-flip-box__back");r&&n.css("transition","none"),r||setTimeout((function(){n.css("transition","")}),10)}}})},8804:(e,t,o)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function _interopRequireWildcard(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var o=_getRequireWildcardCache(t);if(o&&o.has(e))return o.get(e);var r={__proto__:null},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var a=n?Object.getOwnPropertyDescriptor(e,i):null;a&&(a.get||a.set)?Object.defineProperty(r,i,a):r[i]=e[i]}return r.default=e,o&&o.set(e,r),r}(o(8019));function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,o=new WeakMap;return(_getRequireWildcardCache=function(e){return e?o:t})(e)}class Component extends $e.modules.ComponentBase{getNamespace(){return"forms"}defaultHooks(){return this.importHooks(r)}}t.default=Component},6002:(e,t,o)=>{"use strict";var r=o(8003).__;e.exports=elementor.modules.controls.Repeater.extend({onBeforeRender(){this.$el.hide()},updateMap(e){var t=this,o={};t.collection.each((function(e){o[e.get("remote_id")]=e.get("local_id")})),t.collection.reset(),_.each(e,(function(e){var r={remote_id:e.remote_id,remote_label:e.remote_label,remote_type:e.remote_type?e.remote_type:"",remote_required:!!e.remote_required&&e.remote_required,local_id:o[e.remote_id]?o[e.remote_id]:""};t.collection.add(r)})),t.render()},onRender(){elementor.modules.controls.Base.prototype.onRender.apply(this,arguments);var e=this;e.children.each((function(t){var o=t.children.last(),n={"":"- "+r("None","elementor")+" -"},i=t.model.get("remote_label");t.model.get("remote_required")&&(i+='*'),_.each(e.elementSettingsModel.get("form_fields").models,(function(e,o){var r=t.model.get("remote_type");"text"!==r&&r!==e.get("field_type")||(n[e.get("custom_id")]=e.get("field_label")||"Field #"+(o+1))})),o.model.set("label",i),o.model.set("options",n),o.render(),t.$el.find(".elementor-repeater-row-tools").hide(),t.$el.find(".elementor-repeater-row-controls").removeClass("elementor-repeater-row-controls").find(".elementor-control").css({paddingBottom:0})})),e.$el.find(".elementor-button-wrapper").remove(),e.children.length&&e.$el.show()}})},4566:(e,t,o)=>{"use strict";var r=o(3203)(o(7810));e.exports=class extends elementor.modules.controls.Repeater{className(){let e=super.className();return e+=" elementor-control-type-repeater",e}getChildView(){return r.default}initialize(...e){super.initialize(...e);const t=this.container.settings.get("form_fields");this.listenTo(t,"change",(e=>this.onFormFieldChange(e))).listenTo(t,"remove",(e=>this.onFormFieldRemove(e)))}getFirstChild(){return this.children.findByModel(this.collection.models[0])}lockFirstStep(){const e=this.getFirstChild();if("step"!==e.model.get("field_type"))return;11)return;const o=this.getFirstChild();if(1===t.length)return o.toggleTools(!0),void o.toggleFieldTypeControl(!0);o.toggleSort(!0)}onAddChild(e){super.onAddChild(e),"step"===e.model.get("field_type")&&(this.lockFirstStep(),e.toggleStepField(!0))}}},7810:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;class _default extends elementor.modules.controls.RepeaterRow{toggleFieldTypeControl(e){const t=this.collection.findWhere({name:"field_type"});this.children.findByModel(t).$el.toggle(e)}toggleStepField(e){this.$el.toggleClass("elementor-repeater-row--form-step",e)}toggleTools(e){this.ui.removeButton.add(this.ui.duplicateButton).toggle(e)}}t.default=_default},5781:e=>{"use strict";e.exports=elementorModules.editor.utils.Module.extend({renderField(e,t,o,r){var n=_.escape(t.css_classes),i="",a="",s="";return t.required&&(i="required"),t.acceptance_text&&(a='"),t.checked_by_default&&(s=' checked="checked"'),'
"+a+"
"},onInit(){elementor.hooks.addFilter("elementor_pro/forms/content_template/field/acceptance",this.renderField,10,4)}})},3463:e=>{"use strict";e.exports=elementorModules.editor.utils.Module.extend({renderField(e,t,o,r){var n=_.escape(t.css_classes),i="",a="",s="",l="";return t.required&&(i="required"),t.min_date&&(a=' min="'+t.min_date+'"'),t.max_date&&(s=' max="'+t.max_date+'"'),t.placeholder&&(l=' placeholder="'+t.placeholder+'"'),"yes"===t.use_native_date&&(n+=" elementor-use-native"),'"},onInit(){elementor.hooks.addFilter("elementor_pro/forms/content_template/field/date",this.renderField,10,4)}})},6326:e=>{"use strict";e.exports=elementorModules.editor.utils.Module.extend({renderField(e,t,o,r){var n=_.escape(t.css_classes),i="",a="";return t.required&&(i="required"),t.placeholder&&(a=' placeholder="'+t.placeholder+'"'),n="elementor-field-textual "+n,''},onInit(){elementor.hooks.addFilter("elementor_pro/forms/content_template/field/tel",this.renderField,10,4)}})},9395:e=>{"use strict";e.exports=elementorModules.editor.utils.Module.extend({renderField(e,t,o,r){var n=_.escape(t.css_classes),i="",a="";return t.required&&(i="required"),t.placeholder&&(a=' placeholder="'+t.placeholder+'"'),"yes"===t.use_native_time&&(n+=" elementor-use-native"),'"},onInit(){elementor.hooks.addFilter("elementor_pro/forms/content_template/field/time",this.renderField,10,4)}})},4146:e=>{"use strict";e.exports=elementorModules.editor.utils.Module.extend({renderField(e,t,o,r){var n=_.escape(t.css_classes),i="",a="",s="form_field_";return t.required&&(i="required"),t.allow_multiple_upload&&(a=' multiple="multiple"',s+="[]"),'"},onInit(){elementor.hooks.addFilter("elementor_pro/forms/content_template/field/upload",this.renderField,10,4)}})},3186:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.FormFieldsSanitizeCustomId=void 0;class FormFieldsSanitizeCustomId extends $e.modules.hookData.Dependency{ID_SANITIZE_FILTER=/[^\w]/g;getCommand(){return"document/elements/settings"}getId(){return"elementor-pro-forms-fields-sanitize-custom-id"}getContainerType(){return"repeater"}getConditions(e){return void 0!==e.settings.custom_id}apply(e){const{containers:t=[e.container],settings:o}=e,{custom_id:r}=o;return!r.match(this.ID_SANITIZE_FILTER)||(t.forEach((e=>{const t=e.panel.getControlView("form_fields").children.findByModel(e.settings).children.find((e=>"custom_id"===e.model.get("name")));t.render(),t.$el.find("input").trigger("focus")})),!1)}}t.FormFieldsSanitizeCustomId=FormFieldsSanitizeCustomId;t.default=FormFieldsSanitizeCustomId},5389:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.FormFieldsSetCustomId=void 0;class FormFieldsSetCustomId extends $e.modules.hookData.After{getCommand(){return"document/repeater/insert"}getId(){return"elementor-pro-forms-fields-set-custom-id"}getContainerType(){return"widget"}getConditions(e){return"form_fields"===e.name}apply(e,t){const{containers:o=[e.container]}=e,r=$e.commands.isCurrentFirstTrace("document/repeater/duplicate");return o.forEach((e=>{const o=e.repeaters.form_fields.children.find((e=>!!e&&t.get("_id")===e.id));!r&&o.settings.get("custom_id")||$e.run("document/elements/settings",{container:o,settings:{custom_id:"field_"+o.id},options:{external:!0}})})),!0}}t.FormFieldsSetCustomId=FormFieldsSetCustomId;t.default=FormFieldsSetCustomId},9469:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.FormFieldsAddFirstStep=void 0;class FormFieldsAddFirstStep extends $e.modules.hookData.After{getCommand(){return"document/elements/settings"}getId(){return"elementor-pro-forms-fields-first-step"}getContainerType(){return"repeater"}getConditions(e){const{containers:t=[e.container]}=e;return"form"===t[0].parent.parent.model.get("widgetType")&&"step"===e.settings.field_type}apply(e){const{containers:t=[e.container]}=e;return t.forEach((e=>{"step"!==e.parent.children[0].settings.get("field_type")&&$e.run("document/repeater/insert",{container:e.parent.parent,name:"form_fields",model:{field_type:"step"},options:{at:0,external:!0}})})),!0}}t.FormFieldsAddFirstStep=FormFieldsAddFirstStep;t.default=FormFieldsAddFirstStep},2650:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.FormSanitizeId=void 0;class FormSanitizeId extends $e.modules.hookData.Dependency{ID_SANITIZE_FILTER=/[^\w]/g;getCommand(){return"document/elements/settings"}getId(){return"elementor-pro-forms-sanitize-id"}getContainerType(){return"widget"}getConditions(e){return void 0!==e.settings.form_id}apply(e){const{container:t,settings:o}=e,{form_id:r}=o;if(r.match(this.ID_SANITIZE_FILTER)){const e=t.panel.getControlView("form_id");return e.render(),e.$el.find("input").trigger("focus"),!1}return!0}}t.FormSanitizeId=FormSanitizeId;t.default=FormSanitizeId},3267:(e,t,o)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"FormFieldsAddFirstStep",{enumerable:!0,get:function(){return i.FormFieldsAddFirstStep}}),Object.defineProperty(t,"FormFieldsSanitizeCustomId",{enumerable:!0,get:function(){return r.FormFieldsSanitizeCustomId}}),Object.defineProperty(t,"FormFieldsSetCustomId",{enumerable:!0,get:function(){return n.FormFieldsSetCustomId}}),Object.defineProperty(t,"FormSanitizeId",{enumerable:!0,get:function(){return a.FormSanitizeId}});var r=o(3186),n=o(5389),i=o(9469),a=o(2650)},8019:(e,t,o)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=o(3267);Object.keys(r).forEach((function(e){"default"!==e&&"__esModule"!==e&&(e in t&&t[e]===r[e]||Object.defineProperty(t,e,{enumerable:!0,get:function(){return r[e]}}))}));var n=o(200);Object.keys(n).forEach((function(e){"default"!==e&&"__esModule"!==e&&(e in t&&t[e]===n[e]||Object.defineProperty(t,e,{enumerable:!0,get:function(){return n[e]}}))}))},7234:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.FormFieldsUpdateShortCode=void 0;class FormFieldsUpdateShortCode extends $e.modules.hookUI.After{getCommand(){return"document/elements/settings"}getId(){return"elementor-pro-forms-fields-update-shortcode"}getContainerType(){return"repeater"}getConditions(e){return!(!$e.routes.isPartOf("panel/editor")||void 0===e.settings.custom_id)}apply(e){const{containers:t=[e.container]}=e;t.forEach((e=>{e.panel.getControlView("form_fields").children.find((t=>e.id===t.model.get("_id"))).children.find((e=>"shortcode"===e.model.get("name"))).render()}))}}t.FormFieldsUpdateShortCode=FormFieldsUpdateShortCode;t.default=FormFieldsUpdateShortCode},200:(e,t,o)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"FormFieldsUpdateShortCode",{enumerable:!0,get:function(){return r.FormFieldsUpdateShortCode}});var r=o(7234)},4552:(e,t,o)=>{"use strict";var r=o(8003).__;o(3517);var n=o(2220);e.exports=n.extend({fields:{},getName:()=>"activecampaign",onElementChange(e){switch(e){case"activecampaign_api_credentials_source":case"activecampaign_api_key":case"activecampaign_api_url":this.onApiUpdate();break;case"activecampaign_list":this.onListUpdate()}},onApiUpdate(){const e=this,t=e.getEditorControlView("activecampaign_api_key"),o=e.getEditorControlView("activecampaign_api_url"),r=e.getEditorControlView("activecampaign_api_credentials_source");if("default"!==r.getControlValue()&&(""===t.getControlValue()||""===o.getControlValue()))return e.updateOptions("activecampaign_list",[]),void e.getEditorControlView("activecampaign_list").setValue("");e.addControlSpinner("activecampaign_list");const n=this.getCacheKey({controls:[r.getControlValue(),o.getControlValue(),t.getControlValue()]});e.getActiveCampaignCache("lists","activecampaign_list",n).done((function(t){e.updateOptions("activecampaign_list",t.lists),e.fields=t.fields}))},onListUpdate(){this.updateFieldsMapping()},updateFieldsMapping(){if(this.getEditorControlView("activecampaign_list").getControlValue()){var e=[{remote_label:r("Email","elementor"),remote_type:"email",remote_id:"email",remote_required:!0},{remote_label:r("First Name","elementor"),remote_type:"text",remote_id:"first_name",remote_required:!1},{remote_label:r("Last Name","elementor"),remote_type:"text",remote_id:"last_name",remote_required:!1},{remote_label:r("Phone","elementor"),remote_type:"text",remote_id:"phone",remote_required:!1},{remote_label:r("Organization name","elementor"),remote_type:"text",remote_id:"orgname",remote_required:!1}];for(var t in this.fields)Object.prototype.hasOwnProperty.call(this.fields,t)&&e.push(this.fields[t]);this.getEditorControlView("activecampaign_fields_map").updateMap(e)}},getActiveCampaignCache(e,t,o,r){if(_.has(this.cache[e],o)){var n={};return n[e]=this.cache[e][o],jQuery.Deferred().resolve(n)}return r=_.extend({},r,{service:"activecampaign",activecampaign_action:t,api_key:this.getEditorControlView("activecampaign_api_key").getControlValue(),api_url:this.getEditorControlView("activecampaign_api_url").getControlValue(),api_cred:this.getEditorControlView("activecampaign_api_credentials_source").getControlValue()}),this.fetchCache(e,o,r)}})},2220:(e,t,o)=>{"use strict";var r=o(8200);e.exports=r.extend({__construct(){this.cache={},r.prototype.__construct.apply(this,arguments)},getName:()=>"",getCacheKey(e){return JSON.stringify({service:this.getName(),data:e})},fetchCache(e,t,o,r=!1){return elementorPro.ajax.addRequest("forms_panel_action_data",{unique_id:"integrations_"+this.getName(),data:o,success:o=>{this.cache[e]=_.extend({},this.cache[e]),this.cache[e][t]=o[e]}},r)},onInit(){this.addSectionListener("section_"+this.getName(),this.onSectionActive)},onSectionActive(){this.onApiUpdate()},onApiUpdate(){}})},3433:(e,t,o)=>{"use strict";var r=o(8003).__,n=o(2220);e.exports=n.extend({getName:()=>"convertkit",onElementChange(e){switch(e){case"convertkit_api_key_source":case"convertkit_custom_api_key":this.onApiUpdate();break;case"convertkit_form":this.onListUpdate()}},onApiUpdate(){var e=this,t=e.getEditorControlView("convertkit_api_key_source"),o=e.getEditorControlView("convertkit_custom_api_key");if("default"!==t.getControlValue()&&""===o.getControlValue())return e.updateOptions("convertkit_form",[]),void e.getEditorControlView("convertkit_form").setValue("");e.addControlSpinner("convertkit_form");const r=this.getCacheKey({type:"data",controls:[t.getControlValue(),o.getControlValue()]});e.getConvertKitCache("data","convertkit_get_forms",r).done((function(t){e.updateOptions("convertkit_form",t.data.forms),e.updateOptions("convertkit_tags",t.data.tags)}))},onListUpdate(){this.updateFieldsMapping()},updateFieldsMapping(){if(this.getEditorControlView("convertkit_form").getControlValue()){var e=[{remote_label:r("Email","elementor"),remote_type:"email",remote_id:"email",remote_required:!0},{remote_label:r("First Name","elementor"),remote_type:"text",remote_id:"first_name",remote_required:!1}];this.getEditorControlView("convertkit_fields_map").updateMap(e)}},getConvertKitCache(e,t,o,r){if(_.has(this.cache[e],o)){var n={};return n[e]=this.cache[e][o],jQuery.Deferred().resolve(n)}return r=_.extend({},r,{service:"convertkit",convertkit_action:t,api_key:this.getEditorControlView("convertkit_api_key_source").getControlValue(),custom_api_key:this.getEditorControlView("convertkit_custom_api_key").getControlValue()}),this.fetchCache(e,o,r)}})},4772:(e,t,o)=>{"use strict";var r=o(8003).__,n=o(2220);e.exports=n.extend({getName:()=>"drip",onElementChange(e){switch(e){case"drip_api_token_source":case"drip_custom_api_token":this.onApiUpdate();break;case"drip_account":this.onDripAccountsUpdate()}},onApiUpdate(){var e=this,t=e.getEditorControlView("drip_api_token_source"),o=e.getEditorControlView("drip_custom_api_token");if("default"!==t.getControlValue()&&""===o.getControlValue())return e.updateOptions("drip_account",[]),void e.getEditorControlView("drip_account").setValue("");e.addControlSpinner("drip_account"),this.getCacheKey({type:"accounts",controls:[t.getControlValue(),o.getControlValue()]}),e.getDripCache("accounts","accounts",t.getControlValue()).done((function(t){e.updateOptions("drip_account",t.accounts)}))},onDripAccountsUpdate(){this.updateFieldsMapping()},updateFieldsMapping(){if(this.getEditorControlView("drip_account").getControlValue()){var e={remote_label:r("Email","elementor"),remote_type:"email",remote_id:"email",remote_required:!0};this.getEditorControlView("drip_fields_map").updateMap([e])}},getDripCache(e,t,o,r){if(_.has(this.cache[e],o)){var n={};return n[e]=this.cache[e][o],jQuery.Deferred().resolve(n)}return r=_.extend({},r,{service:"drip",drip_action:t,api_token:this.getEditorControlView("drip_api_token_source").getControlValue(),custom_api_token:this.getEditorControlView("drip_custom_api_token").getControlValue()}),this.fetchCache(e,o,r)}})},9228:(e,t,o)=>{"use strict";var r=o(2220);e.exports=r.extend({getName:()=>"getresponse",onElementChange(e){switch(e){case"getresponse_custom_api_key":case"getresponse_api_key_source":this.onApiUpdate();break;case"getresponse_list":this.onGetResonseListUpdate()}},onApiUpdate(){var e=this,t=e.getEditorControlView("getresponse_api_key_source"),o=e.getEditorControlView("getresponse_custom_api_key");if("default"!==t.getControlValue()&&""===o.getControlValue())return e.updateOptions("getresponse_list",[]),void e.getEditorControlView("getresponse_list").setValue("");e.addControlSpinner("getresponse_list");const r=this.getCacheKey({type:"lists",controls:[t.getControlValue(),o.getControlValue()]});e.getCache("lists","lists",r).done((function(t){e.updateOptions("getresponse_list",t.lists)}))},onGetResonseListUpdate(){this.updatGetResonseList()},updatGetResonseList(){var e=this,t=e.getEditorControlView("getresponse_list");if(!t.getControlValue())return;e.addControlSpinner("getresponse_fields_map");const o=this.getCacheKey({type:"fields",controls:[t.getControlValue()]});e.getCache("fields","get_fields",o,{getresponse_list:t.getControlValue()}).done((function(t){e.getEditorControlView("getresponse_fields_map").updateMap(t.fields)}))},getCache(e,t,o,r){if(_.has(this.cache[e],o)){var n={};return n[e]=this.cache[e][o],jQuery.Deferred().resolve(n)}return r=_.extend({},r,{service:"getresponse",getresponse_action:t,api_key:this.getEditorControlView("getresponse_api_key_source").getControlValue(),custom_api_key:this.getEditorControlView("getresponse_custom_api_key").getControlValue()}),this.fetchCache(e,o,r)},onSectionActive(){r.prototype.onSectionActive.apply(this,arguments),this.updatGetResonseList()}})},8326:(e,t,o)=>{"use strict";var r=o(2220);e.exports=r.extend({getName:()=>"mailchimp",onElementChange(e){switch(e){case"mailchimp_api_key_source":case"mailchimp_api_key":this.onApiUpdate();break;case"mailchimp_list":this.onMailchimpListUpdate()}},onApiUpdate(){var e=this,t=e.getEditorControlView("mailchimp_api_key"),o=e.getEditorControlView("mailchimp_api_key_source");if("default"!==o.getControlValue()&&""===t.getControlValue())return e.updateOptions("mailchimp_list",[]),void e.getEditorControlView("mailchimp_list").setValue("");e.resetControlIndicators("mailchimp_list"),e.addControlSpinner("mailchimp_list");const r=this.getCacheKey({type:"lists",controls:[t.getControlValue(),o.getControlValue()]});e.getMailchimpCache("lists","lists",r).done((function(t){e.updateOptions("mailchimp_list",t.lists),e.updatMailchimpList()})).fail((function(t){e.addControlError("mailchimp_list",t)})).always((function(){e.removeControlSpinner("mailchimp_list")}))},onMailchimpListUpdate(){this.updateOptions("mailchimp_groups",[]),this.getEditorControlView("mailchimp_groups").setValue(""),this.updatMailchimpList()},updatMailchimpList(){var e=this,t=e.getEditorControlView("mailchimp_list");if(!t.getControlValue())return;e.resetControlIndicators("mailchimp_groups"),e.addControlSpinner("mailchimp_groups"),this.getCacheKey({type:"list_details",controls:[t.getControlValue()]}),e.getMailchimpCache("list_details","list_details",t.getControlValue(),{mailchimp_list:t.getControlValue()}).done((function(t){e.updateOptions("mailchimp_groups",t.list_details.groups),e.getEditorControlView("mailchimp_fields_map").updateMap(t.list_details.fields)})).fail((function(t){e.addControlError("mailchimp_groups",t)})).always((function(){e.removeControlSpinner("mailchimp_groups")}));const o={type:"fields",action:"fields",cacheKey:t.getControlValue(),args:{mailchimp_list:t.getControlValue()},immediately:!0};e.getMailchimpCache(...Object.values(o)).done((function(t){e.getEditorControlView("mailchimp_fields_map").updateMap(t.fields)}))},getMailchimpCache(e,t,o,r,n=!1){if(_.has(this.cache[e],o)){var i={};return i[e]=this.cache[e][o],jQuery.Deferred().resolve(i)}return r=_.extend({},r,{service:"mailchimp",mailchimp_action:t,api_key:this.getEditorControlView("mailchimp_api_key").getControlValue(),use_global_api_key:this.getEditorControlView("mailchimp_api_key_source").getControlValue()}),this.fetchCache(e,o,r,n)},onSectionActive(){r.prototype.onSectionActive.apply(this,arguments),this.onApiUpdate()}})},2502:(e,t,o)=>{"use strict";var r=o(8003).__;o(3517);const n=o(2220);e.exports=n.extend({fields:{},getName:()=>"mailerlite",onElementChange(e){switch(e){case"mailerlite_api_key_source":case"mailerlite_custom_api_key":this.onMailerliteApiKeyUpdate();break;case"mailerlite_group":this.updateFieldsMapping()}},onMailerliteApiKeyUpdate(){var e=this,t=e.getEditorControlView("mailerlite_custom_api_key"),o=e.getEditorControlView("mailerlite_api_key_source");if("default"!==o.getControlValue()&&""===t.getControlValue())return e.updateOptions("mailerlite_group",[]),void e.getEditorControlView("mailerlite_group").setValue("");e.addControlSpinner("mailerlite_group");const r=this.getCacheKey({type:"groups",controls:[t.getControlValue(),o.getControlValue()]});e.getMailerliteCache("groups","groups",r).done((function(t){e.updateOptions("mailerlite_group",t.groups),e.fields=t.fields}))},updateFieldsMapping(){if(!this.getEditorControlView("mailerlite_group").getControlValue())return;const e=[{remote_label:r("Email","elementor"),remote_type:"email",remote_id:"email",remote_required:!0},{remote_label:r("Name","elementor"),remote_type:"text",remote_id:"name",remote_required:!1},{remote_label:r("Last Name","elementor"),remote_type:"text",remote_id:"last_name",remote_required:!1},{remote_label:r("Company","elementor"),remote_type:"text",remote_id:"company",remote_required:!1},{remote_label:r("Phone","elementor"),remote_type:"text",remote_id:"phone",remote_required:!1},{remote_label:r("Country","elementor"),remote_type:"text",remote_id:"country",remote_required:!1},{remote_label:r("State","elementor"),remote_type:"text",remote_id:"state",remote_required:!1},{remote_label:r("City","elementor"),remote_type:"text",remote_id:"city",remote_required:!1},{remote_label:r("Zip","elementor"),remote_type:"text",remote_id:"zip",remote_required:!1}];for(const t in this.fields)Object.prototype.hasOwnProperty.call(this.fields,t)&&e.push(this.fields[t]);this.getEditorControlView("mailerlite_fields_map").updateMap(e)},getMailerliteCache(e,t,o,r){if(_.has(this.cache[e],o)){const t={};return t[e]=this.cache[e][o],jQuery.Deferred().resolve(t)}return r=_.extend({},r,{service:"mailerlite",mailerlite_action:t,custom_api_key:this.getEditorControlView("mailerlite_custom_api_key").getControlValue(),api_key:this.getEditorControlView("mailerlite_api_key_source").getControlValue()}),this.fetchCache(e,o,r)},onSectionActive(){n.prototype.onSectionActive.apply(this,arguments),this.onMailerliteApiKeyUpdate()}})},7635:(e,t,o)=>{"use strict";var r=o(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=r(o(8804));class FormsModule extends elementorModules.editor.utils.Module{onElementorInit(){const e=o(8005),t=o(1679),r=o(2502),n=o(8326),i=o(4772),a=o(4552),s=o(9228),l=o(3433);this.replyToField=new e,this.mailchimp=new n("form"),this.recaptcha=new t("form"),this.drip=new i("form"),this.activecampaign=new a("form"),this.getresponse=new s("form"),this.convertkit=new l("form"),this.mailerlite=new r("form");const d=o(9395),u=o(3463),c=o(5781),p=o(4146),m=o(6326);this.Fields={time:new d("form"),date:new u("form"),tel:new m("form"),acceptance:new c("form"),upload:new p("form")},elementor.addControlView("Fields_map",o(6002)),elementor.addControlView("form-fields-repeater",o(4566))}onElementorInitComponents(){$e.components.register(new n.default({manager:this}))}}t.default=FormsModule},1679:e=>{"use strict";e.exports=elementorModules.editor.utils.Module.extend({enqueueRecaptchaJs(e,t){elementorFrontend.elements.$body.find('[src="'+e+'"]').length||elementorFrontend.elements.$body.append(' diff --git a/core/integrations/actions/action-base.php b/core/integrations/actions/action-base.php new file mode 100644 index 0000000..d236bd1 --- /dev/null +++ b/core/integrations/actions/action-base.php @@ -0,0 +1,47 @@ +validate( $payload ); + $this->apply( $payload ); + } +} diff --git a/core/integrations/actions/email/email-address.php b/core/integrations/actions/email/email-address.php new file mode 100644 index 0000000..8be400a --- /dev/null +++ b/core/integrations/actions/email/email-address.php @@ -0,0 +1,50 @@ +address = (string) $address; + $this->name = (string) $name; + } + + /** + * Format an email to be ready for header (e.g. `Recipient Name ` or `user@email.com`) + * + * @return string + */ + public function format() { + if ( ! empty( $this->name ) ) { + return sprintf( '%s <%s>', $this->name, $this->address ); + } + + return sprintf( '%s', $this->address ); + } +} diff --git a/core/integrations/actions/email/email-message.php b/core/integrations/actions/email/email-message.php new file mode 100644 index 0000000..ecad0f8 --- /dev/null +++ b/core/integrations/actions/email/email-message.php @@ -0,0 +1,239 @@ +from( get_bloginfo( 'admin_email' ), get_bloginfo( 'name' ) ); + } + + /** + * Set the email sender. + * + * @param string $email + * @param string|null $name + * + * @return $this + */ + public function from( $email, $name = null ) { + $this->from = new Email_Address( $email, $name ); + + return $this; + } + + /** + * Set the email recipient. + * + * @param string $email + * @param string|null $name + * + * @return $this + */ + public function to( $email, $name = null ) { + $this->to = new Email_Address( $email, $name ); + + return $this; + } + + /** + * Add a reply to. + * + * @param string $email + * @param string|null $name + * + * @return $this + */ + public function reply_to( $email, $name = null ) { + $this->reply_to[] = new Email_Address( $email, $name ); + + return $this; + } + + /** + * Add a CC. + * + * @param string $email + * @param string|null $name + * + * @return $this + */ + public function cc( $email, $name = null ) { + $this->cc[] = new Email_Address( $email, $name ); + + return $this; + } + + /** + * Add a BCC. + * + * @param string $email + * @param string|null $name + * + * @return $this + */ + public function bcc( $email, $name = null ) { + $this->bcc[] = new Email_Address( $email, $name ); + + return $this; + } + + /** + * Set the email subject. + * + * @param string $subject + * + * @return $this + */ + public function subject( $subject ) { + $this->subject = (string) $subject; + + return $this; + } + + /** + * Set the email content type. + * + * @param string $content_type + * + * @return $this + */ + public function content_type( $content_type ) { + $this->content_type = (string) $content_type; + + return $this; + } + + /** + * Set the email body using plain text. + * + * @param string $body + * @param string $content_type + * + * @return $this + */ + public function body( $body, $content_type = 'text/html' ) { + $this->body = (string) $body; + + return $this->content_type( $content_type ); + } + + /** + * Set the email body using a view. + * + * @param string $path - View path, + * @param array $data - Data that will be passes to the view. + * + * @return $this + * @throws \Exception + */ + public function view( $path, $data = [] ) { + if ( ! is_file( $path ) ) { + throw new \Exception( "`{$path}` is not a valid view." ); + } + + ob_start(); + + // Inspired from Laravel's view mechanism: + // [1] https://github.dev/illuminate/filesystem/blob/b179f9ea3b3195d1f4b5ae2aee67e42eac6ceb5e/Filesystem.php#L98 + // [2] https://github.dev/illuminate/view/blob/6dd315634a44450c5e443fa8735d4a526833fad3/Engines/PhpEngine.php#L48 + call_user_func( function( $__view_path, $__view_data ) { + extract( $__view_data, EXTR_SKIP ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract + + unset( $__view_data ); + + // `$__view_data` keys are available in the file as variables. + require $__view_path; + }, $path, $data ); + + $this->body = ob_get_clean(); + + return $this->content_type( 'text/html' ); + } + + /** + * Add an attachment. + * + * @param string $path - Attachment path on the server. + * + * @return $this + */ + public function attach( $path ) { + $this->attachments[] = (string) $path; + + return $this; + } +} diff --git a/core/integrations/actions/email/email.php b/core/integrations/actions/email/email.php new file mode 100644 index 0000000..d834a96 --- /dev/null +++ b/core/integrations/actions/email/email.php @@ -0,0 +1,128 @@ +content_type ), + sprintf( 'From: %s', $payload->from->format() ), + ]; + + foreach ( $payload->reply_to as $recipient ) { + $headers[] = sprintf( 'Reply-To: %s', $recipient->format() ); + } + + // Set CC headers. + $cc_headers = []; + + foreach ( $payload->cc as $recipient ) { + $cc_headers[] = sprintf( 'Cc: %s', $recipient->format() ); + } + + // Send email. + $this->send_mail( + $payload->to->format(), + $payload->subject, + $payload->body, + implode( PHP_EOL, array_merge( $headers, $cc_headers ) ), + $payload->attachments + ); + + // Send BCC emails. + foreach ( $payload->bcc as $bcc ) { + $this->send_mail( + $bcc->format(), + $payload->subject, + $payload->body, + implode( PHP_EOL, $headers ), + $payload->attachments + ); + } + } + + /** + * @alias `$this->run()` + * + * @param Email_Message $payload + * + * @return void + *@throws \Exception + * + */ + public function send( Email_Message $payload ) { + $this->run( $payload ); + } + + /** + * Validate the email message DTO. + * + * @param Email_Message $payload + * + * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception + * + * @return void + */ + public function validate( $payload ) { + $required_fields = [ + 'from', + 'to', + 'subject', + 'body', + 'content_type', + ]; + + foreach ( $required_fields as $field ) { + if ( empty( $payload->{$field} ) ) { + throw new Action_Validation_Failed_Exception( + static::class, + "`Email_Message::\${$field}` is required." + ); + } + } + } + + /** + * Calls `wp_mail()`. Used for testing. + * + * @param mixed ...$args + * + * @return void + */ + protected function send_mail( ...$args ) { + add_action( 'wp_mail_failed', [ $this, 'on_wp_mail_error' ] ); + + wp_mail( ...$args ); + + remove_action( 'wp_mail_failed', [ $this, 'on_wp_mail_error' ] ); + } + + /** + * Throw exception on `wp_mail()` error. + * + * @param \WP_Error $error + * + * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception + * + * @return void + */ + public function on_wp_mail_error( \WP_Error $error ) { + throw new Action_Failed_Exception( static::class, '`wp_mail()` cannot send email', $error ); + } +} diff --git a/core/integrations/exceptions/action-failed-exception.php b/core/integrations/exceptions/action-failed-exception.php new file mode 100644 index 0000000..0536c5a --- /dev/null +++ b/core/integrations/exceptions/action-failed-exception.php @@ -0,0 +1,18 @@ +action, + $message + ); + } +} diff --git a/core/integrations/exceptions/action-validation-failed-exception.php b/core/integrations/exceptions/action-validation-failed-exception.php new file mode 100644 index 0000000..a775773 --- /dev/null +++ b/core/integrations/exceptions/action-validation-failed-exception.php @@ -0,0 +1,18 @@ +action, + $message + ); + } +} diff --git a/core/integrations/exceptions/exception-base.php b/core/integrations/exceptions/exception-base.php new file mode 100644 index 0000000..1184d4d --- /dev/null +++ b/core/integrations/exceptions/exception-base.php @@ -0,0 +1,70 @@ +action = $action; + $this->meta = $meta; + + $message = $this->format_message( $message ); + + parent::__construct( $message ); + } + + /** + * Log the exception to Elementor's log. + * + * @return void + */ + public function log() { + Plugin::elementor()->logger->get_logger()->error( $this->getMessage(), [ 'meta' => $this->meta ] ); + } + + /** + * Get the error format. + * + * @return string + */ + public function __toString() { + return sprintf( + '%s: %s', + __CLASS__, + $this->getMessage() + ); + } +} diff --git a/core/integrations/integrations-manager.php b/core/integrations/integrations-manager.php new file mode 100644 index 0000000..73a89a9 --- /dev/null +++ b/core/integrations/integrations-manager.php @@ -0,0 +1,112 @@ +actions_registrar = new Registrar(); + } + + /** + * Get an action instance. + * + * @shortcut `Registrar->get()`. + * + * @return \ElementorPro\Core\Integrations\Actions\Action_Base|null + */ + public function get_action( $id ) { + if ( ! $this->is_initialized() ) { + $this->init_actions(); + } + + return $this->actions_registrar->get( $id ); + } + + /** + * Run an action for a selected payload. + * + * @param array|mixed $payloads - Payloads instances to run the actions on. + * @param null|string $id - If `$payloads` is not an array, a custom action ID can be provided. + * + * @return void + */ + public function run( $payloads, $id = null ) { + if ( ! is_array( $payloads ) ) { + $payloads = $id ? [ $id => $payloads ] : [ $payloads ]; + } + + foreach ( $payloads as $key => $payload ) { + // Get the action ID for the provided payload type. + $action_id = is_numeric( $key ) ? get_class( $payload ) : $key; + + /** + * @type Action_Base $action + */ + $action = $this->get_action( $action_id ); + + if ( ! $action ) { + throw new \Exception( "{$action_id} doesn't have an associated `Action`." ); + } + + if ( ! ( $action instanceof Action_Base ) ) { + $action_class = get_class( $action ); + + throw new \Exception( "{$action_class} is not a valid `Action_Base`." ); + } + + try { + $action->run( $payload ); + } catch ( Action_Validation_Failed_Exception $e ) { + $e->log(); + } catch ( Action_Failed_Exception $e ) { + $e->log(); + } + } + } + + /** + * Initialize the manager actions. + * + * @return void + */ + protected function init_actions() { + add_action( 'elementor_pro/core/integrations/actions/register', function ( Registrar $actions_registrar ) { + $actions_registrar->register( new Email(), Email_Message::class ); + } ); + + do_action( 'elementor_pro/core/integrations/actions/register', $this->actions_registrar ); + } + + /** + * Determine if the manager is initialized. + * + * @return boolean + */ + protected function is_initialized() { + return ! ! did_action( 'elementor_pro/core/integrations/actions/register' ); + } +} diff --git a/core/isolation/wordpress-adapter-interface.php b/core/isolation/wordpress-adapter-interface.php new file mode 100644 index 0000000..48facdc --- /dev/null +++ b/core/isolation/wordpress-adapter-interface.php @@ -0,0 +1,9 @@ +experiments->add_feature( $experimental_data ); + + if ( ! Plugin::elementor()->experiments->is_feature_active( $experimental_data['name'] ) ) { + continue; + } + } + + if ( $class_name::is_active() ) { + $this->modules[ $module_name ] = $class_name::instance(); + } + } + } + + /** + * @param string $module_name + * + * @return Module_Base|Module_Base[] + */ + public function get_modules( $module_name ) { + if ( $module_name ) { + if ( isset( $this->modules[ $module_name ] ) ) { + return $this->modules[ $module_name ]; + } + + return null; + } + + return $this->modules; + } +} diff --git a/core/notifications/notification.php b/core/notifications/notification.php new file mode 100644 index 0000000..1807646 --- /dev/null +++ b/core/notifications/notification.php @@ -0,0 +1,29 @@ +get_payloads( $notifiable ); + + Plugin::instance()->integrations->run( $payloads ); + + return $this; + } +} diff --git a/core/notifications/traits/notifiable.php b/core/notifications/traits/notifiable.php new file mode 100644 index 0000000..257e831 --- /dev/null +++ b/core/notifications/traits/notifiable.php @@ -0,0 +1,30 @@ +notify( new User_Created_Notification( $new_user ) ); + * + * @param Notification $notification - Notification to send. + * + * @throws \Exception + * + * @return void + */ + public function notify( Notification $notification ) { + Plugin::instance()->notifications->send( $notification, $this ); + } +} diff --git a/core/php-api.php b/core/php-api.php new file mode 100644 index 0000000..75ddf66 --- /dev/null +++ b/core/php-api.php @@ -0,0 +1,23 @@ +get_css_assets_url( 'preview', null, 'default', true ), + [], + ELEMENTOR_PRO_VERSION + ); + } + + protected function get_assets_base_url() { + return ELEMENTOR_PRO_URL; + } +} diff --git a/core/upgrade/manager.php b/core/upgrade/manager.php new file mode 100644 index 0000000..abae2c9 --- /dev/null +++ b/core/upgrade/manager.php @@ -0,0 +1,43 @@ +get_col( + 'SELECT `post_id` FROM `' . $wpdb->postmeta . '` WHERE `meta_key` = "_elementor_data" AND `meta_value` LIKE \'%"widgetType":"form"%\';' + ); + + if ( empty( $post_ids ) ) { + return; + } + + foreach ( $post_ids as $post_id ) { + $document = Plugin::elementor()->documents->get( $post_id ); + + if ( $document ) { + $data = $document->get_elements_data(); + } + + if ( empty( $data ) ) { + continue; + } + + $data = Plugin::elementor()->db->iterate_data( $data, function( $element ) { + if ( empty( $element['widgetType'] ) || 'form' !== $element['widgetType'] ) { + return $element; + } + + if ( ! isset( $element['settings']['submit_actions'] ) ) { + $element['settings']['submit_actions'] = [ 'email' ]; + } + + if ( ! empty( $element['settings']['redirect_to'] ) ) { + if ( ! in_array( 'redirect', $element['settings']['submit_actions'] ) ) { + $element['settings']['submit_actions'][] = 'redirect'; + } + } + + if ( ! empty( $element['settings']['webhooks'] ) ) { + if ( ! in_array( 'webhook', $element['settings']['submit_actions'] ) ) { + $element['settings']['submit_actions'][] = 'webhook'; + } + } + + return $element; + } ); + + self::save_editor( $post_id, $data ); + } + } + + public static function _v_1_4_0() { + global $wpdb; + + // Move all posts columns to classic skin (Just add prefix) + $post_ids = $wpdb->get_col( + 'SELECT `post_id` FROM `' . $wpdb->postmeta . '` WHERE `meta_key` = "_elementor_data" AND `meta_value` LIKE \'%"widgetType":"posts"%\';' + ); + + if ( empty( $post_ids ) ) { + return; + } + + foreach ( $post_ids as $post_id ) { + $document = Plugin::elementor()->documents->get( $post_id ); + + if ( $document ) { + $data = $document->get_elements_data(); + } + + if ( empty( $data ) ) { + continue; + } + + $data = Plugin::elementor()->db->iterate_data( $data, function( $element ) { + if ( empty( $element['widgetType'] ) || 'posts' !== $element['widgetType'] ) { + return $element; + } + + $fields_to_change = [ + 'columns', + 'columns_mobile', + 'columns_tablet', + ]; + + foreach ( $fields_to_change as $field ) { + // TODO: Remove old value later + $new_field_key = 'classic_' . $field; + if ( isset( $element['settings'][ $field ] ) && ! isset( $element['settings'][ $new_field_key ] ) ) { + $element['settings'][ $new_field_key ] = $element['settings'][ $field ]; + } + } + + return $element; + } ); + + $document = Plugin::elementor()->documents->get( $post_id ); + + $document->save( [ + 'elements' => $data, + ] ); + } + } + + public static function _v_1_12_0() { + global $wpdb; + + // Set `mailchimp_api_key_source` to `custom`. + $post_ids = $wpdb->get_col( + 'SELECT `post_id` FROM `' . $wpdb->postmeta . '` WHERE `meta_key` = "_elementor_data" AND `meta_value` LIKE \'%"widgetType":"form"%\';' + ); + + if ( empty( $post_ids ) ) { + return; + } + + foreach ( $post_ids as $post_id ) { + $do_update = false; + $document = Plugin::elementor()->documents->get( $post_id ); + + if ( $document ) { + $data = $document->get_elements_data(); + } + + if ( empty( $data ) ) { + continue; + } + + $data = Plugin::elementor()->db->iterate_data( $data, function( $element ) use ( &$do_update ) { + if ( empty( $element['widgetType'] ) || 'form' !== $element['widgetType'] ) { + return $element; + } + + if ( ! empty( $element['settings']['mailchimp_api_key'] ) && ! isset( $element['settings']['mailchimp_api_key_source'] ) ) { + $element['settings']['mailchimp_api_key_source'] = 'custom'; + $do_update = true; + } + + return $element; + } ); + + // Only update if form has mailchimp + if ( ! $do_update ) { + continue; + } + // We need the `wp_slash` in order to avoid the unslashing during the `update_post_meta` + $json_value = wp_slash( wp_json_encode( $data ) ); + + update_metadata( 'post', $post_id, '_elementor_data', $json_value ); + } + } + + /** + * Replace 'sticky' => 'yes' with 'sticky' => 'top' in sections. + */ + public static function _v_2_0_3() { + global $wpdb; + + $post_ids = $wpdb->get_col( + 'SELECT `post_id` FROM `' . $wpdb->postmeta . '` WHERE `meta_key` = "_elementor_data" AND `meta_value` LIKE \'%"sticky":"yes"%\';' + ); + + if ( empty( $post_ids ) ) { + return; + } + + foreach ( $post_ids as $post_id ) { + $do_update = false; + + $document = Plugin::elementor()->documents->get( $post_id ); + + if ( ! $document ) { + continue; + } + + $data = $document->get_elements_data(); + + if ( empty( $data ) ) { + continue; + } + + $data = Plugin::elementor()->db->iterate_data( $data, function( $element ) use ( &$do_update ) { + if ( empty( $element['elType'] ) || 'section' !== $element['elType'] ) { + return $element; + } + + if ( ! empty( $element['settings']['sticky'] ) && 'yes' === $element['settings']['sticky'] ) { + $element['settings']['sticky'] = 'top'; + $do_update = true; + } + + return $element; + } ); + + if ( ! $do_update ) { + continue; + } + // We need the `wp_slash` in order to avoid the unslashing during the `update_metadata` + $json_value = wp_slash( wp_json_encode( $data ) ); + + update_metadata( 'post', $post_id, '_elementor_data', $json_value ); + } // End foreach(). + } + + private static function save_editor( $post_id, $posted ) { + // Change the global post to current library post, so widgets can use `get_the_ID` and other post data + if ( isset( $GLOBALS['post'] ) ) { + $global_post = $GLOBALS['post']; + } + $GLOBALS['post'] = get_post( $post_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + + $editor_data = self::get_editor_data( $posted ); + + // We need the `wp_slash` in order to avoid the unslashing during the `update_post_meta` + $json_value = wp_slash( wp_json_encode( $editor_data ) ); + + $is_meta_updated = update_metadata( 'post', $post_id, '_elementor_data', $json_value ); + + if ( $is_meta_updated ) { + Revisions_Manager::handle_revision(); + } + + // Restore global post + if ( isset( $global_post ) ) { + $GLOBALS['post'] = $global_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + } else { + unset( $GLOBALS['post'] ); + } + + /** + * After editor saves data. + * + * Fires after Elementor editor data was saved. + * + * @since 1.0.0 + * + * @param int $post_id The ID of the post. + * @param array $editor_data The editor data. + */ + do_action( 'elementor/editor/after_save', $post_id, $editor_data ); + } + + private static function get_editor_data( $data, $with_html_content = false ) { + $editor_data = []; + + foreach ( $data as $element_data ) { + $element = Plugin::elementor()->elements_manager->create_element_instance( $element_data ); + + if ( ! $element ) { + continue; + } + + $editor_data[] = $element->get_raw_data( $with_html_content ); + } // End Section + + return $editor_data; + } + + public static function _v_2_5_0_form( $updater ) { + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_repeater_settings' ], + 'control_ids' => [ + 'form_fields' => [ + '_id' => 'custom_id', + ], + ], + ], + ]; + + return self::_update_widget_settings( 'form', $updater, $changes ); + } + + public static function _v_2_5_0_woocommerce_menu_cart( $updater ) { + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings' ], + 'control_ids' => [ + 'checkout_button_border_color' => 'checkout_border_color', + 'view_cart_button_border_color' => 'view_cart_border_color', + ], + ], + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_slider_to_border_settings' ], + 'control_ids' => [ + 'checkout_button_border_width' => [ + 'new' => 'checkout_border_width', + 'add' => 'checkout_border_border', + ], + 'view_cart_button_border_width' => [ + 'new' => 'view_cart_border_width', + 'add' => 'view_cart_border_border', + ], + ], + ], + ]; + + return self::_update_widget_settings( 'woocommerce-menu-cart', $updater, $changes ); + } + + public static function _v_3_7_2_woocommerce_rename_related_to_related_products( $updater ) { + $changes = self::get_woocommerce_rename_related_to_related_products_changes(); + + return self::_update_widget_settings( 'woocommerce-products', $updater, $changes ); + } + + public static function _slider_to_border_settings( $element, $args ) { + $widget_id = $args['widget_id']; + $changes = $args['control_ids']; + + if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { + return $element; + } + + foreach ( $changes as $old => $new ) { + if ( ! empty( $element['settings'][ $old ] ) && ! isset( $element['settings'][ $new['new'] ] ) ) { + $new_border_width = [ + 'unit' => $element['settings'][ $old ]['unit'], + 'top' => $element['settings'][ $old ]['size'], + 'bottom' => $element['settings'][ $old ]['size'], + 'left' => $element['settings'][ $old ]['size'], + 'right' => $element['settings'][ $old ]['size'], + 'isLinked' => true, + ]; + $element['settings'][ $new ['new'] ] = $new_border_width; + $element['settings'][ $new ['add'] ] = 'solid'; + $args['do_update'] = true; + } + } + return $element; + } + + /** + * @param $element + * @param $args + * + * @return mixed + */ + public static function _rename_repeater_settings( $element, $args ) { + $widget_id = $args['widget_id']; + $changes = $args['control_ids']; + + if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { + return $element; + } + + foreach ( $changes as $change_key => $change ) { + foreach ( $change as $old => $new ) { + foreach ( $element['settings'][ $change_key ] as &$repeater ) { + if ( ! empty( $repeater[ $old ] ) && ! isset( $repeater[ $new ] ) ) { + $repeater[ $new ] = $repeater[ $old ]; + $args['do_update'] = true; + } + } + } + } + + return $element; + } + + private static function taxonomies_mapping( $prefix, $map_to ) { + $taxonomy_filter_args = [ + 'show_in_nav_menus' => true, + ]; + + $taxonomies = get_taxonomies( $taxonomy_filter_args ); + + $mapping = []; + + foreach ( $taxonomies as $taxonomy ) { + $mapping[ $prefix . $taxonomy . '_ids' ] = $map_to; + } + + return $mapping; + } + + public static function _v_2_5_0_posts( $updater ) { + $add_taxonomies = self::taxonomies_mapping( 'posts_', [ 'posts_include' => 'terms' ] ); + $merge_taxonomies = self::taxonomies_mapping( 'posts_', 'posts_include_term_ids' ); + + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings' ], + 'control_ids' => [ + 'orderby' => 'posts_orderby', + 'order' => 'posts_order', + 'offset' => 'posts_offset', + 'exclude' => 'posts_exclude', + 'exclude_ids' => 'posts_exclude_ids', + 'posts_query_id' => 'posts_posts_query_id', + 'avoid_duplicates' => 'posts_avoid_duplicates', + 'posts_authors' => 'posts_include_authors', + ], + ], + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_add_widget_settings_to_array' ], + 'control_ids' => array_merge( $add_taxonomies, [ + 'posts_authors' => [ 'posts_include' => 'authors' ], + ] ), + ], + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_merge_widget_settings' ], + 'control_ids' => $merge_taxonomies, + ], + ]; + + return self::_update_widget_settings( 'posts', $updater, $changes ); + } + + public static function _v_2_5_0_portfolio( $updater ) { + $add_taxonomies = self::taxonomies_mapping( 'posts_', [ 'posts_include' => 'terms' ] ); + $merge_taxonomies = self::taxonomies_mapping( 'posts_', 'posts_include_term_ids' ); + + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings' ], + 'control_ids' => [ + 'orderby' => 'posts_orderby', + 'order' => 'posts_order', + 'offset' => 'posts_offset', + 'exclude' => 'posts_exclude', + 'exclude_ids' => 'posts_exclude_ids', + 'posts_query_id' => 'posts_posts_query_id', + 'avoid_duplicates' => 'posts_avoid_duplicates', + 'posts_authors' => 'posts_include_authors', + ], + ], + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_add_widget_settings_to_array' ], + 'control_ids' => array_merge( $add_taxonomies, [ + 'posts_authors' => [ 'posts_include' => 'authors' ], + ] ), + ], + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_merge_widget_settings' ], + 'control_ids' => $merge_taxonomies, + ], + ]; + + return self::_update_widget_settings( 'portfolio', $updater, $changes ); + } + + public static function _v_2_5_0_products( $updater ) { + $add_taxonomies = self::taxonomies_mapping( 'query_', [ 'query_include' => 'terms' ] ); + $merge_taxonomies = self::taxonomies_mapping( 'query_', 'query_include_term_ids' ); + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings' ], + 'control_ids' => [ + 'orderby' => 'query_orderby', + 'order' => 'query_order', + 'exclude' => 'query_exclude', + 'exclude_ids' => 'query_exclude_ids', + 'query_authors' => 'query_include_authors', + 'query_product_tag_ids' => 'query_include_term_ids', + 'query_product_cat_ids' => 'query_include_term_ids', + ], + ], + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_add_widget_settings_to_array' ], + 'control_ids' => array_merge( $add_taxonomies, [ + 'query_authors' => [ 'query_include' => 'authors' ], + ] ), + ], + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_merge_widget_settings' ], + 'control_ids' => $merge_taxonomies, + ], + ]; + + return self::_update_widget_settings( 'woocommerce-products', $updater, $changes ); + } + + /** + * @param $updater + * + * @return bool Should run again. + */ + public static function _v_2_5_0_sitemap( $updater ) { + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings' ], + 'control_ids' => [ + 'exclude' => 'sitemap_exclude', + 'exclude_ids' => 'sitemap_exclude_ids', + ], + ], + ]; + + return self::_update_widget_settings( 'sitemap', $updater, $changes ); + } + + /** + * @param Updater $updater + * + * @return bool + */ + public static function _v_2_5_0_popup_border_radius( $updater ) { + global $wpdb; + + $post_ids = $updater->query_col( + "SELECT pm1.post_id + FROM {$wpdb->postmeta} AS pm1 + LEFT JOIN {$wpdb->postmeta} AS pm2 ON (pm1.post_id = pm2.post_id) + WHERE pm1.meta_key = '_elementor_template_type' + AND pm1.meta_value = 'popup' + AND pm2.`meta_key` = '" . Document::PAGE_META_KEY . "' + AND pm2.`meta_value` LIKE '%border_radius%';" + ); + + if ( empty( $post_ids ) ) { + return false; + } + + foreach ( $post_ids as $post_id ) { + // Clear WP cache for next step. + $document = Plugin::elementor()->documents->get( $post_id ); + + if ( ! $document ) { + continue; + } + + $page_settings = $document->get_settings(); + + // Check if there isn't 'border_radius' setting or if it has already been upgraded + if ( empty( $page_settings['border_radius']['size'] ) ) { + continue; + } + + $border_radius = $page_settings['border_radius']; + + $new_border_radius = [ + 'unit' => $border_radius['unit'], + 'top' => $border_radius['size'], + 'bottom' => $border_radius['size'], + 'left' => $border_radius['size'], + 'right' => $border_radius['size'], + 'isLinked' => true, + ]; + + $page_settings['border_radius'] = $new_border_radius; + + // TODO: `$document->update_settings`. + $document->update_meta( Document::PAGE_META_KEY, $page_settings ); + + wp_cache_flush(); + } // End foreach(). + + return $updater->should_run_again( $post_ids ); + } + + public static function _v_2_5_4_posts( $updater ) { + $merge_taxonomies = self::taxonomies_mapping( 'posts_', 'posts_include_term_ids' ); + + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_term_id_to_term_taxonomy_id' ], + 'control_ids' => $merge_taxonomies, + 'prefix' => 'posts_', + 'new_id' => 'include_term_ids', + ], + ]; + + return self::_update_widget_settings( 'posts', $updater, $changes ); + } + + public static function _v_2_5_4_portfolio( $updater ) { + $merge_taxonomies = self::taxonomies_mapping( 'posts_', 'posts_include_term_ids' ); + + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_term_id_to_term_taxonomy_id' ], + 'control_ids' => $merge_taxonomies, + 'prefix' => 'posts_', + 'new_id' => 'include_term_ids', + ], + ]; + + return self::_update_widget_settings( 'portfolio', $updater, $changes ); + } + + public static function _v_2_5_4_products( $updater ) { + $merge_taxonomies = self::taxonomies_mapping( 'query_', 'query_include_term_ids' ); + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_term_id_to_term_taxonomy_id' ], + 'control_ids' => $merge_taxonomies, + 'prefix' => 'query_', + 'new_id' => 'include_term_ids', + ], + ]; + + return self::_update_widget_settings( 'woocommerce-products', $updater, $changes ); + } + + public static function _v_2_5_4_form( $updater ) { + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_missing_form_custom_id_settings' ], + 'control_ids' => [], + ], + ]; + + return self::_update_widget_settings( 'form', $updater, $changes ); + } + + public static function _v_3_1_0_media_carousel( $updater ) { + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_progress_to_progressbar' ], + 'control_ids' => [], + ], + ]; + + return self::_update_widget_settings( 'media-carousel', $updater, $changes ); + } + + public static function _v_3_1_0_reviews( $updater ) { + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_progress_to_progressbar' ], + 'control_ids' => [], + ], + ]; + + return self::_update_widget_settings( 'reviews', $updater, $changes ); + } + + public static function _v_3_1_0_testimonial_carousel( $updater ) { + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_convert_progress_to_progressbar' ], + 'control_ids' => [], + ], + ]; + + return self::_update_widget_settings( 'testimonial-carousel', $updater, $changes ); + } + + public static function _v_3_1_0_slides( $updater ) { + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_migrate_slides_button_color_settings' ], + 'control_ids' => [], + ], + ]; + + return self::_update_widget_settings( 'slides', $updater, $changes ); + } + + public static function _v_3_3_0_nav_menu_icon( $updater ) { + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_migrate_indicator_control_to_submenu_icon' ], + 'control_ids' => [], + ], + ]; + + return self::_update_widget_settings( 'nav-menu', $updater, $changes ); + } + + public static function _v_3_3_0_recalc_usage_data( $updater ) { + return Core_Upgrades::recalc_usage_data( $updater ); + } + + public static function _v_3_5_0_price_list( $updater ) { + $changes = [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_copy_title_styles_to_new_price_controls' ], + 'control_ids' => [], + ], + ]; + + return self::_update_widget_settings( 'price-list', $updater, $changes ); + } + + /** + * $changes is an array of arrays in the following format: + * [ + * 'control_ids' => array of control ids + * 'callback' => user callback to manipulate the control_ids + * ] + * + * @param $widget_id + * @param $updater + * @param array $changes + * + * @return bool + */ + public static function _update_widget_settings( $widget_id, $updater, $changes ) { + global $wpdb; + + $post_ids = $updater->query_col( + 'SELECT `post_id` + FROM `' . $wpdb->postmeta . '` + WHERE `meta_key` = "_elementor_data" + AND `meta_value` LIKE \'%"widgetType":"' . $widget_id . '"%\';' + ); + + if ( empty( $post_ids ) ) { + return false; + } + + foreach ( $post_ids as $post_id ) { + $do_update = false; + + $document = Plugin::elementor()->documents->get( $post_id ); + + if ( ! $document ) { + continue; + } + + $data = $document->get_elements_data(); + + if ( empty( $data ) ) { + continue; + } + + // loop through callbacks & array + foreach ( $changes as $change ) { + $args = [ + 'do_update' => &$do_update, + 'widget_id' => $widget_id, + 'control_ids' => $change['control_ids'], + ]; + if ( isset( $change['prefix'] ) ) { + $args['prefix'] = $change['prefix']; + $args['new_id'] = $change['new_id']; + } + $data = Plugin::elementor()->db->iterate_data( $data, $change['callback'], $args ); + if ( ! $do_update ) { + continue; + } + + // We need the `wp_slash` in order to avoid the unslashing during the `update_metadata` + $json_value = wp_slash( wp_json_encode( $data ) ); + + update_metadata( 'post', $post_id, '_elementor_data', $json_value ); + } + } // End foreach(). + + return $updater->should_run_again( $post_ids ); + } + + /** + * @param $element + * @param $args + * + * @return mixed + */ + public static function _rename_widget_settings( $element, $args ) { + $widget_id = $args['widget_id']; + $changes = $args['control_ids']; + + if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { + return $element; + } + + foreach ( $changes as $old => $new ) { + if ( ! empty( $element['settings'][ $old ] ) && ! isset( $element['settings'][ $new ] ) ) { + $element['settings'][ $new ] = $element['settings'][ $old ]; + $args['do_update'] = true; + } + } + + return $element; + } + + + /** + * @param $element + * @param $args + * + * @return mixed + */ + public static function _rename_widget_settings_value( $element, $args ) { + $widget_id = $args['widget_id']; + $changes = $args['control_ids']; + + if ( self::is_widget_matched( $element, $widget_id ) ) { + $element = self::apply_rename( $changes, $element, $args ); + } + + return $element; + } + + /** + * @param $element + * @param $args + * + * @return mixed + */ + public static function _add_widget_settings_to_array( $element, $args ) { + $widget_id = $args['widget_id']; + $changes = $args['control_ids']; + + if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { + return $element; + } + + foreach ( $changes as $old_key => $added_key ) { + if ( ! empty( $element['settings'][ $old_key ] ) ) { + foreach ( $added_key as $control_id => $val ) { + if ( ! in_array( $val, $element['settings'][ $control_id ], true ) ) { + $element['settings'][ $control_id ][] = $val; + $args['do_update'] = true; + } + } + } + } + + return $element; + } + + /** + * @param $element + * @param $args + * + * @return mixed + */ + public static function _merge_widget_settings( $element, $args ) { + $widget_id = $args['widget_id']; + $changes = $args['control_ids']; + + if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { + return $element; + } + + foreach ( $changes as $old => $new ) { + if ( ! empty( $element['settings'][ $old ] ) ) { + if ( ! isset( $element['settings'][ $new ] ) ) { + $element['settings'][ $new ] = $element['settings'][ $old ]; + } else { + $element['settings'][ $new ] = array_unique( array_merge( $element['settings'][ $old ], $element['settings'][ $new ] ) ); + } + $args['do_update'] = true; + } + } + + return $element; + } + + /** + * Possible scenarios: + * 1) custom_id is not empty --> do nothing + * 2) Existing _id: Empty or Missing custom_id --> create custom_id and set the value to the value of _id + * 3) Missing _id: Empty or Missing custom_id --> generate a unique key and set it as custom_id value + * @param $element + * @param $args + * + * @return mixed + */ + public static function _missing_form_custom_id_settings( $element, $args ) { + $widget_id = $args['widget_id']; + + if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { + return $element; + } + + $random_id = (int) substr( time(), -5 ); + //form_fields loop: + foreach ( $element['settings']['form_fields'] as &$repeater_item ) { + if ( ! empty( $repeater_item['custom_id'] ) ) { // Scenario 1 + continue; + } + + if ( ! empty( $repeater_item['_id'] ) ) { // Scenario 2 + $repeater_item['custom_id'] = $repeater_item['_id']; + } else { // Scenario 3 + $repeater_item['custom_id'] = 'field_' . $random_id; + $random_id++; + } + + $args['do_update'] = true; + } + + return $element; + } + + /** + * Migrates the value saved for the 'indicator' SELECT control in the Nav Menu Widget to the new replacement + * 'submenu_icon' ICONS control. + * + * @param $element + * @param $args + * + * @return mixed; + */ + public static function _migrate_indicator_control_to_submenu_icon( $element, $args ) { + $widget_id = $args['widget_id']; + + // If the current element is not a Nav Menu widget, go to the next one. + if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { + return $element; + } + + // If this Nav Menu widget's 'indicator' control value is the default one (there is no value in the DB), + // there is nothing to migrate, since the default icon is identical in the new control. Go to the next element. + if ( ! isset( $element['settings']['indicator'] ) ) { + return $element; + } + + $new_value = ''; + $new_library = 'fa-solid'; + + switch ( $element['settings']['indicator'] ) { + case 'none': + $new_library = ''; + break; + case 'classic': + $new_value = 'fa-caret-down'; + break; + case 'chevron': + $new_value = 'fa-chevron-down'; + break; + case 'angle': + $new_value = 'fa-angle-down'; + break; + case 'plus': + $new_value = 'e-plus-icon'; + $new_library = ''; + break; + } + + // This is done in order to make sure that the menu will not look any different for users who upgrade. + // The 'None' option should be completely empty. + if ( $new_value ) { + if ( Icons_Manager::is_migration_allowed() ) { + // If the site has been migrated to FA5, add the new FA Solid class. + $new_value = 'fas ' . $new_value; + } else { + // If the site has not been migrated, add the old generic 'fa' class. + $new_value = 'fa ' . $new_value; + } + } + + // Set the migrated value for the new control. + $element['settings']['submenu_icon'] = [ + 'value' => $new_value, + 'library' => $new_library, + ]; + + $args['do_update'] = true; + + return $element; + } + + /** + * @param $element + * @param $args + * + * @return mixed + */ + public static function _convert_term_id_to_term_taxonomy_id( $element, $args ) { + $widget_id = $args['widget_id']; + $changes = $args['control_ids']; + $prefix = $args['prefix']; + $new_id = $prefix . $args['new_id']; + + if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { + return $element; + } + + // Exit if new is empty (should not happen) + if ( empty( $element['settings'][ $new_id ] ) ) { + return $element; + } + + // 1) Convert each term-id to the equivalent term_taxonomy_id + $term_taxonomy_ids = []; + $old_term_ids = []; + foreach ( $changes as $old => $new ) { + if ( ! empty( $element['settings'][ $old ] ) ) { + $start = strlen( $prefix ); + $end = -strlen( '_ids' ); + $taxonomy = substr( $old, $start, $end ); + foreach ( $element['settings'][ $old ] as $term_id ) { + $old_term_ids[] = $term_id; + $term_obj = get_term( $term_id, $taxonomy, OBJECT ); + if ( $term_obj && ! is_wp_error( $term_obj ) ) { + $term_taxonomy_ids[] = $term_obj->term_taxonomy_id; + } + } + } + } + + // 2) Check if the widget's settings were changed after the u/g to 2.5.0 + $diff = array_diff( $element['settings'][ $new_id ], array_unique( $old_term_ids ) ); + if ( empty( $diff ) ) { // Nothing was changed + $element['settings'][ $new_id . '_backup' ] = $element['settings'][ $new_id ]; + $element['settings'][ $new_id ] = $term_taxonomy_ids; + $args['do_update'] = true; + } + + return $element; + } + + /** + * Convert 'progress' to 'progressbar' + * + * Before Elementor 2.2.0, the progress bar option key was 'progress'. In Elementor 2.2.0, + * it was changed to 'progressbar'. This upgrade script migrated the DB data for old websites using 'progress'. + * + * @param $element + * @param $args + * @return mixed + */ + public static function _convert_progress_to_progressbar( $element, $args ) { + $widget_id = $args['widget_id']; + + if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) { + return $element; + } + + if ( 'progress' === $element['settings']['pagination'] ) { + $element['settings']['pagination'] = 'progressbar'; + $args['do_update'] = true; + } + + return $element; + } + + /** + * Migrate Slides Button Color Settings + * + * Move Slides Widget's 'button_color' settings to 'button_text_color' and 'button_border_color' as necessary, + * to allow for removing the redundant control. + * + * @param $element + * @param $args + * @return mixed + */ + public static function _migrate_slides_button_color_settings( $element, $args ) { + if ( empty( $element['widgetType'] ) || $args['widget_id'] !== $element['widgetType'] ) { + return $element; + } + + // If the element doesn't use the 'button_color' control, no need to do anything. + if ( ! isset( $element['settings']['button_color'] ) ) { + return $element; + } + + // Check if button_text_color is set. If it is not set, transfer the value from button_color to button_text_color. + if ( ! isset( $element['settings']['button_text_color'] ) ) { + $element['settings']['button_text_color'] = $element['settings']['button_color']; + $args['do_update'] = true; + } + + // Check if button_border_color is set. If it is not set, transfer the value from button_color to button_border_color. + if ( ! isset( $element['settings']['button_border_color'] ) ) { + $element['settings']['button_border_color'] = $element['settings']['button_color']; + $args['do_update'] = true; + } + + return $element; + } + + /** + * Copy Title Styles to New Price Controls + * + * Copy the values from the Price List widget's Title Style controls to new Price Style controls. + * + * @param $element + * @param $args + * @return mixed + * @since 3.4.0 + * + */ + public static function _copy_title_styles_to_new_price_controls( $element, $args ) { + if ( empty( $element['widgetType'] ) || $args['widget_id'] !== $element['widgetType'] ) { + return $element; + } + + if ( ! empty( $element['settings']['heading_color'] ) ) { + $element['settings']['price_color'] = $element['settings']['heading_color']; + + $args['do_update'] = true; + } + + $old_control_prefix = 'heading_typography_'; + $new_control_prefix = 'price_typography_'; + + foreach ( self::$typography_control_names as $control_name ) { + if ( ! empty( $element['settings'][ $old_control_prefix . $control_name ] ) ) { + $element['settings'][ $new_control_prefix . $control_name ] = $element['settings'][ $old_control_prefix . $control_name ]; + + $args['do_update'] = true; + } + } + + return $element; + } + + public static function _remove_remote_info_api_data() { + global $wpdb; + + $key = API::TRANSIENT_KEY_PREFIX; + + return $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '{$key}%';"); // phpcs:ignore + } + + /** + * @param $element + * @param $to + * @param $control_id + * @param $args + * @return array + */ + protected static function set_new_value( $element, $to, $control_id, $args ) { + $element['settings'][ $control_id ] = $to; + $args['do_update'] = true; + return $element; + } + + /** * + * @param $change + * @param array $element + * @param $args + * @return array + */ + protected static function replace_value_if_found( $change, array $element, $args ) { + $control_id = key( $args['control_ids'] ); + $from = $change['from']; + $to = $change['to']; + if ( self::is_control_exist_in_settings( $element, $control_id ) && self::is_need_to_replace_value( $element, $control_id, $from ) ) { + $element = self::set_new_value( $element, $to, $control_id, $args ); + } + return $element; + } + + /** + * @param $element + * @param $widget_id + * @return bool + */ + protected static function is_widget_matched( $element, $widget_id ) { + return ! empty( $element['widgetType'] ) && $widget_id === $element['widgetType']; + } + + /** + * @param $changes + * @param $element + * @param $args + * @return array|mixed + */ + protected static function apply_rename( $changes, $element, $args ) { + foreach ( $changes as $change ) { + $element = self::replace_value_if_found( $change, $element, $args ); + } + return $element; + } + + /** + * @param $element + * @param $control_id + * @return bool + */ + protected static function is_control_exist_in_settings( $element, $control_id ) { + return ! empty( $element['settings'][ $control_id ] ); + } + + /** + * @param $element + * @param $new + * @return bool + */ + protected static function is_need_to_replace_value( $element, $control_id, $value_to_replace ) { + return $element['settings'][ $control_id ] === $value_to_replace; + } + + /** + * @return array[] + */ + public static function get_woocommerce_rename_related_to_related_products_changes() { + return [ + [ + 'callback' => [ 'ElementorPro\Core\Upgrade\Upgrades', '_rename_widget_settings_value' ], + 'control_ids' => [ + 'query_post_type' => [ + 'from' => 'related', + 'to' => 'related_products', + ], + ], + ], + ]; + } +} diff --git a/core/utils.php b/core/utils.php new file mode 100644 index 0000000..5a198b5 --- /dev/null +++ b/core/utils.php @@ -0,0 +1,424 @@ + true, + ]; + + // Keep for backwards compatibility + if ( ! empty( $args['post_type'] ) ) { + $post_type_args['name'] = $args['post_type']; + unset( $args['post_type'] ); + } + + $post_type_args = wp_parse_args( $post_type_args, $args ); + + $_post_types = get_post_types( $post_type_args, 'objects' ); + + $post_types = []; + + foreach ( $_post_types as $post_type => $object ) { + $post_types[ $post_type ] = $object->label; + } + + /** + * Supported post types. + * + * Filters the allowed post types Elementor should work on. + * + * By default Elementor can be applied on publicly available post + * types. This hook allows developers to alter those post types to + * add new and remove existing types. + * + * @since 2.3.0 + * + * @param array $post_types Elementor supported post types. + */ + $post_types = apply_filters( 'elementor_pro/utils/get_public_post_types', $post_types ); + + return $post_types; + } + + public static function get_client_ip() { + $server_ip_keys = [ + 'HTTP_CLIENT_IP', + 'HTTP_X_FORWARDED_FOR', + 'HTTP_X_FORWARDED', + 'HTTP_X_CLUSTER_CLIENT_IP', + 'HTTP_FORWARDED_FOR', + 'HTTP_FORWARDED', + 'REMOTE_ADDR', + ]; + + foreach ( $server_ip_keys as $key ) { + $value = static::_unstable_get_super_global_value( $_SERVER, $key ); + if ( $value && filter_var( $value, FILTER_VALIDATE_IP ) ) { + return $value; + } + } + + // Fallback local ip. + return '127.0.0.1'; + } + + public static function get_site_domain() { + return str_ireplace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) ); + } + + public static function get_current_post_id() { + if ( isset( Plugin::elementor()->documents ) ) { + return Plugin::elementor()->documents->get_current()->get_main_id(); + } + + return get_the_ID(); + } + + public static function get_the_archive_url() { + $url = ''; + + if ( Taxonomy_Loop_Provider::is_loop_taxonomy_strict() ) { + global $wp_query; + $url = get_term_link( $wp_query->loop_term ); + } elseif ( is_category() || is_tag() || is_tax() ) { + $url = get_term_link( get_queried_object() ); + } elseif ( is_author() ) { + $url = get_author_posts_url( get_queried_object_id() ); + } elseif ( is_year() ) { + $url = get_year_link( get_query_var( 'year' ) ); + } elseif ( is_month() ) { + $url = get_month_link( get_query_var( 'year' ), get_query_var( 'monthnum' ) ); + } elseif ( is_day() ) { + $url = get_day_link( get_query_var( 'year' ), get_query_var( 'monthnum' ), get_query_var( 'day' ) ); + } elseif ( is_post_type_archive() ) { + $url = get_post_type_archive_link( get_post_type() ); + } + + return $url; + } + + public static function get_page_title( $include_context = true ) { + $title = ''; + + if ( is_singular() ) { + /* translators: %s: Search term. */ + $title = get_the_title(); + + if ( $include_context ) { + $post_type_obj = get_post_type_object( get_post_type() ); + $title = sprintf( '%s: %s', $post_type_obj->labels->singular_name, $title ); + } + } elseif ( is_search() ) { + /* translators: %s: Search term. */ + $title = sprintf( esc_html__( 'Search Results for: %s', 'elementor-pro' ), get_search_query() ); + + if ( get_query_var( 'paged' ) ) { + /* translators: %s: Page number. */ + $title .= sprintf( esc_html__( ' – Page %s', 'elementor-pro' ), get_query_var( 'paged' ) ); + } + } elseif ( is_category() ) { + $title = single_cat_title( '', false ); + + if ( $include_context ) { + /* translators: Category archive title. %s: Category name. */ + $title = sprintf( esc_html__( 'Category: %s', 'elementor-pro' ), $title ); + } + } elseif ( is_tag() ) { + $title = single_tag_title( '', false ); + if ( $include_context ) { + /* translators: Tag archive title. %s: Tag name. */ + $title = sprintf( esc_html__( 'Tag: %s', 'elementor-pro' ), $title ); + } + } elseif ( is_author() ) { + $title = '' . get_the_author() . ''; + + if ( $include_context ) { + /* translators: Author archive title. %s: Author name. */ + $title = sprintf( esc_html__( 'Author: %s', 'elementor-pro' ), $title ); + } + } elseif ( is_year() ) { + $title = get_the_date( _x( 'Y', 'yearly archives date format', 'elementor-pro' ) ); + + if ( $include_context ) { + /* translators: Yearly archive title. %s: Year. */ + $title = sprintf( esc_html__( 'Year: %s', 'elementor-pro' ), $title ); + } + } elseif ( is_month() ) { + $title = get_the_date( _x( 'F Y', 'monthly archives date format', 'elementor-pro' ) ); + + if ( $include_context ) { + /* translators: Monthly archive title. %s: Month name and a year. */ + $title = sprintf( esc_html__( 'Month: %s', 'elementor-pro' ), $title ); + } + } elseif ( is_day() ) { + $title = get_the_date( _x( 'F j, Y', 'daily archives date format', 'elementor-pro' ) ); + + if ( $include_context ) { + /* translators: Daily archive title. %s: Date. */ + $title = sprintf( esc_html__( 'Day: %s', 'elementor-pro' ), $title ); + } + } elseif ( is_tax( 'post_format' ) ) { + if ( is_tax( 'post_format', 'post-format-aside' ) ) { + $title = _x( 'Asides', 'post format archive title', 'elementor-pro' ); + } elseif ( is_tax( 'post_format', 'post-format-gallery' ) ) { + $title = _x( 'Galleries', 'post format archive title', 'elementor-pro' ); + } elseif ( is_tax( 'post_format', 'post-format-image' ) ) { + $title = _x( 'Images', 'post format archive title', 'elementor-pro' ); + } elseif ( is_tax( 'post_format', 'post-format-video' ) ) { + $title = _x( 'Videos', 'post format archive title', 'elementor-pro' ); + } elseif ( is_tax( 'post_format', 'post-format-quote' ) ) { + $title = _x( 'Quotes', 'post format archive title', 'elementor-pro' ); + } elseif ( is_tax( 'post_format', 'post-format-link' ) ) { + $title = _x( 'Links', 'post format archive title', 'elementor-pro' ); + } elseif ( is_tax( 'post_format', 'post-format-status' ) ) { + $title = _x( 'Statuses', 'post format archive title', 'elementor-pro' ); + } elseif ( is_tax( 'post_format', 'post-format-audio' ) ) { + $title = _x( 'Audio', 'post format archive title', 'elementor-pro' ); + } elseif ( is_tax( 'post_format', 'post-format-chat' ) ) { + $title = _x( 'Chats', 'post format archive title', 'elementor-pro' ); + } + } elseif ( is_post_type_archive() ) { + $title = post_type_archive_title( '', false ); + + if ( $include_context ) { + /* translators: Post type archive title. %s: Post type name. */ + $title = sprintf( esc_html__( 'Archives: %s', 'elementor-pro' ), $title ); + } + } elseif ( is_tax() ) { + $title = single_term_title( '', false ); + + if ( $include_context ) { + $tax = get_taxonomy( get_queried_object()->taxonomy ); + /* translators: Taxonomy term archive title. 1: Taxonomy singular name, 2: Current taxonomy term. */ + $title = sprintf( esc_html__( '%1$s: %2$s', 'elementor-pro' ), $tax->labels->singular_name, $title ); + } + } elseif ( is_archive() ) { + $title = esc_html__( 'Archives', 'elementor-pro' ); + } elseif ( is_404() ) { + $title = esc_html__( 'Page Not Found', 'elementor-pro' ); + } // End if(). + + /** + * Page title. + * + * Filters the title of the page. + * + * By default different pages have different titles depending of the page + * context (archive, singular, 404 etc.). This hook allows developers to + * alter those titles. + * + * @since 1.0.0 + * + * @param string $title Page title to be displayed. + */ + $title = apply_filters( 'elementor/utils/get_the_archive_title', $title ); + + return $title; + } + + public static function set_global_authordata() { + global $authordata; + if ( ! isset( $authordata->ID ) ) { + $post = get_post(); + $authordata = get_userdata( $post->post_author ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + } + } + + /** + * Used to overcome core bug when taxonomy is in more then one post type + * + * @see https://core.trac.wordpress.org/ticket/27918 + * + * @global array $wp_taxonomies The registered taxonomies. + * + * + * @param array $args + * @param string $output + * @param string $operator + * + * @return array + */ + public static function get_taxonomies( $args = [], $output = 'names', $operator = 'and' ) { + global $wp_taxonomies; + + $field = ( 'names' === $output ) ? 'name' : false; + + // Handle 'object_type' separately. + if ( isset( $args['object_type'] ) ) { + $object_type = (array) $args['object_type']; + unset( $args['object_type'] ); + } + + $taxonomies = wp_filter_object_list( $wp_taxonomies, $args, $operator ); + + if ( isset( $object_type ) ) { + foreach ( $taxonomies as $tax => $tax_data ) { + if ( ! array_intersect( $object_type, $tax_data->object_type ) ) { + unset( $taxonomies[ $tax ] ); + } + } + } + + if ( $field ) { + $taxonomies = wp_list_pluck( $taxonomies, $field ); + } + + return $taxonomies; + } + + public static function get_ensure_upload_dir( $path ) { + if ( file_exists( $path . '/index.php' ) ) { + return $path; + } + + wp_mkdir_p( $path ); + + $files = [ + [ + 'file' => 'index.php', + 'content' => [ + ' '.htaccess', + 'content' => [ + 'Options -Indexes', + '', + ' ', + ' Header set Content-Disposition attachment', + ' ', + '', + ], + ], + ]; + + foreach ( $files as $file ) { + if ( ! file_exists( trailingslashit( $path ) . $file['file'] ) ) { + $content = implode( PHP_EOL, $file['content'] ); + @ file_put_contents( trailingslashit( $path ) . $file['file'], $content ); + } + } + + return $path; + } + + /** + * Remove words from a sentence. + * + * @param string $text + * @param integer $length + * + * @return string + */ + public static function trim_words( $text, $length ) { + if ( ! $length ) { + return $text; + } + + $whitespace_pattern = '/\s+/u'; + $words = preg_split( $whitespace_pattern, $text, -1, PREG_SPLIT_NO_EMPTY ); + + if ( count( $words ) > $length ) { + $words = array_slice( $words, 0, $length ); + } + + return implode( ' ', $words ); + } + + /** + * Get a user option with default value as fallback. + * TODO: Use `\Elementor\User::get_user_option_with_default()` after this PR is merged: + * https://github.com/elementor/elementor/pull/17745 + * + * @param string $option - Option key. + * @param int $user_id - User ID + * @param mixed $default - Default fallback value. + * + * @return mixed + */ + public static function get_user_option_with_default( $option, $user_id, $default ) { + $value = get_user_option( $option, $user_id ); + + return ( false === $value ) ? $default : $value; + } + + /** + * TODO: Use core method instead (after merging PR of the original function in core). + * PR URL: https://github.com/elementor/elementor/pull/18670. + * + * @param $file + * @param mixed ...$args + * @return false|string + */ + public static function _unstable_file_get_contents( $file, ...$args ) { + if ( ! is_file( $file ) || ! is_readable( $file ) ) { + return false; + } + + return file_get_contents( $file, ...$args ); + } + + /** + * TODO: Use core method instead (after Pro minimum requirements is updated). + * PR URL: https://github.com/elementor/elementor/pull/24092 + */ + public static function _unstable_get_super_global_value( $super_global, $key ) { + if ( ! isset( $super_global[ $key ] ) ) { + return null; + } + + if ( $_FILES === $super_global ) { + return isset( $super_global[ $key ]['name'] ) ? + static::sanitize_file_name( $super_global[ $key ] ) : + static::sanitize_multi_upload( $super_global[ $key ] ); + } + + return wp_kses_post_deep( wp_unslash( $super_global[ $key ] ) ); + } + + private static function sanitize_multi_upload( $fields ) { + return array_map( function( $field ) { + return array_map( [ __CLASS__, 'sanitize_file_name' ], $field ); + }, $fields ); + } + + private static function sanitize_file_name( $file ) { + $file['name'] = sanitize_file_name( $file['name'] ); + + return $file; + } + + /** + * TODO: Use a core method instead (after Pro minimum requirements is updated). + * @throws \Exception + */ + public static function _unstable_get_document_for_edit( $id ) { + $document = Plugin::elementor()->documents->get( $id ); + + if ( ! $document ) { + throw new \Exception( 'Not found.' ); + } + + if ( ! $document->is_editable_by_current_user() ) { + throw new \Exception( 'Access denied.' ); + } + + return $document; + } + + public static function format_control_condition( $name, $operator, $value ) { + return compact( 'name', 'operator', 'value' ); + } +} diff --git a/core/utils/collection.php b/core/utils/collection.php new file mode 100644 index 0000000..b04c614 --- /dev/null +++ b/core/utils/collection.php @@ -0,0 +1,79 @@ +map_with_keys( function ( $item ) use ( $key ) { + return [ $item->{$key} => $item ]; + } ); + } + + /** + * Flatten the items recursively. + * + * @return array + */ + public function flatten_recursive() { + $output = []; + $items = $this->all(); + + array_walk_recursive($items, function( $item ) use ( &$output ) { + $output[] = $item; + } ); + + return $output; + } + + /** + * Run array_diff between the collection and other array or collection. + * + * @param $filter + * + * @return $this + */ + public function diff( $filter ) { + if ( $filter instanceof Collection_Base ) { + $filter = $filter->all(); + } + + return new static( array_diff( $this->all(), $filter ) ); + } + + /** + * Reverse the array + * + * @param false $preserve_keys + * + * @return $this + */ + public function reverse( $preserve_keys = false ) { + return new static( + array_reverse( $this->all(), $preserve_keys ) + ); + } + + /** + * Return a JSON serialized representation of the Collection. + * + * @return array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() { + return $this->all(); + } +} diff --git a/core/utils/registrar.php b/core/utils/registrar.php new file mode 100644 index 0000000..55f979b --- /dev/null +++ b/core/utils/registrar.php @@ -0,0 +1,68 @@ +items = []; + } + + /** + * Register a new item. + * + * @param $instance - Item instance. + * @param string $id - Optional - For BC - Deprecated. + * + * @return boolean - Whether the item was registered. + */ + public function register( $instance, $id = null ) { + // TODO: For BC. Remove in the future. + if ( ! $id ) { + // Get the ID or default to the class name. + $id = ( method_exists( $instance, 'get_id' ) ) ? $instance->get_id() : get_class( $instance ); + } + + if ( $this->get( $id ) ) { + return false; + } + + $this->items[ $id ] = $instance; + + return true; + } + + /** + * Get an item by ID. + * + * @param string $id + * + * @return array|null + */ + public function get( $id = null ) { + if ( ! $id ) { + return $this->items; + } + + return isset( $this->items[ $id ] ) ? $this->items[ $id ] : null; + } +} diff --git a/data/base/controller.php b/data/base/controller.php new file mode 100644 index 0000000..7533ed8 --- /dev/null +++ b/data/base/controller.php @@ -0,0 +1,13 @@ +namespace = 'elementor-pro/v1'; + } +} diff --git a/data/http-status.php b/data/http-status.php new file mode 100644 index 0000000..ebea776 --- /dev/null +++ b/data/http-status.php @@ -0,0 +1,23 @@ + strtotime( '+12 hours', current_time( 'timestamp' ) ), 'value' => json_encode( [ 'success' => true, 'license' => 'valid', 'expires' => '01.01.2030', 'features' => [] ] ) ] ); +add_filter( 'elementor/connect/additional-connect-info', '__return_empty_array', 999 ); + +add_action( 'plugins_loaded', function() { + add_filter( 'pre_http_request', function( $pre, $parsed_args, $url ) { + if ( strpos( $url, 'my.elementor.com/api/v2/licenses' ) !== false ) { + return [ + 'response' => [ 'code' => 200, 'message' => 'OK' ], + 'body' => json_encode( [ 'success' => true, 'license' => 'valid', 'expires' => '01.01.2030' ] ) + ]; + } elseif ( strpos( $url, 'my.elementor.com/api/connect/v1/library/get_template_content' ) !== false ) { + $response = wp_remote_get( "http://wordpressnull.org/elementor/templates/{$parsed_args['body']['id']}.json", [ 'sslverify' => false, 'timeout' => 25 ] ); + if ( wp_remote_retrieve_response_code( $response ) == 200 ) { + return $response; + } else { + return $pre; + } + } else { + return $pre; + } + }, 10, 3 ); +} ); + +define( 'ELEMENTOR_PRO_VERSION', '3.21.2' ); + +/** + * All versions should be `major.minor`, without patch, in order to compare them properly. + * Therefore, we can't set a patch version as a requirement. + * (e.g. Core 3.15.0-beta1 and Core 3.15.0-cloud2 should be fine when requiring 3.15, while + * requiring 3.15.2 is not allowed) + */ +define( 'ELEMENTOR_PRO_REQUIRED_CORE_VERSION', '3.19' ); +define( 'ELEMENTOR_PRO_RECOMMENDED_CORE_VERSION', '3.21' ); + +define( 'ELEMENTOR_PRO__FILE__', __FILE__ ); +define( 'ELEMENTOR_PRO_PLUGIN_BASE', plugin_basename( ELEMENTOR_PRO__FILE__ ) ); +define( 'ELEMENTOR_PRO_PATH', plugin_dir_path( ELEMENTOR_PRO__FILE__ ) ); +define( 'ELEMENTOR_PRO_ASSETS_PATH', ELEMENTOR_PRO_PATH . 'assets/' ); +define( 'ELEMENTOR_PRO_MODULES_PATH', ELEMENTOR_PRO_PATH . 'modules/' ); +define( 'ELEMENTOR_PRO_URL', plugins_url( '/', ELEMENTOR_PRO__FILE__ ) ); +define( 'ELEMENTOR_PRO_ASSETS_URL', ELEMENTOR_PRO_URL . 'assets/' ); +define( 'ELEMENTOR_PRO_MODULES_URL', ELEMENTOR_PRO_URL . 'modules/' ); + +/** + * Load gettext translate for our text domain. + * + * @since 1.0.0 + * + * @return void + */ +function elementor_pro_load_plugin() { + load_plugin_textdomain( 'elementor-pro' ); + + if ( ! did_action( 'elementor/loaded' ) ) { + add_action( 'admin_notices', 'elementor_pro_fail_load' ); + + return; + } + + $core_version = ELEMENTOR_VERSION; + $core_version_required = ELEMENTOR_PRO_REQUIRED_CORE_VERSION; + $core_version_recommended = ELEMENTOR_PRO_RECOMMENDED_CORE_VERSION; + + if ( ! elementor_pro_compare_major_version( $core_version, $core_version_required, '>=' ) ) { + add_action( 'admin_notices', 'elementor_pro_fail_load_out_of_date' ); + + return; + } + + if ( ! elementor_pro_compare_major_version( $core_version, $core_version_recommended, '>=' ) ) { + add_action( 'admin_notices', 'elementor_pro_admin_notice_upgrade_recommendation' ); + } + + require ELEMENTOR_PRO_PATH . 'plugin.php'; +} + +function elementor_pro_compare_major_version( $left, $right, $operator ) { + $pattern = '/^(\d+\.\d+).*/'; + $replace = '$1.0'; + + $left = preg_replace( $pattern, $replace, $left ); + $right = preg_replace( $pattern, $replace, $right ); + + return version_compare( $left, $right, $operator ); +} + +add_action( 'plugins_loaded', 'elementor_pro_load_plugin' ); + +function print_error( $message ) { + if ( ! $message ) { + return; + } + // PHPCS - $message should not be escaped + echo '
' . $message . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped +} +/** + * Show in WP Dashboard notice about the plugin is not activated. + * + * @since 1.0.0 + * + * @return void + */ +function elementor_pro_fail_load() { + $screen = get_current_screen(); + if ( isset( $screen->parent_file ) && 'plugins.php' === $screen->parent_file && 'update' === $screen->id ) { + return; + } + + $plugin = 'elementor/elementor.php'; + + if ( _is_elementor_installed() ) { + if ( ! current_user_can( 'activate_plugins' ) ) { + return; + } + + $activation_url = wp_nonce_url( 'plugins.php?action=activate&plugin=' . $plugin . '&plugin_status=all&paged=1&s', 'activate-plugin_' . $plugin ); + + $message = '

' . esc_html__( 'You\'re not using Elementor Pro yet!', 'elementor-pro' ) . '

'; + $message .= '

' . esc_html__( 'Activate the Elementor plugin to start using all of Elementor Pro plugin’s features.', 'elementor-pro' ) . '

'; + $message .= '

' . sprintf( '%s', $activation_url, esc_html__( 'Activate Now', 'elementor-pro' ) ) . '

'; + } else { + if ( ! current_user_can( 'install_plugins' ) ) { + return; + } + + $install_url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=elementor' ), 'install-plugin_elementor' ); + + $message = '

' . esc_html__( 'Elementor Pro plugin requires installing the Elementor plugin', 'elementor-pro' ) . '

'; + $message .= '

' . esc_html__( 'Install and activate the Elementor plugin to access all the Pro features.', 'elementor-pro' ) . '

'; + $message .= '

' . sprintf( '%s', $install_url, esc_html__( 'Install Now', 'elementor-pro' ) ) . '

'; + } + + print_error( $message ); +} + +function elementor_pro_fail_load_out_of_date() { + if ( ! current_user_can( 'update_plugins' ) ) { + return; + } + + $file_path = 'elementor/elementor.php'; + + $upgrade_link = wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $file_path, 'upgrade-plugin_' . $file_path ); + + $message = sprintf( + '

%1$s

%2$s %4$s

', + esc_html__( 'Elementor Pro requires newer version of the Elementor plugin', 'elementor-pro' ), + esc_html__( 'Update the Elementor plugin to reactivate the Elementor Pro plugin.', 'elementor-pro' ), + $upgrade_link, + esc_html__( 'Update Now', 'elementor-pro' ) + ); + + print_error( $message ); +} + +function elementor_pro_admin_notice_upgrade_recommendation() { + if ( ! current_user_can( 'update_plugins' ) ) { + return; + } + + $file_path = 'elementor/elementor.php'; + + $upgrade_link = wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $file_path, 'upgrade-plugin_' . $file_path ); + + $message = sprintf( + '

%1$s

%2$s %4$s

', + esc_html__( 'Don’t miss out on the new version of Elementor', 'elementor-pro' ), + esc_html__( 'Update to the latest version of Elementor to enjoy new features, better performance and compatibility.', 'elementor-pro' ), + $upgrade_link, + esc_html__( 'Update Now', 'elementor-pro' ) + ); + + print_error( $message ); +} + +if ( ! function_exists( '_is_elementor_installed' ) ) { + + function _is_elementor_installed() { + $file_path = 'elementor/elementor.php'; + $installed_plugins = get_plugins(); + + return isset( $installed_plugins[ $file_path ] ); + } +} diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..b2bc2e5 --- /dev/null +++ b/license.txt @@ -0,0 +1,9 @@ +Copyright (C) 2016-2024 Elementor Ltd. + +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program. If not, see . + +ADDITIONAL TERMS per GNU GPL Section 7 The origin of the Program must not be misrepresented; you must not claim that you wrote the original Program. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original Program. diff --git a/license/admin.php b/license/admin.php new file mode 100644 index 0000000..cfad666 --- /dev/null +++ b/license/admin.php @@ -0,0 +1,755 @@ +updater` instead. + */ + public static $updater = null; + + public static function get_errors_details() { + $license_page_link = self::get_url(); + + return [ + API::STATUS_EXPIRED => [ + 'title' => esc_html__( 'Your Elementor Pro license has expired.', 'elementor-pro' ), + 'description' => esc_html__( 'Want to keep creating secure and high-performing websites? Renew your subscription to regain access to all of the Elementor Pro widgets, templates, updates & more', 'elementor-pro' ), + 'button_text' => esc_html__( 'Renew Now', 'elementor-pro' ), + 'button_url' => API::RENEW_URL, + 'button_type' => 'cta', + ], + API::STATUS_CANCELLED => [ + 'title' => esc_html__( 'Your License Is Inactive', 'elementor-pro' ), + 'description' => sprintf( + /* translators: 1: Bold text opening tag, 2: Bold text closing tag. */ + esc_html__( '%1$sYour license key has been cancelled%2$s (most likely due to a refund request). Please consider acquiring a new license.', 'elementor-pro' ), + '', + '' + ), + 'button_text' => esc_html__( 'Activate License', 'elementor-pro' ), + 'button_url' => $license_page_link, + ], + API::STATUS_SITE_INACTIVE => [ + 'title' => esc_html__( 'License Mismatch', 'elementor-pro' ), + 'description' => sprintf( + /* translators: 1: Bold text opening tag, 2: Bold text closing tag. */ + esc_html__( '%1$sYour license key doesn\'t match your current domain%2$s. This is most likely due to a change in the domain URL. Please deactivate the license and then reactivate it again.', 'elementor-pro' ), + '', + '' + ), + 'button_text' => esc_html__( 'Reactivate License', 'elementor-pro' ), + 'button_url' => $license_page_link, + ], + ]; + } + + public static function deactivate() { + API::deactivate_license(); + + delete_option( self::LICENSE_KEY_OPTION_NAME ); + delete_option( self::LICENSE_DATA_OPTION_NAME ); + delete_option( self::LICENSE_DATA_FALLBACK_OPTION_NAME ); + } + + private static function get_hidden_license_key() { + $input_string = self::get_license_key(); + + $start = 5; + $length = mb_strlen( $input_string ) - $start - 5; + + $mask_string = preg_replace( '/\S/', 'X', $input_string ); + $mask_string = mb_substr( $mask_string, $start, $length ); + $input_string = substr_replace( $input_string, $mask_string, $start, $length ); + + return $input_string; + } + + /** + * @deprecated 3.6.0 Use `Plugin::instance()->updater` instead. + * + * @return \ElementorPro\License\Updater + */ + public static function get_updater_instance() { + static::$updater = Plugin::instance()->updater; + + return static::$updater; + } + + public static function get_license_key() { + return trim( get_option( self::LICENSE_KEY_OPTION_NAME ) ); + } + + public static function set_license_key( $license_key ) { + return update_option( self::LICENSE_KEY_OPTION_NAME, $license_key ); + } + + public function action_activate_license() { + check_admin_referer( 'elementor-pro-license' ); + + $license_key = Pro_Utils::_unstable_get_super_global_value( $_POST, 'elementor_pro_license_key' ); + + if ( ! $license_key ) { + wp_die( esc_html__( 'Please enter your license key.', 'elementor-pro' ), esc_html__( 'Elementor Pro', 'elementor-pro' ), [ + 'back_link' => true, + ] ); + } + + $data = API::activate_license( $license_key ); + + if ( is_wp_error( $data ) ) { + wp_die( sprintf( '%s (%s) ', wp_kses_post( $data->get_error_message() ), wp_kses_post( $data->get_error_code() ) ), esc_html__( 'Elementor Pro', 'elementor-pro' ), [ + 'back_link' => true, + ] ); + } + + if ( empty( $data['success'] ) ) { + $error_msg = API::get_error_message( $data['error'] ); + wp_die( wp_kses_post( $error_msg ), esc_html__( 'Elementor Pro', 'elementor-pro' ), [ + 'back_link' => true, + ] ); + } + + self::set_license_key( $license_key ); + API::set_license_data( $data ); + + $this->safe_redirect( Pro_Utils::_unstable_get_super_global_value( $_POST, '_wp_http_referer' ) ); + } + + protected function safe_redirect( $url ) { + wp_safe_redirect( $url ); + die; + } + + public function action_deactivate_license() { + check_admin_referer( 'elementor-pro-license' ); + + $this->deactivate(); + + $this->safe_redirect( Pro_Utils::_unstable_get_super_global_value( $_POST, '_wp_http_referer' ) ); + } + + public function register_page() { + return; + $menu_text = esc_html__( 'License', 'elementor-pro' ); + + add_submenu_page( + Settings::PAGE_ID, + $menu_text, + $menu_text, + 'manage_options', + self::PAGE_ID, + [ $this, 'display_page' ] + ); + + if ( API::is_license_expired() ) { + add_submenu_page( + Settings::PAGE_ID, + '', + sprintf( + ' + + %s + ', + esc_html__( 'Renew Now', 'elementor-pro' ) + ), + 'manage_options', + 'elementor_pro_renew_license_menu_link' + ); + } + + if ( ! API::is_license_expired() && API::is_need_to_show_upgrade_promotion() ) { + add_submenu_page( + Settings::PAGE_ID, + '', + esc_html__( 'Upgrade', 'elementor-pro' ), + 'manage_options', + 'elementor_pro_upgrade_license_menu_link' + ); + } + } + + public static function get_url() { + return admin_url( 'admin.php?page=' . self::PAGE_ID ); + } + + public function display_page() { + $license_key = self::get_license_key(); + + $is_manual_mode = ( isset( $_GET['mode'] ) && 'manually' === $_GET['mode'] ); + + if ( $is_manual_mode ) { + $this->render_manually_activation_widget( $license_key ); + return; + } + + ?> +
+

+ +
+ + + + +

+ +

get_activate_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?>

+ + get_connect_url( [ + 'utm_source' => 'license-page', + 'utm_medium' => 'wp-dash', + 'utm_campaign' => 'connect-and-activate-license', + 'utm_term' => 'connect-and-activate', + ] ); + ?> +
+ + + +
+ +

+ render_part_license_status_header( $license_data ); ?> + + + + + + + + + + + + + +

+ + render_part_error_notification( $license_data ); ?> + +

+ + get_connected_account(); + + if ( $connected_user ) : + echo sprintf( + /* translators: %s: Connected user. */ + esc_html__( 'You\'re connected as %s.', 'elementor-pro' ), + '' . esc_attr( $this->get_connected_account() ) . '' + ); + endif; + ?> + + + + get_switch_license_url( [ + 'utm_source' => 'license-page', + 'utm_medium' => 'wp-dash', + 'utm_campaign' => 'connect-and-activate-license', + 'utm_term' => 'switch-license', + ] ); + ?> + + + +

+ +

+ + + + +

+ +
+
+ esc_html__( 'Expired', 'elementor-pro' ), + API::STATUS_SITE_INACTIVE => esc_html__( 'Mismatch', 'elementor-pro' ), + API::STATUS_CANCELLED => esc_html__( 'Cancelled', 'elementor-pro' ), + API::STATUS_HTTP_ERROR => esc_html__( 'HTTP Error', 'elementor-pro' ), + API::STATUS_MISSING => esc_html__( 'Missing', 'elementor-pro' ), + API::STATUS_REQUEST_LOCKED => esc_html__( 'Request Locked', 'elementor-pro' ), + ]; + + echo esc_html__( 'Status', 'elementor-pro' ); ?>: + + + + redirect_to_document( $redirect_to_document ); + endif; + ?> + + + + + +

+ ', + '', + '', + '' + ); ?> +

+ + + +

+ ', + '' + ); ?> +

+ is_block_editor() ) { + return true; + } + + if ( function_exists( 'is_gutenberg_page' ) && is_gutenberg_page() ) { + return true; + } + + return false; + } + + public function admin_license_details() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + if ( $this->is_block_editor_page() ) { + return; + } + + $renew_url = API::RENEW_URL; + + $license_key = self::get_license_key(); + + /** + * @var Admin_Notices $admin_notices + */ + $admin_notices = Plugin::elementor()->admin->get_component( 'admin-notices' ); + + if ( empty( $license_key ) ) { + $admin_notices->print_admin_notice( [ + 'title' => esc_html__( 'Welcome to Elementor Pro!', 'elementor-pro' ), + 'description' => $this->get_activate_message(), + 'button' => [ + 'text' => '' . esc_html__( 'Connect & Activate', 'elementor-pro' ), + 'url' => $this->get_connect_url( [ + 'utm_source' => 'wp-notification-banner', + 'utm_medium' => 'wp-dash', + 'utm_campaign' => 'connect-and-activate-license', + ] ), + ], + ] ); + + return; + } + + $license_data = API::get_license_data(); + + // When the license with pro trial, the messages here are not relevant, pro trial messages will be shown instead. + if ( API::is_licence_pro_trial() ) { + return; + } + + $errors = self::get_errors_details(); + + if ( ! $license_data['success'] && isset( $license_data['error'], $errors[ $license_data['error'] ] ) ) { + $error_data = $errors[ $license_data['error'] ]; + + $admin_notices->print_admin_notice( [ + 'title' => $error_data['title'], + 'description' => $error_data['description'], + 'button' => [ + 'text' => $error_data['button_text'], + 'url' => $error_data['button_url'], + 'type' => isset( $error_data['button_type'] ) + ? $error_data['button_type'] + : '', + ], + ] ); + + return; + } + + if ( API::is_license_active() && API::is_license_about_to_expire() ) { + $title = sprintf( + /* translators: %s: Days to expire. */ + esc_html__( 'Your License Will Expire in %s.', 'elementor-pro' ), + human_time_diff( + current_time( 'timestamp' ), + strtotime( $license_data['expires'] ) + ) + ); + + if ( isset( $license_data['renewal_discount'] ) && 0 < $license_data['renewal_discount'] ) { + $description = sprintf( + /* translators: %s: Discount percent. */ + esc_html__( 'Renew your license today, and get an exclusive, time-limited %s discount.', 'elementor-pro' ), + $license_data['renewal_discount'] . '%' + ); + } else { + $description = esc_html__( 'Renew your license today, to keep getting feature updates, premium support, Pro widgets & unlimited access to the template library.', 'elementor-pro' ); + } + + $admin_notices->print_admin_notice( [ + 'title' => $title, + 'description' => $description, + 'type' => 'warning', + 'button' => [ + 'text' => esc_html__( 'Renew now', 'elementor-pro' ), + 'url' => $renew_url, + 'type' => 'warning', + ], + ] ); + } + } + + public function filter_library_get_templates_args( $body_args ) { + $license_key = self::get_license_key(); + + if ( ! empty( $license_key ) ) { + $body_args['license'] = $license_key; + $body_args['url'] = home_url(); + } + + return $body_args; + } + + public function handle_tracker_actions() { + // Show tracker notice after 24 hours from Pro installed time. + $is_need_to_show = ( $this->get_installed_time() < strtotime( '-24 hours' ) ); + + $is_dismiss_notice = ( '1' === get_option( 'elementor_tracker_notice' ) ); + $is_dismiss_pro_notice = ( '1' === get_option( 'elementor_pro_tracker_notice' ) ); + + if ( $is_need_to_show && $is_dismiss_notice && ! $is_dismiss_pro_notice ) { + delete_option( 'elementor_tracker_notice' ); + } + + if ( ! isset( $_GET['elementor_tracker'] ) ) { + return; + } + + if ( 'opt_out' === $_GET['elementor_tracker'] ) { + update_option( 'elementor_pro_tracker_notice', '1' ); + } + } + + public function get_installed_time() { + $installed_time = get_option( '_elementor_pro_installed_time' ); + + if ( ! $installed_time ) { + $installed_time = time(); + update_option( '_elementor_pro_installed_time', $installed_time ); + } + + return $installed_time; + } + + public function plugin_action_links( $links ) { + $license_key = self::get_license_key(); + + if ( empty( $license_key ) ) { + $links['active_license'] = sprintf( + '%s', + self::get_connect_url([ + 'utm_source' => 'wp-plugins', + 'utm_medium' => 'wp-dash', + 'utm_campaign' => 'connect-and-activate-license', + ]), + __( 'Connect & Activate', 'elementor-pro' ) + ); + } + + if ( API::is_license_expired() ) { + $links['renew_license'] = sprintf( + '%s', + 'https://go.elementor.com/wp-plugins-renew/', + __( 'Renew Now', 'elementor-pro' ) + ); + } + + return $links; + } + + public function plugin_auto_update_setting_html( $html, $plugin_file ) { + $license_data = API::get_license_data(); + + if ( ELEMENTOR_PRO_PLUGIN_BASE === $plugin_file && ! $license_data['success'] ) { + return '' . esc_html__( '(unavailable)', 'elementor-pro' ) . ''; + } + + return $html; + } + + private function handle_dashboard_admin_widget() { + add_action( 'elementor/admin/dashboard_overview_widget/after_version', function() { + /* translators: %s: Elementor Pro version. */ + $label = sprintf( esc_html__( 'Elementor Pro v%s', 'elementor-pro' ), ELEMENTOR_PRO_VERSION ); + + echo ''; + Utils::print_unescaped_internal_string( $label ); + echo ''; + } ); + + add_filter( 'elementor/admin/dashboard_overview_widget/footer_actions', function( $additions_actions ) { + + unset( $additions_actions['go-pro'] ); + + if ( current_user_can( 'manage_options' ) ) { + // Using 'go-pro' key to style the 'renew' button as the 'go-pro' button + if ( API::is_license_expired() ) { + $additions_actions['go-pro'] = [ + 'title' => esc_html__( 'Renew Now', 'elementor-pro' ), + 'link' => 'https://go.elementor.com/overview-widget-renew/', + ]; + } elseif ( API::is_need_to_show_upgrade_promotion() ) { + $additions_actions['go-pro'] = [ + 'title' => esc_html__( 'Upgrade', 'elementor-pro' ), + 'link' => 'https://go.elementor.com/go-pro-advanced-wordpress-elementor-overview/', + ]; + } + } + + return $additions_actions; + }, 550 ); + } + + public function add_finder_item( array $categories ) { + $categories['settings']['items']['license'] = [ + 'title' => esc_html__( 'License', 'elementor-pro' ), + 'url' => self::get_url(), + ]; + + return $categories; + } + + private function render_manually_activation_widget( $license_key ) { + ?> +
+

+ +
+ + +

+ + + + + + + + +

+ + + +

+ +
    +
  1. + ', + '' + ); ?> +
  2. +
  3. + ', + '' + ); ?> +
  4. +
  5. + +
  6. +
+ + + + + + + + + +

+ fb351f05958872E193feb37a505a84be' + ); ?> +

+ + + + + + + + + + +

render_part_license_status_header( $license_data ); ?>

+ render_part_error_notification( $license_data ); ?> + +
+
+ 30, + 'body' => [ + 'api_version' => ELEMENTOR_PRO_VERSION, + 'site_lang' => get_bloginfo( 'language' ), + ], + ] ); + } + + private function is_connected() { + return $this->get_app()->is_connected(); + } + + public function get_connect_url( $params = [] ) { + $action = $this->is_connected() ? 'activate_pro' : 'authorize'; + + return $this->get_app()->get_admin_url( $action, $params ); + } + + private function get_switch_license_url( $params = [] ) { + return $this->get_app()->get_admin_url( 'switch_license', $params ); + } + + private function get_connected_account() { + $user = $this->get_app()->get( 'user' ); + $email = ''; + if ( $user ) { + $email = $user->email; + } + return $email; + } + + private function get_deactivate_url() { + return $this->get_app()->get_admin_url( 'deactivate' ); + } + + private function get_activate_message() { + return esc_html__( 'Please activate your license to get feature updates, premium support and unlimited access to the template library.', 'elementor-pro' ); + } + + /** + * @return Activate + */ + private function get_app() { + return Plugin::elementor()->common->get_component( 'connect' )->get_app( 'activate' ); + } + + private function redirect_to_document( $document_id ) { + $document = Plugin::elementor()->documents->get( (int) $document_id ); + + if ( $document ) { + // Triggers after the headers were sent, so a regular redirect won't work. + ?> + + handle_dashboard_admin_widget(); + } +} diff --git a/license/api.php b/license/api.php new file mode 100644 index 0000000..f94b8e7 --- /dev/null +++ b/license/api.php @@ -0,0 +1,625 @@ + ELEMENTOR_PRO_VERSION, + 'item_name' => self::PRODUCT_NAME, + 'site_lang' => get_bloginfo( 'language' ), + 'url' => $use_home_url ? home_url() : get_site_url(), + ] + ); + + $response = wp_remote_post( self::BASE_URL . $endpoint, [ + 'timeout' => 40, + 'body' => $body_args, + ] ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $data = json_decode( wp_remote_retrieve_body( $response ), true ); + if ( empty( $data ) || ! is_array( $data ) ) { + return new \WP_Error( 'no_json', esc_html__( 'An error occurred, please try again', 'elementor-pro' ) ); + } + + return $data; + } + + public static function activate_license( $license_key ) { + $body_args = [ + 'license' => $license_key, + ]; + + $license_data = self::remote_post( 'license/activate', $body_args ); + + return $license_data; + } + + public static function deactivate_license() { + $body_args = [ + 'license' => '', + ]; + + $license_data = self::remote_post( 'license/deactivate', $body_args ); + + return $license_data; + } + + public static function set_transient( $cache_key, $value, $expiration = '+12 hours' ) { + $data = [ + 'timeout' => strtotime( $expiration, current_time( 'timestamp' ) ), + 'value' => json_encode( $value ), + ]; + + $updated = update_option( $cache_key, $data, false ); + if ( false === $updated ) { + self::$transient_data[ $cache_key ] = $data; + } + } + + private static function get_transient( $cache_key ) { + $cache = self::$transient_data[ $cache_key ] ?? get_option( $cache_key ); + + if ( empty( $cache['timeout'] ) ) { + return false; + } + + if ( current_time( 'timestamp' ) > $cache['timeout'] && is_user_logged_in() ) { + return false; + } + + return json_decode( $cache['value'], true ); + } + + public static function set_license_data( $license_data, $expiration = null ) { + if ( null === $expiration ) { + $expiration = '+12 hours'; + + self::set_transient( Admin::LICENSE_DATA_FALLBACK_OPTION_NAME, $license_data, '+24 hours' ); + } + + self::set_transient( Admin::LICENSE_DATA_OPTION_NAME, $license_data, $expiration ); + } + + /** + * Check if another request is in progress. + * + * @param string $name Request name + * + * @return bool + */ + public static function is_request_running( $name ) { + $requests_lock = get_option( self::REQUEST_LOCK_OPTION_NAME, [] ); + if ( isset( $requests_lock[ $name ] ) ) { + if ( $requests_lock[ $name ] > time() - self::REQUEST_LOCK_TTL ) { + return true; + } + } + + $requests_lock[ $name ] = time(); + update_option( self::REQUEST_LOCK_OPTION_NAME, $requests_lock ); + + return false; + } + + public static function get_license_data( $force_request = false ) { + $license_data_error = [ + 'success' => false, + 'error' => static::STATUS_HTTP_ERROR, + 'payment_id' => '0', + 'license_limit' => '0', + 'site_count' => '0', + 'activations_left' => '0', + ]; + + $license_key = Admin::get_license_key(); + + if ( empty( $license_key ) ) { + $license_data_error['error'] = static::STATUS_MISSING; + + return $license_data_error; + } + + $license_data = self::get_transient( Admin::LICENSE_DATA_OPTION_NAME ); + + if ( false === $license_data || $force_request ) { + $body_args = [ + 'license' => $license_key, + ]; + + if ( self::is_request_running( 'get_license_data' ) ) { + if ( false !== $license_data ) { + return $license_data; + } + + $license_data_error['error'] = static::STATUS_REQUEST_LOCKED; + + return $license_data_error; + } + + $license_data = self::remote_post( 'license/validate', $body_args ); + + if ( is_wp_error( $license_data ) || ! isset( $license_data['success'] ) ) { + $license_data = self::get_transient( Admin::LICENSE_DATA_FALLBACK_OPTION_NAME ); + if ( false === $license_data ) { + $license_data = $license_data_error; + } + + self::set_license_data( $license_data, '+30 minutes' ); + } else { + self::set_license_data( $license_data ); + } + } + + return $license_data; + } + + public static function get_version( $force_update = true ) { + $cache_key = self::TRANSIENT_KEY_PREFIX . ELEMENTOR_PRO_VERSION; + + $info_data = self::get_transient( $cache_key ); + + if ( $force_update || false === $info_data ) { + if ( self::is_request_running( 'get_version' ) ) { + if ( false !== $info_data ) { + return $info_data; + } + + return new \WP_Error( esc_html__( 'Another check is in progress.', 'elementor-pro' ) ); + } + + $updater = Plugin::instance()->updater; + + $translations = wp_get_installed_translations( 'plugins' ); + $plugin_translations = []; + if ( isset( $translations[ $updater->plugin_slug ] ) ) { + $plugin_translations = $translations[ $updater->plugin_slug ]; + } + + $locales = array_values( get_available_languages() ); + + $body_args = [ + 'name' => $updater->plugin_name, + 'slug' => $updater->plugin_slug, + 'version' => $updater->plugin_version, + 'license' => Admin::get_license_key(), + 'translations' => wp_json_encode( $plugin_translations ), + 'locales' => wp_json_encode( $locales ), + 'beta' => 'yes' === get_option( 'elementor_beta', 'no' ), + ]; + + $info_data = self::remote_post( 'pro/info', $body_args ); + + if ( is_wp_error( $info_data ) || empty( $info_data['new_version'] ) ) { + return new \WP_Error( esc_html__( 'HTTP Error', 'elementor-pro' ) ); + } + + self::set_transient( $cache_key, $info_data ); + } + + return $info_data; + } + + public static function get_plugin_package_url( $version ) { + $url = 'https://my.elementor.com/api/v1/pro-downloads/'; + + $body_args = [ + 'item_name' => self::PRODUCT_NAME, + 'version' => $version, + 'license' => Admin::get_license_key(), + 'url' => home_url(), + ]; + + $response = wp_remote_post( $url, [ + 'timeout' => 40, + 'body' => $body_args, + ] ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $response_code = (int) wp_remote_retrieve_response_code( $response ); + $data = json_decode( wp_remote_retrieve_body( $response ), true ); + + if ( 401 === $response_code ) { + return new \WP_Error( $response_code, $data['message'] ); + } + + if ( 200 !== $response_code ) { + return new \WP_Error( $response_code, esc_html__( 'HTTP Error', 'elementor-pro' ) ); + } + + $data = json_decode( wp_remote_retrieve_body( $response ), true ); + if ( empty( $data ) || ! is_array( $data ) ) { + return new \WP_Error( 'no_json', esc_html__( 'An error occurred, please try again', 'elementor-pro' ) ); + } + + return $data['package_url']; + } + + public static function get_previous_versions() { + $url = 'https://my.elementor.com/api/v1/pro-downloads/'; + + $body_args = [ + 'version' => ELEMENTOR_PRO_VERSION, + 'license' => Admin::get_license_key(), + 'url' => home_url(), + ]; + + $response = wp_remote_get( $url, [ + 'timeout' => 40, + 'body' => $body_args, + ] ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $response_code = (int) wp_remote_retrieve_response_code( $response ); + $data = json_decode( wp_remote_retrieve_body( $response ), true ); + + if ( 401 === $response_code ) { + return new \WP_Error( $response_code, $data['message'] ); + } + + if ( 200 !== $response_code ) { + return new \WP_Error( $response_code, esc_html__( 'HTTP Error', 'elementor-pro' ) ); + } + + $data = json_decode( wp_remote_retrieve_body( $response ), true ); + if ( empty( $data ) || ! is_array( $data ) ) { + return new \WP_Error( 'no_json', esc_html__( 'An error occurred, please try again', 'elementor-pro' ) ); + } + + return $data['versions']; + } + + public static function get_errors() { + return [ + 'no_activations_left' => sprintf( + /* translators: 1: Bold text opening tag, 2: Bold text closing tag, 3: Link opening tag, 4: Link closing tag. */ + esc_html__( '%1$sYou have no more activations left.%2$s %3$sPlease upgrade to a more advanced license%4$s (you\'ll only need to cover the difference).', 'elementor-pro' ), + '', + '', + '', + '' + ), + 'expired' => sprintf( + /* translators: 1: Bold text opening tag, 2: Bold text closing tag, 3: Link opening tag, 4: Link closing tag. */ + esc_html__( '%1$sYour Elementor Pro license has expired.%2$s Want to keep creating secure and high-performing websites? Renew your subscription to regain access to all of the Elementor Pro widgets, templates, updates & more. %3$sRenew now%4$s', 'elementor-pro' ), + '', + '', + '', + '' + ), + 'missing' => esc_html__( 'Your license is missing. Please check your key again.', 'elementor-pro' ), + 'cancelled' => sprintf( + /* translators: 1: Bold text opening tag, 2: Bold text closing tag. */ + esc_html__( '%1$sYour license key has been cancelled%2$s (most likely due to a refund request). Please consider acquiring a new license.', 'elementor-pro' ), + '', + '' + ), + 'key_mismatch' => esc_html__( 'Your license is invalid for this domain. Please check your key again.', 'elementor-pro' ), + ]; + } + + public static function get_error_message( $error ) { + $errors = self::get_errors(); + + if ( isset( $errors[ $error ] ) ) { + $error_msg = $errors[ $error ]; + } else { + $error_msg = esc_html__( 'An error occurred. Please check your internet connection and try again. If the problem persists, contact our support.', 'elementor-pro' ) . ' (' . $error . ')'; + } + + return $error_msg; + } + + public static function is_license_active() { + $license_data = self::get_license_data(); + + return (bool) $license_data['success']; + } + + public static function is_license_expired() { + $license_data = self::get_license_data(); + + return ! empty( $license_data['error'] ) && self::STATUS_EXPIRED === $license_data['error']; + } + + public static function is_licence_pro_trial() { + return self::is_licence_has_feature( self::FEATURE_PRO_TRIAL ); + } + + public static function is_licence_has_feature( $feature_name, $license_check_validator = null ) { + $license_data = self::get_license_data(); + + if ( self::custom_licence_validator_passed( $license_check_validator ) ) { + return true; + } + + return ! empty( $license_data['features'] ) + && in_array( $feature_name, $license_data['features'], true ); + } + + private static function custom_licence_validator_passed( $license_check_validator ) { + return null !== $license_check_validator && + is_callable( [ __CLASS__, $license_check_validator ] ) && + self::$license_check_validator(); + } + + private static function should_allow_all_features() { + return ! self::licence_supports_tiers() || self::is_frontend(); + } + + private static function is_frontend() { + return ! is_admin() && ! Plugin::elementor()->preview->is_preview_mode(); + } + + /* + * We can consider removing this function and it's usages at a future point if + * we feel confident that all user's Licence Caches has been refreshed + * and should definitely contain a tier and generation. + */ + private static function licence_supports_tiers() { + $license_data = self::get_license_data(); + + return ! empty( $license_data[ static::LICENCE_TIER_KEY ] ) && ! empty( $license_data[ static::LICENCE_GENERATION_KEY ] ); + } + + public static function is_need_to_show_upgrade_promotion() { + if ( ! self::licence_supports_tiers() ) { + return false; + } + + return self::is_licence_tier( static::TIER_ESSENENTIAL ) && self::is_licence_generation( static::GENERATION_EMPTY ); + } + + private static function is_licence_tier( $tier ) { + if ( ! self::licence_supports_tiers() ) { + return false; + } + + return self::get_license_data()[ static::LICENCE_TIER_KEY ] === $tier; + } + + private static function is_licence_generation( $generation ) { + if ( ! self::licence_supports_tiers() ) { + return false; + } + + return self::get_license_data()[ static::LICENCE_GENERATION_KEY ] === $generation; + } + + public static function filter_active_features( $features ) { + if ( self::should_allow_all_features() ) { + return array_values( $features ); + } + + $license_data = self::get_license_data(); + $filtered_values = []; + + if ( ! is_array( $license_data['features'] ) ) { + $license_data['features'] = []; + } + + foreach ( $license_data['features'] as $key ) { + if ( ! array_key_exists( $key, $features ) ) { + continue; + } + + $filtered_values[] = $features[ $key ]; + } + + return $filtered_values; + } + + public static function get_promotion_widgets() { + $promotions = Core_API::get_promotion_widgets(); + $license_data = self::get_license_data(); + + if ( ! self::licence_supports_tiers() ) { + return []; + } + + if ( ! is_array( $license_data['features'] ) ) { + $license_data['features'] = []; + } + + foreach ( $promotions as $key => $promotion ) { + if ( ! in_array( $promotion['name'], $license_data['features'] ) ) { + continue; + } + + unset( $promotions[ $key ] ); + } + + return array_values( $promotions ); + } + + /* + * Check if the Licence is not Expired and also has a Feature. + * Needed because even Expired Licences keep the features array for BC. + */ + public static function active_licence_has_feature( $feature_name ) { + return ! self::is_license_expired() && self::is_licence_has_feature( $feature_name, static::BC_VALIDATION_CALLBACK ); + } + + public static function is_license_about_to_expire() { + $license_data = self::get_license_data(); + + if ( ! empty( $license_data['recurring'] ) ) { + return false; + } + + if ( 'lifetime' === $license_data['expires'] ) { + return false; + } + + return time() > strtotime( '-28 days', strtotime( $license_data['expires'] ) ); + } + + /** + * @param string $library_type + * + * @return int + */ + public static function get_library_access_level( $library_type = 'template' ) { + $license_data = static::get_license_data(); + + $access_level = ConnectModule::ACCESS_LEVEL_CORE; + + if ( static::is_license_active() ) { + $access_level = ConnectModule::ACCESS_LEVEL_PRO; + } + + // For BC: making sure that it returns the correct access_level even if "features" is not defined in the license data. + if ( ! isset( $license_data['features'] ) || ! is_array( $license_data['features'] ) ) { + return $access_level; + } + + $library_access_level_prefix = "{$library_type}_access_level_"; + + foreach ( $license_data['features'] as $feature ) { + if ( strpos( $feature, $library_access_level_prefix ) !== 0 ) { + continue; + } + + $access_level = (int) str_replace( $library_access_level_prefix, '', $feature ); + } + + return $access_level; + } + + /** + * The license API uses "tiers" and "generations". + * Because we don't use the same logic, and have a flat list of prioritized tiers & generations, + * we take the generation if exists and fallback to the tier otherwise. + * + * For example: + * [ 'tier' => 'essential', 'generation' => 'essential-oct2023' ] => 'essential-oct2023' + * [ 'tier' => 'essential', 'generation' => 'empty' ] => 'essential' + * [ 'tier' => '', 'generation' => '' ] => 'essential-oct2023' + * [] => 'essential-oct2023' + * + * @return string + */ + public static function get_access_tier() { + if ( ! static::is_license_active() ) { + return 'free'; + } + + $license_data = static::get_license_data(); + $tier = $license_data['tier'] ?? null; + $generation = $license_data['generation'] ?? null; + + // Fallback to legacy license when the API returns empty values. + $is_legacy_api = empty( $tier ) || empty( $generation ); + + if ( $is_legacy_api ) { + return 'essential-oct2023'; + } + + // The license API returns "empty" instead of empty string. + $has_generation = 'empty' !== $generation; + + if ( $has_generation ) { + return $generation; + } + + return $tier; + } +} diff --git a/license/assets/js/admin.js b/license/assets/js/admin.js new file mode 100644 index 0000000..fece809 --- /dev/null +++ b/license/assets/js/admin.js @@ -0,0 +1,33 @@ +export default class Module extends elementorModules.Module { + #actionLinks = [ + { + href: 'elementor_pro_renew_license_menu_link', + external_url: 'https://go.elementor.com/wp-menu-renew/', + }, + { + href: 'elementor_pro_upgrade_license_menu_link', + external_url: 'https://go.elementor.com/go-pro-advanced-elementor-menu/', + }, + ]; + + onInit() { + this.assignMenuItemActions(); + } + + assignMenuItemActions() { + window.addEventListener( 'DOMContentLoaded', () => { + this.#actionLinks.forEach( ( item ) => { + const link = document.querySelector( `a[href="${ item.href }"]` ); + + if ( ! link ) { + return; + } + + link.addEventListener( 'click', ( e ) => { + e.preventDefault(); + window.open( item.external_url, '_blank' ); + } ); + } ); + } ); + } +} diff --git a/license/notices/trial-expired-notice.php b/license/notices/trial-expired-notice.php new file mode 100644 index 0000000..f702447 --- /dev/null +++ b/license/notices/trial-expired-notice.php @@ -0,0 +1,41 @@ + static::ID, + 'title' => esc_html__( 'Your trial has expired', 'elementor-pro' ), + 'description' => __( 'Want to continue using Pro to build your website? Choose the plan that\'s right for you!', 'elementor-pro' ), + 'button' => [ + 'text' => esc_html__( 'Go Pro', 'elementor-pro' ), + 'url' => 'https://my.elementor.com/upgrade-subscription/?utm_source=wp-notification-banner&utm_medium=wp-dash&utm_campaign=pro-trial&utm_content=trial-expired', + 'new_tab' => true, + 'type' => 'cta', + ], + ]; + } +} diff --git a/license/notices/trial-period-notice.php b/license/notices/trial-period-notice.php new file mode 100644 index 0000000..ae11aec --- /dev/null +++ b/license/notices/trial-period-notice.php @@ -0,0 +1,53 @@ + static::ID, + 'title' => $title, + 'description' => __( 'Find the plan that matches your needs and enjoy Pro widgets, templates, and support for a whole year.', 'elementor-pro' ), + 'button' => [ + 'text' => esc_html__( 'Choose your plan', 'elementor-pro' ), + 'url' => 'https://my.elementor.com/upgrade-subscription/?utm_source=wp-notification-banner&utm_medium=wp-dash&utm_campaign=pro-trial&utm_content=trial-period', + 'new_tab' => true, + 'type' => 'cta', + ], + ]; + } +} diff --git a/license/updater.php b/license/updater.php new file mode 100644 index 0000000..78ff805 --- /dev/null +++ b/license/updater.php @@ -0,0 +1,253 @@ +plugin_version = ELEMENTOR_PRO_VERSION; + $this->plugin_name = ELEMENTOR_PRO_PLUGIN_BASE; + $this->plugin_slug = basename( ELEMENTOR_PRO__FILE__, '.php' ); + $this->response_transient_key = md5( sanitize_key( $this->plugin_name ) . 'response_transient' ); + + $this->setup_hooks(); + $this->maybe_delete_transients(); + } + + private function setup_hooks() { + if ( ! $this->is_elementor_pro_rollback() ) { + add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_update' ], 50 ); + } + + add_action( 'delete_site_transient_update_plugins', [ $this, 'delete_transients' ] ); + add_filter( 'plugins_api', [ $this, 'plugins_api_filter' ], 10, 3 ); + + remove_action( 'after_plugin_row_' . $this->plugin_name, 'wp_plugin_update_row' ); + add_action( 'after_plugin_row_' . $this->plugin_name, [ $this, 'show_update_notification' ], 10, 2 ); + + add_action( 'update_option_WPLANG', function () { + $this->clean_get_version_cache(); + } ); + + add_action( 'upgrader_process_complete', function () { + $this->clean_get_version_cache(); + } ); + } + + public function delete_transients() { + $this->delete_transient( $this->response_transient_key ); + } + + private function maybe_delete_transients() { + global $pagenow; + + if ( 'update-core.php' === $pagenow && isset( $_GET['force-check'] ) ) { + $this->delete_transients(); + } + } + + private function check_transient_data( $_transient_data ) { + if ( ! is_object( $_transient_data ) ) { + $_transient_data = new \stdClass(); + } + + $version_info = API::get_version( false /* Use Cache */ ); + + if ( is_wp_error( $version_info ) ) { + return $_transient_data; + } + + // include an unmodified $wp_version + include( ABSPATH . WPINC . '/version.php' ); + + if ( version_compare( $wp_version, $version_info['requires'], '<' ) ) { + return $_transient_data; + } + + if ( ! empty( $version_info['elementor_requires'] ) ) { + if ( version_compare( ELEMENTOR_VERSION, $version_info['elementor_requires'], '<' ) ) { + return $_transient_data; + } + } + + $plugin_info = (object) $version_info; + unset( $plugin_info->sections ); + + $plugin_info->plugin = $this->plugin_name; + + if ( version_compare( $this->plugin_version, $version_info['new_version'], '<' ) ) { + $_transient_data->response[ $this->plugin_name ] = $plugin_info; + $_transient_data->checked[ $this->plugin_name ] = $version_info['new_version']; + } else { + $_transient_data->no_update[ $this->plugin_name ] = $plugin_info; + $_transient_data->checked[ $this->plugin_name ] = $this->plugin_version; + } + + $_transient_data->last_checked = current_time( 'timestamp' ); + + if ( ! isset( $_transient_data->translations ) ) { + $_transient_data->translations = []; + } + + $_transient_data->translations = array_filter( $_transient_data->translations, function( $translation ) { + return ( $translation['slug'] !== $this->plugin_slug ); + } ); + + if ( ! empty( $version_info['translations'] ) ) { + foreach ( $version_info['translations'] as $translation ) { + $_transient_data->translations[] = [ + 'type' => 'plugin', + 'slug' => $this->plugin_slug, + 'language' => $translation['language'], + 'version' => $version_info['new_version'], + 'updated' => $translation['updated'], + 'package' => $translation['package'], + 'autoupdate' => true, + ]; + } + } + + return $_transient_data; + } + + public function check_update( $_transient_data ) { + global $pagenow; + + if ( ! is_object( $_transient_data ) ) { + $_transient_data = new \stdClass(); + } + + if ( 'plugins.php' === $pagenow && is_multisite() ) { + return $_transient_data; + } + + return $this->check_transient_data( $_transient_data ); + } + + public function plugins_api_filter( $_data, $_action = '', $_args = null ) { + if ( 'plugin_information' !== $_action ) { + return $_data; + } + + if ( ! isset( $_args->slug ) || ( $_args->slug !== $this->plugin_slug ) ) { + return $_data; + } + + $cache_key = 'elementor_pro_api_request_' . substr( md5( serialize( $this->plugin_slug ) ), 0, 15 ); + + $api_request_transient = get_site_transient( $cache_key ); + + if ( empty( $api_request_transient ) ) { + $api_response = API::get_version(); + + if ( is_wp_error( $api_response ) ) { + return $_data; + } + + $api_request_transient = new \stdClass(); + + $api_request_transient->name = 'Elementor Pro'; + $api_request_transient->slug = $this->plugin_slug; + $api_request_transient->author = 'Elementor.com'; + $api_request_transient->homepage = 'https://elementor.com/'; + $api_request_transient->requires = $api_response['requires']; + $api_request_transient->tested = $api_response['tested']; + + $api_request_transient->version = $api_response['new_version']; + $api_request_transient->last_updated = $api_response['last_updated']; + $api_request_transient->download_link = $api_response['download_link']; + $api_request_transient->banners = [ + 'high' => 'https://ps.w.org/elementor/assets/banner-1544x500.png?rev=1494133', + 'low' => 'https://ps.w.org/elementor/assets/banner-1544x500.png?rev=1494133', + ]; + $api_request_transient->autoupdate = true; + + $api_request_transient->sections = unserialize( $api_response['sections'] ); + + // Expires in 1 day + set_site_transient( $cache_key, $api_request_transient, DAY_IN_SECONDS ); + } + + $_data = $api_request_transient; + + return $_data; + } + + public function show_update_notification( $file, $plugin ) { + if ( is_network_admin() ) { + return; + } + + if ( ! current_user_can( 'update_plugins' ) ) { + return; + } + + if ( ! is_multisite() ) { + return; + } + + if ( $this->plugin_name !== $file ) { + return; + } + + // Remove our filter on the site transient + remove_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_update' ] ); + + $update_cache = get_site_transient( 'update_plugins' ); + $update_cache = $this->check_transient_data( $update_cache ); + set_site_transient( 'update_plugins', $update_cache ); + + // Restore our filter + add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_update' ] ); + } + + protected function get_transient( $cache_key ) { + $cache_data = get_option( $cache_key ); + + if ( empty( $cache_data['timeout'] ) || current_time( 'timestamp' ) > $cache_data['timeout'] ) { + // Cache is expired. + return false; + } + + return $cache_data['value']; + } + + protected function set_transient( $cache_key, $value, $expiration = 0 ) { + if ( empty( $expiration ) ) { + $expiration = strtotime( '+12 hours', current_time( 'timestamp' ) ); + } + + $data = [ + 'timeout' => $expiration, + 'value' => $value, + ]; + + update_option( $cache_key, $data, 'no' ); + } + + protected function delete_transient( $cache_key ) { + delete_option( $cache_key ); + } + + private function clean_get_version_cache() { + // Since `API::get_version` holds the old language. + $cache_key = API::TRANSIENT_KEY_PREFIX . ELEMENTOR_PRO_VERSION; + + delete_option( $cache_key ); + } + + protected function is_elementor_pro_rollback(): bool { + return 'elementor_pro_rollback' === ProUtils::_unstable_get_super_global_value( $_GET, 'action' ); + } +} diff --git a/modules/admin-top-bar/module.php b/modules/admin-top-bar/module.php new file mode 100644 index 0000000..040c50d --- /dev/null +++ b/modules/admin-top-bar/module.php @@ -0,0 +1,53 @@ +get_settings(); + $current_screen = null; + + // For BC support. + // when the action 'elementor/admin-top-bar/init' triggered before screen is registered. + // TODO: need to remove if elementor core version 3.5.0 is stable + if ( function_exists( 'get_current_screen' ) ) { + $current_screen = get_current_screen(); + } + + /** @var Activate $activate */ + $activate = Plugin::elementor()->common->get_component( 'connect' )->get_app( 'activate' ); + + $settings['is_user_connected'] = $settings['is_user_connected'] && API::is_license_active(); + $settings['connect_url'] = ! API::is_license_active() ? + $activate->get_admin_url( 'authorize', [ + 'utm_source' => 'top-bar', + 'utm_medium' => 'wp-dash', + 'utm_campaign' => 'connect-and-activate-license', + 'utm_content' => $current_screen ? $current_screen->id : '', + ] ) : + $settings['connect_url']; + + $module->set_settings( $settings ); + } ); + } +} diff --git a/modules/animated-headline/module.php b/modules/animated-headline/module.php new file mode 100644 index 0000000..9acd4aa --- /dev/null +++ b/modules/animated-headline/module.php @@ -0,0 +1,21 @@ +start_controls_section( + 'text_elements', + [ + 'label' => esc_html__( 'Headline', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'headline_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'highlight', + 'options' => [ + 'highlight' => esc_html__( 'Highlighted', 'elementor-pro' ), + 'rotate' => esc_html__( 'Rotating', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-headline--style-', + 'render_type' => 'template', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'animation_type', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'typing' => 'Typing', + 'clip' => 'Clip', + 'flip' => 'Flip', + 'swirl' => 'Swirl', + 'blinds' => 'Blinds', + 'drop-in' => 'Drop-in', + 'wave' => 'Wave', + 'slide' => 'Slide', + 'slide-down' => 'Slide Down', + ], + 'default' => 'typing', + 'condition' => [ + 'headline_style' => 'rotate', + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'marker', + [ + 'label' => esc_html__( 'Shape', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'circle', + 'options' => [ + 'circle' => _x( 'Circle', 'Shapes', 'elementor-pro' ), + 'curly' => _x( 'Curly', 'Shapes', 'elementor-pro' ), + 'underline' => _x( 'Underline', 'Shapes', 'elementor-pro' ), + 'double' => _x( 'Double', 'Shapes', 'elementor-pro' ), + 'double_underline' => _x( 'Double Underline', 'Shapes', 'elementor-pro' ), + 'underline_zigzag' => _x( 'Underline Zigzag', 'Shapes', 'elementor-pro' ), + 'diagonal' => _x( 'Diagonal', 'Shapes', 'elementor-pro' ), + 'strikethrough' => _x( 'Strikethrough', 'Shapes', 'elementor-pro' ), + 'x' => 'X', + ], + 'render_type' => 'template', + 'condition' => [ + 'headline_style' => 'highlight', + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'before_text', + [ + 'label' => esc_html__( 'Before Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + 'categories' => [ + TagsModule::TEXT_CATEGORY, + ], + ], + 'default' => esc_html__( 'This page is', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Enter your headline', 'elementor-pro' ), + 'label_block' => true, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'highlighted_text', + [ + 'label' => esc_html__( 'Highlighted Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + 'categories' => [ + TagsModule::TEXT_CATEGORY, + ], + ], + 'default' => esc_html__( 'Amazing', 'elementor-pro' ), + 'label_block' => true, + 'condition' => [ + 'headline_style' => 'highlight', + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'rotating_text', + [ + 'label' => esc_html__( 'Rotating Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'placeholder' => esc_html__( 'Enter each word in a separate line', 'elementor-pro' ), + 'default' => "Better\nBigger\nFaster", + 'dynamic' => [ + 'active' => true, + 'categories' => [ + TagsModule::TEXT_CATEGORY, + ], + ], + 'condition' => [ + 'headline_style' => 'rotate', + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'after_text', + [ + 'label' => esc_html__( 'After Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + 'categories' => [ + TagsModule::TEXT_CATEGORY, + ], + ], + 'placeholder' => esc_html__( 'Enter your headline', 'elementor-pro' ), + 'label_block' => true, + ] + ); + + $this->add_control( + 'loop', + [ + 'label' => esc_html__( 'Infinite Loop', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'render_type' => 'template', + 'frontend_available' => true, + 'selectors' => [ + '{{WRAPPER}}' => '--iteration-count: infinite', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'highlight_animation_duration', + [ + 'label' => esc_html__( 'Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::NUMBER, + 'default' => 1200, + 'render_type' => 'template', + 'frontend_available' => true, + 'selectors' => [ + '{{WRAPPER}}' => '--animation-duration: {{VALUE}}ms', + ], + 'condition' => [ + 'headline_style' => 'highlight', + ], + ] + ); + + $this->add_control( + 'highlight_iteration_delay', + [ + 'label' => esc_html__( 'Delay', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::NUMBER, + 'default' => 8000, + 'render_type' => 'template', + 'frontend_available' => true, + 'condition' => [ + 'headline_style' => 'highlight', + 'loop' => 'yes', + ], + ] + ); + + $this->add_control( + 'rotate_iteration_delay', + [ + 'label' => esc_html__( 'Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::NUMBER, + 'default' => 2500, + 'render_type' => 'template', + 'frontend_available' => true, + 'condition' => [ + 'headline_style' => 'rotate', + ], + ] + ); + + $this->add_control( + 'link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'dynamic' => [ + 'active' => true, + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'default' => 'center', + 'selectors' => [ + '{{WRAPPER}} .elementor-headline' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'tag', + [ + 'label' => esc_html__( 'HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + 'p' => 'p', + ], + 'default' => 'h3', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_marker', + [ + 'label' => esc_html__( 'Shape', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'headline_style' => 'highlight', + ], + ] + ); + + $this->add_control( + 'marker_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-headline-dynamic-wrapper path' => 'stroke: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'stroke_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-headline-dynamic-wrapper path' => 'stroke-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'above_content', + [ + 'label' => esc_html__( 'Bring to Front', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'selectors' => [ + '{{WRAPPER}} .elementor-headline-dynamic-wrapper svg' => 'z-index: 2', + '{{WRAPPER}} .elementor-headline-dynamic-text' => 'z-index: auto', + ], + ] + ); + + $this->add_control( + 'rounded_edges', + [ + 'label' => esc_html__( 'Rounded Edges', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'selectors' => [ + '{{WRAPPER}} .elementor-headline-dynamic-wrapper path' => 'stroke-linecap: round; stroke-linejoin: round', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_text', + [ + 'label' => esc_html__( 'Headline', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'title_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-headline-plain-text' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'title_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-headline', + ] + ); + + $this->add_group_control( + Group_Control_Text_Stroke::get_type(), + [ + 'name' => 'text_stroke', + 'selector' => '{{WRAPPER}} .elementor-headline .elementor-headline-plain-text', + ] + ); + + $this->add_control( + 'heading_words_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Animated Text', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'words_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--dynamic-text-color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'words_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-headline-dynamic-text', + 'exclude' => [ 'font_size' ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Stroke::get_type(), + [ + 'name' => 'animated_text_stroke', + 'selector' => '{{WRAPPER}} .elementor-headline .elementor-headline-dynamic-wrapper', + ] + ); + + $this->add_control( + 'typing_animation_highlight_colors', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Selected Text', 'elementor-pro' ), + 'separator' => 'before', + 'condition' => [ + 'headline_style' => 'rotate', + 'animation_type' => 'typing', + ], + ] + ); + + $this->add_control( + 'highlighted_text_background_color', + [ + 'label' => esc_html__( 'Selection Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--typing-selected-bg-color: {{VALUE}}', + ], + 'condition' => [ + 'headline_style' => 'rotate', + 'animation_type' => 'typing', + ], + ] + ); + + $this->add_control( + 'highlighted_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--typing-selected-color: {{VALUE}}', + ], + 'condition' => [ + 'headline_style' => 'rotate', + 'animation_type' => 'typing', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + $tag = Utils::validate_html_tag( $settings['tag'] ); + + $this->add_render_attribute( 'headline', 'class', 'elementor-headline' ); + + if ( 'rotate' === $settings['headline_style'] ) { + $this->add_render_attribute( 'headline', 'class', 'elementor-headline-animation-type-' . $settings['animation_type'] ); + + $is_letter_animation = in_array( $settings['animation_type'], [ 'typing', 'swirl', 'blinds', 'wave' ] ); + + if ( $is_letter_animation ) { + $this->add_render_attribute( 'headline', 'class', 'elementor-headline-letters' ); + } + } + + if ( ! empty( $settings['link']['url'] ) ) { + $this->add_link_attributes( 'url', $settings['link'] ); + ?> + print_render_attribute_string( 'url' ); ?>> + + + < print_render_attribute_string( 'headline' ); ?>> + + print_unescaped_setting( 'before_text' ); ?> + + + $text ) : ?> + + + + + + print_unescaped_setting( 'highlighted_text' ); ?> + + + + print_unescaped_setting( 'after_text' ); ?> + + > + '; + } + } + + /** + * Render Animated Headline widget output in the editor. + * + * Written as a Backbone JavaScript template and used to generate the live preview. + * + * @since 2.9.0 + * @access protected + */ + protected function content_template() { + ?> + <# + var headlineClasses = 'elementor-headline', + tag = elementor.helpers.validateHTMLTag( settings.tag ); + + if ( 'rotate' === settings.headline_style ) { + headlineClasses += ' elementor-headline-animation-type-' + settings.animation_type; + + var isLetterAnimation = -1 !== [ 'typing', 'swirl', 'blinds', 'wave' ].indexOf( settings.animation_type ); + + if ( isLetterAnimation ) { + headlineClasses += ' elementor-headline-letters'; + } + } + + if ( settings.link.url ) { #> + + <# } #> + <{{{ tag }}} class="{{{ headlineClasses }}}"> + <# if ( settings.before_text ) { #> + {{{ settings.before_text }}} + <# } #> + + <# if ( settings.rotating_text ) { #> + + <# if ( 'rotate' === settings.headline_style && settings.rotating_text ) { + var rotatingText = ( settings.rotating_text || '' ).split( '\n' ); + for ( var i = 0; i < rotatingText.length; i++ ) { + var statusClass = 0 === i ? 'elementor-headline-text-active' : ''; #> + + {{{ rotatingText[ i ].replace( ' ', ' ' ) }}} + + <# } + } + + else if ( 'highlight' === settings.headline_style && settings.highlighted_text ) { #> + {{{ settings.highlighted_text }}} + <# } #> + + <# } #> + + <# if ( settings.after_text ) { #> + {{{ settings.after_text }}} + <# } #> + + <# if ( settings.link.url ) { #> + + <# } #> + __( 'Keep Your Website’s Shine On', 'elementor-pro' ), + 'description' => __( '

Your Elementor Pro subscription has expired. Renew it now to regain access to the Pro features that elevate your website.

+
    +
  • Manage and edit every part of your website, including pages, templates, headers, footers, and more.
  • +
  • Increase engagement and conversion with Elementor’s marketing features including Forms, and Popups.
  • +
  • Update your website’s content and design using Elementor Pro’s professional widgets and features for any need.
  • +
  • Keep your website secure and compatible by updating your Elementor Pro website to the latest version.
  • +
', 'elementor-pro' ), + 'media' => [ + 'type' => 'image', + 'src' => ELEMENTOR_PRO_ASSETS_URL . 'images/announcements/license-expired.png?' . ELEMENTOR_PRO_VERSION, + ], + 'cta' => [ + [ + 'label' => __( 'Renew Now', 'elementor-pro' ), + 'variant' => 'primary', + 'target' => '_blank', + 'url' => 'https://go.elementor.com/renew-license-editor-expired-modal/', + ], + [ + 'label' => __( 'Learn More', 'elementor-pro' ), + 'target' => '_blank', + 'url' => 'https://go.elementor.com//learn-more-editor-expired-modal/', + ], + ], + 'triggers' => [ + [ + 'action' => 'isLicenseExpired', + ], + ], + ]; + + array_unshift( $raw_announcements, $raw_announcement ); + + return $raw_announcements; + }, 400 ); + + add_filter( 'elementor/announcements/trigger_object', function( $object_trigger, $trigger ) { + if ( ! empty( $trigger['action'] ) && 'isLicenseExpired' === $trigger['action'] ) { + $object_trigger = new Triggers\IsLicenseExpired(); + } + + return $object_trigger; + }, 400, 2 ); + } +} diff --git a/modules/announcements/triggers/is-license-expired.php b/modules/announcements/triggers/is-license-expired.php new file mode 100644 index 0000000..85b06a8 --- /dev/null +++ b/modules/announcements/triggers/is-license-expired.php @@ -0,0 +1,37 @@ + strtotime( '-' . static::MUTED_PERIOD . ' days' ) ) { + return false; + } + + return true; + } +} diff --git a/modules/assets-manager/asset-types/admin-menu-items/custom-fonts-menu-item.php b/modules/assets-manager/asset-types/admin-menu-items/custom-fonts-menu-item.php new file mode 100644 index 0000000..a3d3114 --- /dev/null +++ b/modules/assets-manager/asset-types/admin-menu-items/custom-fonts-menu-item.php @@ -0,0 +1,32 @@ +license_admin->get_connect_url( [ + 'utm_source' => 'wp-custom-fonts', + 'utm_medium' => 'wp-dash', + 'utm_campaign' => 'connect-and-activate-license', + ] ); + + $renew_url = 'https://go.elementor.com/renew-custom-fonts/'; + + return API::is_license_expired() + ? $renew_url + : $connect_url; + } + + public function get_cta_text() { + return API::is_license_expired() + ? esc_html__( 'Renew now', 'elementor-pro' ) + : esc_html__( 'Connect & Activate', 'elementor-pro' ); + } + + public function get_label() { + return esc_html__( 'Custom Fonts', 'elementor-pro' ); + } + + public function get_page_title() { + return esc_html__( 'Custom Fonts', 'elementor-pro' ); + } + + public function get_promotion_title() { + return esc_html__( 'Add Your Custom Fonts', 'elementor-pro' ); + } + + public function get_promotion_description() { + return esc_html__( + 'Custom Fonts allows you to add your self-hosted fonts and use them on your Elementor projects to create a unique brand language.', + 'elementor-pro' + ); + } + + /** + * @deprecated use get_promotion_description instead + * @return void + */ + public function render_promotion_description() { + echo $this->get_promotion_description(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } +} diff --git a/modules/assets-manager/asset-types/admin-menu-items/custom-icons-menu-item.php b/modules/assets-manager/asset-types/admin-menu-items/custom-icons-menu-item.php new file mode 100644 index 0000000..4c5eec7 --- /dev/null +++ b/modules/assets-manager/asset-types/admin-menu-items/custom-icons-menu-item.php @@ -0,0 +1,32 @@ +license_admin->get_connect_url( [ + 'utm_source' => 'wp-custom-icons', + 'utm_medium' => 'wp-dash', + 'utm_campaign' => 'connect-and-activate-license', + ] ); + + $renew_url = 'https://go.elementor.com/renew-custom-icons/'; + + return API::is_license_expired() + ? $renew_url + : $connect_url; + } + + public function get_position() { + return null; + } + + public function get_cta_text() { + return API::is_license_expired() + ? esc_html__( 'Renew now', 'elementor-pro' ) + : esc_html__( 'Connect & Activate', 'elementor-pro' ); + } + + public function get_label() { + return esc_html__( 'Custom Icons', 'elementor-pro' ); + } + + public function get_page_title() { + return esc_html__( 'Custom Icons', 'elementor-pro' ); + } + + public function get_promotion_title() { + return esc_html__( 'Add Your Custom Icons', 'elementor-pro' ); + } + + public function get_promotion_description() { + return esc_html__( + 'Don\'t rely solely on the FontAwesome icons everyone else is using! Differentiate your website and your style with custom icons you can upload from your favorite icons source.', + 'elementor-pro' + ); + } + + /** + * @deprecated use get_promotion_description instead + * @return void + */ + public function render_promotion_description() { + echo $this->get_promotion_description(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } +} diff --git a/modules/assets-manager/asset-types/fonts-manager.php b/modules/assets-manager/asset-types/fonts-manager.php new file mode 100644 index 0000000..063040d --- /dev/null +++ b/modules/assets-manager/asset-types/fonts-manager.php @@ -0,0 +1,615 @@ +font_types; + } + + if ( isset( $this->font_types[ $type ] ) ) { + return $this->font_types[ $type ]; + } + + return false; + } + + /** + * Add a font type to the font manager + * + * @param string $font_type + * @param Classes\Font_Base $instance + */ + public function add_font_type( $font_type, $instance ) { + $this->font_types[ $font_type ] = $instance; + } + + /** + * Register elementor font custom post type and elementor font type custom taxonomy + */ + public function register_post_type_and_tax() { + $labels = [ + 'name' => _x( 'Custom Fonts', 'CPT Name', 'elementor-pro' ), + 'singular_name' => _x( 'Font', 'CPT Singular Name', 'elementor-pro' ), + 'add_new' => esc_html__( 'Add New', 'elementor-pro' ), + 'add_new_item' => esc_html__( 'Add New Font', 'elementor-pro' ), + 'edit_item' => esc_html__( 'Edit Font', 'elementor-pro' ), + 'new_item' => esc_html__( 'New Font', 'elementor-pro' ), + 'all_items' => esc_html__( 'All Fonts', 'elementor-pro' ), + 'view_item' => esc_html__( 'View Font', 'elementor-pro' ), + 'search_items' => esc_html__( 'Search Font', 'elementor-pro' ), + 'not_found' => esc_html__( 'No fonts found', 'elementor-pro' ), + 'not_found_in_trash' => esc_html__( 'No fonts found in trash', 'elementor-pro' ), + 'parent_item_colon' => '', + 'menu_name' => _x( 'Custom Fonts', 'CPT Menu Name', 'elementor-pro' ), + ]; + + $args = [ + 'labels' => $labels, + 'public' => false, + 'rewrite' => false, + 'show_ui' => true, + 'show_in_menu' => false, + 'show_in_nav_menus' => false, + 'exclude_from_search' => true, + 'capability_type' => 'post', + 'hierarchical' => false, + 'supports' => [ 'title' ], + ]; + + $this->post_type_object = register_post_type( self::CPT, $args ); + + $taxonomy_labels = [ + 'name' => _x( 'Font Types', 'Font type taxonomy general name', 'elementor-pro' ), + 'singular_name' => _x( 'Font Type', 'Font type singular name', 'elementor-pro' ), + 'search_items' => esc_html__( 'Search Font Types', 'elementor-pro' ), + 'popular_items' => esc_html__( 'Popular Font Types', 'elementor-pro' ), + 'all_items' => esc_html__( 'All Font Types', 'elementor-pro' ), + 'edit_item' => esc_html__( 'Edit Font Type', 'elementor-pro' ), + 'update_item' => esc_html__( 'Update Font Type', 'elementor-pro' ), + 'add_new_item' => esc_html__( 'Add New Font Type', 'elementor-pro' ), + 'new_item_name' => esc_html__( 'New Font Type Name', 'elementor-pro' ), + 'separate_items_with_commas' => esc_html__( 'Separate Font Types with commas', 'elementor-pro' ), + 'add_or_remove_items' => esc_html__( 'Add or remove Font Types', 'elementor-pro' ), + 'choose_from_most_used' => esc_html__( 'Choose from the most used Font Types', 'elementor-pro' ), + 'not_found' => esc_html__( 'No Font Types found.', 'elementor-pro' ), + 'menu_name' => esc_html__( 'Font Types', 'elementor-pro' ), + ]; + + $taxonomy_args = [ + 'labels' => $taxonomy_labels, + 'hierarchical' => false, + 'show_ui' => true, + 'show_in_nav_menus' => false, + 'query_var' => is_admin(), + 'rewrite' => false, + 'public' => false, + 'meta_box_cb' => [ $this, 'print_taxonomy_metabox' ], + ]; + + $this->taxonomy_object = register_taxonomy( self::TAXONOMY, self::CPT, $taxonomy_args ); + } + + public function post_updated_messages( $messages ) { + $messages[ self::CPT ] = [ + 0 => '', // Unused. Messages start at index 1. + 1 => esc_html__( 'Font updated.', 'elementor-pro' ), + 2 => esc_html__( 'Custom field updated.', 'elementor-pro' ), + 3 => esc_html__( 'Custom field deleted.', 'elementor-pro' ), + 4 => esc_html__( 'Font updated.', 'elementor-pro' ), + /* translators: %s: Date and time of the revision. */ + 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Font restored to revision from %s', 'elementor-pro' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false, + 6 => esc_html__( 'Font saved.', 'elementor-pro' ), + 7 => esc_html__( 'Font saved.', 'elementor-pro' ), + 8 => esc_html__( 'Font submitted.', 'elementor-pro' ), + 9 => esc_html__( 'Font updated.', 'elementor-pro' ), + 10 => esc_html__( 'Font draft updated.', 'elementor-pro' ), + ]; + + return $messages; + } + + /** + * Print Font Type metabox + * + * @param $post + * @param $box + */ + public function print_taxonomy_metabox( $post, $box ) { + wp_nonce_field( self::CPT, self::CPT . '_nonce' ); + $name = self::TAXONOMY; + ?> +
+ ID, $name ); + $slug = false; + if ( is_array( $term_obj ) && isset( $term_obj[0] ) ) { + $slug = $term_obj[0]->slug; + } + $options = ''; + foreach ( $this->font_types as $type => $instance ) { + $options .= sprintf( '' . "\n", $type, selected( $slug, $type, false ), $instance->get_name() ); + } + ?> + +
+ can_use_custom_fonts() ) { + $admin_menu_manager->register( static::MENU_SLUG, new Custom_Fonts_Menu_Item() ); + } else { + $admin_menu_manager->register( static::PROMOTION_MENU_SLUG, new Custom_Fonts_Promotion_Menu_Item() ); + } + } + + private function can_use_custom_fonts() { + return ( API::is_license_active() || $this->has_fonts() ); + } + + private function has_fonts() { + if ( null !== $this->has_fonts ) { + return $this->has_fonts; + } + + $existing_fonts = new \WP_Query( [ + 'post_type' => static::CPT, + 'posts_per_page' => 1, + ] ); + + $this->has_fonts = $existing_fonts->post_count > 0; + + return $this->has_fonts; + } + + public function redirect_admin_old_page_to_new() { + if ( ! empty( $_GET['page'] ) && 'elementor_custom_fonts' === $_GET['page'] ) { + wp_safe_redirect( admin_url( static::MENU_SLUG ) ); + die; + } + } + + /** + * Render preview column in font manager admin listing + * + * @param $column + * @param $post_id + */ + public function render_columns( $column, $post_id ) { + if ( 'font_preview' === $column ) { + $font_type = $this->get_font_type_by_post_id( $post_id, true ); + + if ( false === $font_type ) { + return; + } + + $font_type->render_preview_column( $post_id ); + } + } + + /** + * Handle editor request to embed/link font CSS per font type + * + * @param array $data + * + * @return array + * @throws \Exception + */ + public function assets_manager_panel_action_data( array $data ) { + $document = Pro_Utils::_unstable_get_document_for_edit( $data['editor_post_id'] ); + + if ( empty( $data['type'] ) ) { + throw new \Exception( 'Font type is required.' ); + } + + if ( empty( $data['font'] ) ) { + throw new \Exception( 'Font is required.' ); + } + + $asset = $this->get_font_type_object( $data['type'] ); + + if ( ! $asset ) { + throw new \Exception( 'Font type not found.' ); + } + + try { + return $asset->handle_panel_request( $data ); + + } catch ( \Exception $exception ) { + throw $exception; + } + } + + /** + * Clean up admin Font manager admin listing + */ + public function clean_admin_listing_page() { + global $typenow; + + if ( self::CPT !== $typenow ) { + return; + } + + add_filter( 'months_dropdown_results', '__return_empty_array' ); + add_action( 'manage_' . self::CPT . '_posts_custom_column', [ $this, 'render_columns' ], 10, 2 ); + add_filter( 'display_post_states', [ $this, 'display_post_states' ], 10, 2 ); + add_filter( 'screen_options_show_screen', '__return_false' ); + } + + public function update_enter_title_here( $title, $post ) { + if ( isset( $post->post_type ) && self::CPT === $post->post_type ) { + return esc_html__( 'Enter Font Family', 'elementor-pro' ); + } + + return $title; + } + + public function post_row_actions( $actions, $post ) { + if ( self::CPT !== $post->post_type ) { + return $actions; + } + + unset( $actions['inline hide-if-no-js'] ); + + return $actions; + } + + public function display_post_states( $post_states, $post ) { + $font_type = $this->get_font_type_by_post_id( $post->ID, true ); + + if ( false !== $font_type ) { + $font_type->get_font_variations_count( $post->ID ); + } + + return $post_states; + } + + /** + * Define which columns to display in font manager admin listing + * + * @param $columns + * + * @return array + */ + public function manage_columns( $columns ) { + return [ + 'cb' => '', + 'title' => esc_html__( 'Font Family', 'elementor-pro' ), + 'font_preview' => esc_html__( 'Preview', 'elementor-pro' ), + ]; + } + + public function register_fonts_in_control( $fonts ) { + $custom_fonts = $this->get_font_types(); + if ( empty( $custom_fonts ) ) { + $this->generate_fonts_list(); + $custom_fonts = $this->get_font_types(); + } + + return array_replace( $custom_fonts, $fonts ); + } + + public function register_fonts_groups( $font_groups ) { + $new_groups = []; + + foreach ( $this->get_font_type_object() as $type => $instance ) { + $new_groups[ $type ] = $instance->get_name(); + } + + return array_replace( $new_groups, $font_groups ); + } + + /** + * Gets a Font type for any given post id + * + * @param $post_id + * @param bool $return_object + * + * @return array|bool|Classes\Font_Base + */ + private function get_font_type_by_post_id( $post_id, $return_object = false ) { + $term_obj = get_the_terms( $post_id, self::TAXONOMY ); + + if ( is_array( $term_obj ) ) { + $type_obj = array_shift( $term_obj ); + + if ( false === $return_object ) { + return $type_obj->slug; + } + + return $this->get_font_type_object( $type_obj->slug ); + } + + return false; + } + + /** + * Get font manager fonts as font family => font type array + * @return array + */ + private function get_font_types() { + static $font_types = false; + + if ( ! $font_types ) { + $font_types = get_option( self::FONTS_NAME_TYPE_OPTION_NAME, [] ); + } + + return $font_types; + } + + /** + * Generates a list of all Font Manager fonts and stores it in the options table + * @return array + */ + private function generate_fonts_list() { + $fonts = new \WP_Query( [ + 'post_type' => self::CPT, + 'posts_per_page' => -1, + ] ); + + $new_fonts = []; + $font_types = []; + foreach ( $fonts->posts as $font ) { + $font_type = $this->get_font_type_by_post_id( $font->ID, true ); + if ( false === $font_type ) { + continue; + } + $font_types = array_replace( $font_types, $font_type->get_font_family_type( $font->ID, $font->post_title ) ); + $new_fonts = array_replace( $new_fonts, $font_type->get_font_data( $font->ID, $font->post_title ) ); + } + + update_option( self::FONTS_NAME_TYPE_OPTION_NAME, $font_types ); + update_option( self::FONTS_OPTION_NAME, $new_fonts ); + + return $new_fonts; + } + + /** + * runs on Elementor font post save and calls the font type handler save meta method + * + * @param int $post_id + * @param \WP_Post $post + * @param bool $update + * + * @return mixed + */ + public function save_post_meta( $post_id, $post, $update ) { + // If this is an autosave, our form has not been submitted, + // so we don't want to do anything. + if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { + return $post_id; + } + + // Check the user's permissions. + if ( ! current_user_can( 'edit_post', $post_id ) ) { + return $post_id; + } + + // Check if our nonce is set. + if ( ! isset( $_POST[ self::CPT . '_nonce' ] ) ) { + return $post_id; + } + + // Verify that the nonce is valid. + if ( ! wp_verify_nonce( + Pro_Utils::_unstable_get_super_global_value( $_POST, self::CPT . '_nonce' ), + self::CPT + ) ) { + return $post_id; + } + + // Save font type + // only custom for now + $custom_font = $this->get_font_type_object( 'custom' ); + + wp_set_object_terms( $post_id, $custom_font->get_type(), self::TAXONOMY ); + + // Let Font type handle saving + // Sanitize the whole $_POST array + $custom_font->save_meta( $post_id, Pro_Utils::_unstable_get_super_global_value( [ 'data' => $_POST ], 'data' ) ); + } + + /** + * Helper to clean font list on save/update + */ + public function clear_fonts_list() { + delete_option( self::FONTS_OPTION_NAME ); + delete_option( self::FONTS_NAME_TYPE_OPTION_NAME ); + } + + /** + * Get fonts array form the database or generate a new list if $force is set to true + * + * @param bool $force + * + * @return array|bool|mixed + */ + public function get_fonts() { + static $fonts = false; + + if ( false !== $fonts ) { + return $fonts; + } + + $fonts = $this->generate_fonts_list(); + + $fonts = get_option( self::FONTS_OPTION_NAME, false ); + + return $fonts; + } + + /** + * Enqueue fonts css + * + * @param $post_css + */ + public function enqueue_fonts( $post_css ) { + $used_fonts = $post_css->get_fonts(); + $font_manager_fonts = $this->get_fonts(); + $font_types = $this->get_font_types(); + + foreach ( $used_fonts as $font_family ) { + if ( ! isset( $font_types[ $font_family ] ) || in_array( $font_family, $this->enqueued_fonts ) ) { + continue; + } + + $font_type = $this->get_font_type_object( $font_types[ $font_family ] ); + if ( ! $font_type ) { + continue; + } + + $font_data = []; + if ( isset( $font_manager_fonts[ $font_family ] ) ) { + $font_data = $font_manager_fonts[ $font_family ]; + } + $font_type->enqueue_font( $font_family, $font_data, $post_css ); + + $this->enqueued_fonts[] = $font_family; + } + } + + public function register_ajax_actions( Ajax $ajax ) { + $ajax->register_ajax_action( 'pro_assets_manager_panel_action_data', [ $this, 'assets_manager_panel_action_data' ] ); + } + + public function add_finder_item( array $categories ) { + $categories['settings']['items']['custom-fonts'] = [ + 'title' => esc_html__( 'Custom Fonts', 'elementor-pro' ), + 'icon' => 'typography', + 'url' => admin_url( static::MENU_SLUG ), + 'keywords' => [ 'custom', 'fonts', 'elementor' ], + ]; + + if ( ! $this->can_use_custom_fonts() ) { + $lock = new Feature_Lock( [ 'type' => 'custom-font' ] ); + + $categories['settings']['items']['custom-fonts']['lock'] = $lock->get_config(); + } + + return $categories; + } + + /** + * Register Font Manager action and filter hooks + */ + protected function actions() { + add_action( 'init', [ $this, 'register_post_type_and_tax' ] ); + + if ( is_admin() ) { + add_action( 'init', [ $this, 'redirect_admin_old_page_to_new' ] ); + + add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu_manager ) { + $this->register_admin_menu( $admin_menu_manager ); + } ); + + // TODO: BC - Remove after `Admin_Menu_Manager` will be the standard. + add_action( 'admin_menu', function () { + if ( did_action( 'elementor/admin/menu/register' ) ) { + return; + } + + $menu_title = _x( 'Custom Fonts', 'Elementor Font', 'elementor-pro' ); + + add_submenu_page( + Settings::PAGE_ID, + $menu_title, + $menu_title, + self::CAPABILITY, + static::MENU_SLUG + ); + }, 50 ); + + add_action( 'admin_head', [ $this, 'clean_admin_listing_page' ] ); + } + + // TODO: Maybe just ignore all of those when the user can't use custom fonts? + add_filter( 'post_row_actions', [ $this, 'post_row_actions' ], 10, 2 ); + add_filter( 'manage_' . self::CPT . '_posts_columns', [ $this, 'manage_columns' ], 100 ); + add_action( 'save_post_' . self::CPT, [ $this, 'save_post_meta' ], 10, 3 ); + add_action( 'save_post_' . self::CPT, [ $this, 'clear_fonts_list' ], 100 ); + + add_filter( 'elementor/fonts/groups', [ $this, 'register_fonts_groups' ] ); + add_filter( 'elementor/fonts/additional_fonts', [ $this, 'register_fonts_in_control' ] ); + add_filter( 'elementor/finder/categories', [ $this, 'add_finder_item' ] ); + add_action( 'elementor/css-file/post/parse', [ $this, 'enqueue_fonts' ] ); + add_action( 'elementor/css-file/global/parse', [ $this, 'enqueue_fonts' ] ); + add_filter( 'post_updated_messages', [ $this, 'post_updated_messages' ] ); + add_filter( 'enter_title_here', [ $this, 'update_enter_title_here' ], 10, 2 ); + + // Ajax. + add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); + + /** + * Elementor fonts manager loaded. + * + * Fires after the fonts manager was fully loaded and instantiated. + * + * @since 2.0.0 + * + * @param Fonts_Manager $this An instance of fonts manager. + */ + do_action( 'elementor_pro/fonts_manager_loaded', $this ); + } + + /** + * Fonts_Manager constructor. + */ + public function __construct() { + $this->actions(); + $this->add_font_type( 'custom', new Fonts\Custom_Fonts() ); + $this->add_font_type( 'typekit', new Fonts\Typekit_Fonts() ); + } +} diff --git a/modules/assets-manager/asset-types/fonts/custom-fonts.php b/modules/assets-manager/asset-types/fonts/custom-fonts.php new file mode 100644 index 0000000..c1b055a --- /dev/null +++ b/modules/assets-manager/asset-types/fonts/custom-fonts.php @@ -0,0 +1,467 @@ + 'font/woff|application/font-woff|application/x-font-woff|application/octet-stream', + 'woff2' => 'font/woff2|application/octet-stream|font/x-woff2', + 'ttf' => 'application/x-font-ttf|application/octet-stream|font/ttf', + 'svg' => 'image/svg+xml|application/octet-stream|image/x-svg+xml', + 'eot' => 'application/vnd.ms-fontobject|application/octet-stream|application/x-vnd.ms-fontobject', + ]; + } + + public function add_meta_box() { + add_meta_box( + 'elementor-font-' . $this->get_type() . 'metabox', + __( 'Manage Your Font Files', 'elementor-pro' ), + [ $this, 'render_metabox' ], + Fonts_Manager::CPT, + 'normal', + 'default' + ); + } + + public function render_metabox( $post ) { + wp_enqueue_media(); + + $fields = [ + [ + 'id' => 'open_div', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'attributes' => [ + 'class' => 'repeater-content-top', + ], + ], + [ + 'id' => 'font_weight', + 'field_type' => 'select', + 'label' => esc_html__( 'Weight', 'elementor-pro' ) . ':', + 'extra_attributes' => [ + 'class' => 'font_weight', + ], + 'options' => $this->get_font_weight_options(), + ], + [ + 'id' => 'font_style', + 'field_type' => 'select', + 'label' => esc_html__( 'Style', 'elementor-pro' ) . ':', + 'extra_attributes' => [ + 'class' => 'font_style', + ], + 'options' => $this->get_font_style_options(), + ], + [ + 'id' => 'preview_label', + 'field_type' => 'html', + 'label' => false, + 'raw_html' => sprintf( '
%s
', esc_html__( 'Elementor Is Making the Web Beautiful!!!', 'elementor-pro' ) ), + ], + [ + 'id' => 'toolbar', + 'field_type' => 'toolbar', + 'label' => false, + ], + [ + 'id' => 'close_div', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'close' => true, + ], + [ + 'id' => 'open_div', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'attributes' => [ + 'class' => 'repeater-content-bottom', + ], + ], + ]; + + foreach ( $this->get_file_types() as $type => $mine ) { + $fields[] = [ + 'id' => $type, + 'field_type' => 'file', + 'mine' => str_replace( '|', ',', $mine ), + 'ext' => $type, + /* translators: %s: Font file format. */ + 'label' => sprintf( esc_html__( '%s File', 'elementor-pro' ), strtoupper( $type ) ), + /* translators: %s: Font file format. */ + 'box_title' => sprintf( esc_html__( 'Upload font .%s file', 'elementor-pro' ), $type ), + /* translators: %s: Font file format. */ + 'box_action' => sprintf( esc_html__( 'Select .%s file', 'elementor-pro' ), $type ), + 'preview_anchor' => 'none', + 'description' => $this->get_file_type_description( $type ), + ]; + } + + $fields[] = [ + 'id' => 'close_div', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'close' => true, + ]; + + $font_data = get_post_meta( $post->ID, self::FONT_META_KEY, true ); + + $repeater = [ + 'fields' => $fields, + 'id' => 'font_face', + 'label' => false, + 'add_label' => esc_html__( 'Add Font Variation', 'elementor-pro' ), + 'toggle_title' => esc_html__( 'Edit', 'elementor-pro' ), + 'remove_title' => esc_html__( 'Delete', 'elementor-pro' ), + 'field_type' => 'repeater', + 'row_label' => [ + 'default' => 'Settings', + 'selector' => '.font_weight', + ], + 'saved' => $font_data, + ]; + + $this->print_metabox( [ $repeater ] ); + + // PHPCS - Dedicated for CSS. + printf( '', get_post_meta( $post->ID, self::FONT_FACE_META_KEY, true ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + public function save_meta( $post_id, $data ) { + if ( ! isset( $data['font_face'] ) || ! is_array( $data['font_face'] ) ) { + return; + } + + // Sanitize a little + $font_face = []; + foreach ( $data['font_face'] as $font_data ) { + $font_face[] = $this->sanitize_text_field_recursive( $font_data ); + } + + // All good save the files array + update_post_meta( $post_id, self::FONT_META_KEY, $font_face ); + + // Save font face + update_post_meta( $post_id, self::FONT_FACE_META_KEY, $this->generate_font_face( $post_id ) ); + } + + public function upload_mimes( $mine_types ) { + if ( current_user_can( Fonts_Manager::CAPABILITY ) && $this->is_elementor_font_upload() ) { + foreach ( $this->get_file_types() as $type => $mine ) { + if ( ! isset( $mine_types[ $type ] ) ) { + $mine_types[ $type ] = $mine; + } + } + } + + return $mine_types; + } + + public function wp_handle_upload_prefilter( $file ) { + if ( ! $this->is_elementor_font_upload() ) { + return $file; + } + + $ext = pathinfo( $file['name'], PATHINFO_EXTENSION ); + + if ( 'svg' !== $ext ) { + return $file; + } + + /** + * @var \Elementor\Core\Files\Assets\Svg\Svg_Handler $svg_handler; + */ + $svg_handler = Plugin::elementor()->assets_manager->get_asset( 'svg-handler' ); + + if ( Files_Upload_Handler::file_sanitizer_can_run() && ! $svg_handler->sanitize_svg( $file['tmp_name'] ) ) { + $file['error'] = esc_html__( 'Invalid SVG Format, file not uploaded for security reasons', 'elementor-pro' ); + } + + return $file; + } + + private function is_elementor_font_upload() { + return isset( $_POST['uploadTypeCaller'] ) && 'elementor-admin-font-upload' === $_POST['uploadTypeCaller']; // phpcs:ignore + } + + /** + * A workaround for upload validation which relies on a PHP extension (fileinfo) with inconsistent reporting behaviour. + * ref: https://core.trac.wordpress.org/ticket/39550 + * ref: https://core.trac.wordpress.org/ticket/40175 + */ + public function filter_fix_wp_check_filetype_and_ext( $data, $file, $filename, $mimes ) { + if ( ! empty( $data['ext'] ) && ! empty( $data['type'] ) ) { + return $data; + } + + $registered_file_types = $this->get_file_types(); + $filetype = wp_check_filetype( $filename, $mimes ); + + if ( ! isset( $registered_file_types[ $filetype['ext'] ] ) ) { + return $data; + } + // Fix incorrect file mime type + $filetype['type'] = explode( '|', $filetype['type'] )[0]; + + return [ + 'ext' => $filetype['ext'], + 'type' => $filetype['type'], + 'proper_filename' => $data['proper_filename'], + ]; + } + + public function generate_font_face( $post_id ) { + $saved = get_post_meta( $post_id, self::FONT_META_KEY, true ); + if ( ! is_array( $saved ) ) { + return false; + } + + $font_family = get_the_title( $post_id ); + $font_face = ''; + + foreach ( $saved as $font_data ) { + $font_face .= $this->get_font_face_from_data( $font_family, $font_data ) . PHP_EOL; + } + + return $font_face; + } + + public function get_font_face_from_data( $font_family, $data ) { + $src = []; + foreach ( [ 'eot', 'woff2', 'woff', 'ttf', 'svg' ] as $type ) { + if ( ! isset( $data[ $type ] ) || ! isset( $data[ $type ]['url'] ) || empty( $data[ $type ]['url'] ) ) { + continue; + } + + if ( 'svg' === $type ) { + $data[ $type ]['url'] .= '#' . str_replace( ' ', '', $font_family ); + } + + $src[] = $this->get_font_src_per_type( $type, $data[ $type ]['url'] ); + } + + $font_face = '@font-face {' . PHP_EOL; + $font_face .= "\tfont-family: '" . $font_family . "';" . PHP_EOL; + $font_face .= "\tfont-style: " . $data['font_style'] . ';' . PHP_EOL; + $font_face .= "\tfont-weight: " . $data['font_weight'] . ';' . PHP_EOL; + $font_face .= "\tfont-display: " . apply_filters( 'elementor_pro/custom_fonts/font_display', 'auto', $font_family, $data ) . ';' . PHP_EOL; + + if ( isset( $data['eot'] ) && isset( $data['eot']['url'] ) && ! empty( $data['eot']['url'] ) ) { + $font_face .= "\tsrc: url('" . esc_attr( $data['eot']['url'] ) . "');" . PHP_EOL; + } + + $font_face .= "\tsrc: " . implode( ',' . PHP_EOL . "\t\t", $src ) . ';' . PHP_EOL . '}'; + + return $font_face; + } + + private function get_font_src_per_type( $type, $url ) { + $src = 'url(\'' . esc_attr( $url ) . '\') '; + switch ( $type ) { + case 'woff': + case 'woff2': + case 'svg': + $src .= 'format(\'' . $type . '\')'; + break; + + case 'ttf': + $src .= 'format(\'truetype\')'; + break; + + case 'eot': + $src = 'url(\'' . esc_attr( $url ) . '?#iefix\') format(\'embedded-opentype\')'; + break; + } + + return $src; + } + + public function get_fonts( $force = false ) { + $fonts = get_option( self::FONTS_OPTION_NAME, false ); + if ( $fonts && ! $force ) { + return $fonts; + } + + add_filter( 'posts_fields', [ $this, 'posts_fields' ] ); + $fonts = new \WP_Query( [ + 'post_type' => Fonts_Manager::CPT, + 'posts_per_page' => -1, + ] ); + remove_filter( 'posts_fields', [ $this, 'posts_fields' ] ); + + $new_fonts = []; + foreach ( $fonts->posts as $font ) { + $new_fonts[ $font->post_title ] = 'custom'; + } + + update_option( self::FONTS_OPTION_NAME, $new_fonts ); + + return $new_fonts; + } + + private function get_font_face_by_font_family( $font_family ) { + global $wpdb; + + $id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type = %s LIMIT 1", $font_family, Fonts_Manager::CPT ) ); + + if ( $id ) { + return get_post_meta( $id, self::FONT_FACE_META_KEY, true ); + } + + return ''; + } + + public function render_preview_column( $post_id ) { + $font_face = get_post_meta( $post_id, self::FONT_FACE_META_KEY, true ); + + if ( ! $font_face ) { + return; + } + + // PHPCS - the variable $font_face is CSS. the property $this->font_preview_phrase is safe. + printf( '%s', $font_face, esc_html( get_the_title( $post_id ) ), $this->font_preview_phrase ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + public function get_font_family_type( $post_id, $post_title ) { + return [ + $post_title => $this->get_type(), + ]; + } + + public function get_font_data( $post_id, $post_title ) { + return [ + $post_title => [ + 'font_face' => $this->generate_font_face( $post_id ), + 'post_id' => $post_id, + ], + ]; + } + + public function get_font_variations_count( $post_id ) { + $data = get_post_meta( $post_id, self::FONT_META_KEY, true ); + if ( ! empty( $data ) && count( $data ) > 0 ) { + echo sprintf( '%d', count( $data ) ); + } + } + + /** + * @param string $font_family + * @param array $font_data + * @param Base $post_css + */ + public function enqueue_font( $font_family, $font_data, $post_css ) { + $font_faces = isset( $font_data['font_face'] ) ? $font_data['font_face'] : $this->get_font_face_by_font_family( $font_family ); + // Add a css comment + $custom_css = '/* Start Custom Fonts CSS */' . $font_faces . '/* End Custom Fonts CSS */'; + $post_css->get_stylesheet()->add_raw_css( $custom_css ); + } + + /** + * @param array $data + * + * @return array + * @throws \Exception + */ + public function handle_panel_request( array $data ) { + $font_family = sanitize_text_field( $data['font'] ); + + $font_face = $this->get_font_face_by_font_family( $font_family ); + + if ( empty( $font_face ) ) { + /* translators: %s: Font family. */ + $error_message = sprintf( esc_html__( 'Font %s was not found.', 'elementor-pro' ), $font_family ); + + throw new \Exception( $error_message ); + } + + return [ + 'font_face' => $font_face, + ]; + } + + private function get_font_style_options() { + return [ + 'normal' => esc_html__( 'Normal', 'elementor-pro' ), + 'italic' => esc_html__( 'Italic', 'elementor-pro' ), + 'oblique' => esc_html__( 'Oblique', 'elementor-pro' ), + ]; + } + + private function get_font_weight_options() { + return [ + 'normal' => esc_html__( 'Normal', 'elementor-pro' ), + 'bold' => esc_html__( 'Bold', 'elementor-pro' ), + '100' => '100', + '200' => '200', + '300' => '300', + '400' => '400', + '500' => '500', + '600' => '600', + '700' => '700', + '800' => '800', + '900' => '900', + ]; + } + + private function get_file_type_description( $file_type ) { + $descriptions = [ + 'eot' => esc_html__( 'Embedded OpenType, Used by IE6-IE9 Browsers', 'elementor-pro' ), + 'woff2' => esc_html__( 'The Web Open Font Format 2, Used by Super Modern Browsers', 'elementor-pro' ), + 'woff' => esc_html__( 'The Web Open Font Format, Used by Modern Browsers', 'elementor-pro' ), + 'ttf' => esc_html__( 'TrueType Fonts, Used for better supporting Safari, Android, iOS', 'elementor-pro' ), + 'svg' => esc_html__( 'SVG fonts allow SVG to be used as glyphs when displaying text, Used by Legacy iOS', 'elementor-pro' ), + ]; + + return isset( $descriptions[ $file_type ] ) ? $descriptions[ $file_type ] : ''; + } + + private function replace_urls( $rows_affected, $from, $to ) { + global $wpdb; + + $rows_affected = $wpdb->query( + "UPDATE {$wpdb->postmeta} " . + $wpdb->prepare( 'SET `meta_value` = REPLACE(`meta_value`, %s, %s) ', $from, $to ) . + 'WHERE `meta_key` = \'' . self::FONT_FACE_META_KEY . '\'' + ); + + return $rows_affected; + } + + protected function actions() { + parent::actions(); + + add_filter( 'elementor/tools/replace-urls', function( $rows_affected, $from, $to ) { + return $this->replace_urls( $rows_affected, $from, $to ); + }, 10, 3 ); + add_filter( 'wp_check_filetype_and_ext', [ $this, 'filter_fix_wp_check_filetype_and_ext' ], 10, 4 ); + add_filter( 'wp_handle_upload_prefilter', [ $this, 'wp_handle_upload_prefilter' ] ); + add_filter( 'upload_mimes', [ $this, 'upload_mimes' ] ); + add_action( 'add_meta_boxes_' . Fonts_Manager::CPT, [ $this, 'add_meta_box' ] ); + } +} diff --git a/modules/assets-manager/asset-types/fonts/typekit-fonts.php b/modules/assets-manager/asset-types/fonts/typekit-fonts.php new file mode 100644 index 0000000..ada0006 --- /dev/null +++ b/modules/assets-manager/asset-types/fonts/typekit-fonts.php @@ -0,0 +1,262 @@ +get_typekit_kit_id(); + if ( ! $kit_id ) { + return false; + } + + $response = wp_remote_get( $this->api_base . '/' . $kit_id . '/published' ); + + // Response is a WP_Error object + if ( is_wp_error( $response ) ) { + return false; + } + + // Response code is not success + $response_code = (int) wp_remote_retrieve_response_code( $response ); + $response_body = json_decode( wp_remote_retrieve_body( $response ) ); + if ( 200 !== $response_code ) { + switch ( $response_code ) { + case 404: + $this->error = esc_html__( 'Project not found.', 'elementor-pro' ); + break; + default: + $this->error = $response_code; + if ( isset( $response_body->errors ) ) { + $this->error .= ': ' . implode( ', ', $response_body->errors ); + } + break; + } + + return false; + } + + if ( ! $response_body ) { + $this->error = esc_html__( 'No project data was returned.', 'elementor-pro' ); + + return false; + } + + /* + * Expected Json response example + * { + * "kit": { + * "id": "nmm7qvq", + * "families": [ + * { + * "id": "hmqz", + * "name": "Adobe Caslon Pro", + * "slug": "adobe-caslon-pro", + * "css_names": [ + * "adobe-caslon-pro" + * ], + * "css_stack": "\"adobe-caslon-pro\",serif", + * "variations": [ "n6","i6","i7" ] + * } + * ] + * } + * } + */ + if ( ! is_object( $response_body ) || ! isset( $response_body->kit ) || ! isset( $response_body->kit->families ) || ! is_array( $response_body->kit->families ) ) { + return false; + } + + $families = []; + foreach ( $response_body->kit->families as $font_family ) { + $font_css = isset( $font_family->css_names[0] ) ? $font_family->css_names[0] : $font_family->slug; + $families[ $font_css ] = $this->get_type(); + } + update_option( self::TYPEKIT_FONTS_OPTION_NAME, $families ); + + return $families; + } + + private function get_kit_fonts() { + $typekit_fonts = $this->get_typekit_fonts(); + if ( ! $typekit_fonts ) { + $typekit_fonts = $this->fetch_typekit_data(); + } + + return $typekit_fonts; + } + + /** + * @param array $data + * + * @return array + * @throws \Exception + */ + public function handle_panel_request( array $data ) { + $font_family = sanitize_text_field( $data['font'] ); + + $typekit_fonts = $this->get_kit_fonts(); + + if ( ! $typekit_fonts || ! is_array( $typekit_fonts ) ) { + throw new \Exception( 'Error with TypeKit fonts.' ); + } + + if ( ! in_array( $font_family, array_keys( $typekit_fonts ) ) ) { + throw new \Exception( 'Font missing in Project.' ); + } + + $kit_id = $this->get_typekit_kit_id(); + + return [ 'font_url' => sprintf( self::TYPEKIT_FONTS_LINK, $kit_id ) ]; + } + + public function sanitize_kit_id_settings( $input ) { + if ( empty( $input ) ) { + delete_option( self::TYPEKIT_FONTS_OPTION_NAME ); + } + + return $input; + } + + public function register_admin_fields( Settings $settings ) { + $fonts = $this->get_typekit_fonts(); + $button_label = esc_html__( 'Get Project ID', 'elementor-pro' ); + $found_label = '{{count}} ' . esc_html__( 'Fonts Families Found in project. Please note that typekit takes a few minutes to sync once you publish or update a project.', 'elementor-pro' ); + if ( $fonts && is_array( $fonts ) ) { + $button_label = esc_html__( 'Sync Project', 'elementor-pro' ); + } + $settings->add_section( Settings::TAB_INTEGRATIONS, 'typekit', [ + 'callback' => function() { + echo '

' . esc_html__( 'Adobe Fonts (TypeKit)', 'elementor-pro' ) . '

'; + esc_html_e( 'TypeKit partners with the world’s leading type foundries to bring thousands of beautiful fonts to designers every day.', 'elementor-pro' ); + }, + 'fields' => [ + self::TYPEKIT_KIT_ID_OPTION_NAME => [ + 'label' => esc_html__( 'Project ID', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + 'desc' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'Enter Your %1$sTypeKit Project ID%2$s.', 'elementor-pro' ), + '', + '' + ), + ], + 'setting_args' => [ + 'sanitize_callback' => [ $this, 'sanitize_kit_id_settings' ], + ], + ], + 'validate_api_data' => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => sprintf( '

', + esc_html( $found_label ), + self::TYPEKIT_KIT_ID_OPTION_NAME . '_fetch', + wp_create_nonce( self::TYPEKIT_KIT_ID_OPTION_NAME ), + $button_label + ), + ], + ], + ], + ] ); + } + + public function register_fonts_in_control( $fonts ) { + $typekit_fonts = $this->get_kit_fonts(); + if ( $typekit_fonts ) { + return array_merge( $typekit_fonts, $fonts ); + } + + return $fonts; + } + + public function print_font_link( $font ) { + if ( $this->kit_enqueued ) { + return; + } + if ( $this->is_font_in_kit( $font ) ) { + $kit_url = sprintf( self::TYPEKIT_FONTS_LINK, $this->get_typekit_kit_id() ); + echo ''; + $this->kit_enqueued = true; + } + } + + private function is_font_in_kit( $font ) { + $kit_fonts = $this->get_kit_fonts(); + if ( ! $kit_fonts || ! is_array( $kit_fonts ) ) { + return false; + } + + return in_array( $font, array_keys( $kit_fonts ) ); + } + + public function integrations_admin_ajax_handler() { + check_ajax_referer( self::TYPEKIT_KIT_ID_OPTION_NAME, '_nonce' ); + + if ( ! current_user_can( Fonts_Manager::CAPABILITY ) ) { + wp_send_json_error( 'Permission denied' ); + } + + $kit_id = Utils::_unstable_get_super_global_value( $_POST, 'kit_id' ); + + if ( ! $kit_id ) { + wp_send_json_error(); + } + $fonts = []; + try { + update_option( 'elementor_' . self::TYPEKIT_KIT_ID_OPTION_NAME, sanitize_text_field( $kit_id ) ); + $fonts = $this->fetch_typekit_data(); + } catch ( \Exception $exception ) { + wp_send_json_error(); + } + wp_send_json_success( [ + 'fonts' => $fonts, + 'count' => count( $fonts ), + ] ); + } + + protected function actions() { + parent::actions(); + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 100 ); + } + add_filter( 'elementor/fonts/additional_fonts', [ $this, 'register_fonts_in_control' ] ); + add_action( 'elementor/fonts/print_font_links/' . $this->get_type(), [ $this, 'print_font_link' ] ); + add_action( 'wp_ajax_elementor_pro_admin_fetch_fonts', [ $this, 'integrations_admin_ajax_handler' ] ); + } +} diff --git a/modules/assets-manager/asset-types/icons-manager.php b/modules/assets-manager/asset-types/icons-manager.php new file mode 100644 index 0000000..3fb37e5 --- /dev/null +++ b/modules/assets-manager/asset-types/icons-manager.php @@ -0,0 +1,259 @@ +icon_types; + } + + if ( isset( $this->icon_types[ $type ] ) ) { + return $this->icon_types[ $type ]; + } + + return false; + } + + /** + * Add a font type to the font manager + * + * @param string $icon_type + * @param Classes\Assets_Base $instance + */ + public function add_icon_type( $icon_type, $instance ) { + $this->icon_types[ $icon_type ] = $instance; + } + + /** + * Register elementor icon set custom post type + */ + public function register_post_type() { + $labels = [ + 'name' => _x( 'Custom Icons', 'CPT Name', 'elementor-pro' ), + 'singular_name' => _x( 'Icon Set', 'CPT Singular Name', 'elementor-pro' ), + 'add_new' => esc_html__( 'Add New', 'elementor-pro' ), + 'add_new_item' => esc_html__( 'Add New Icon Set', 'elementor-pro' ), + 'edit_item' => esc_html__( 'Edit Icon Set', 'elementor-pro' ), + 'new_item' => esc_html__( 'New Icon Set', 'elementor-pro' ), + 'all_items' => esc_html__( 'All Icons', 'elementor-pro' ), + 'view_item' => esc_html__( 'View Icon', 'elementor-pro' ), + 'search_items' => esc_html__( 'Search Icon Set', 'elementor-pro' ), + 'not_found' => esc_html__( 'No icons found', 'elementor-pro' ), + 'not_found_in_trash' => esc_html__( 'No icons found in trash', 'elementor-pro' ), + 'parent_item_colon' => '', + 'menu_name' => _x( 'Custom Icons', 'CPT Menu Name', 'elementor-pro' ), + ]; + + $args = [ + 'labels' => $labels, + 'public' => false, + 'rewrite' => false, + 'show_ui' => true, + 'show_in_menu' => false, + 'show_in_nav_menus' => false, + 'exclude_from_search' => true, + 'capability_type' => 'post', + 'hierarchical' => false, + 'supports' => [ 'title' ], + ]; + + $this->post_type_object = register_post_type( self::CPT, $args ); + } + + public function post_updated_messages( $messages ) { + $messages[ self::CPT ] = [ + 0 => '', // Unused. Messages start at index 1. + 1 => esc_html__( 'Icon Set updated.', 'elementor-pro' ), + 2 => esc_html__( 'Custom field updated.', 'elementor-pro' ), + 3 => esc_html__( 'Custom field deleted.', 'elementor-pro' ), + 4 => esc_html__( 'Icon Set updated.', 'elementor-pro' ), + /* translators: %s: Date and time of the revision. */ + 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Icon Set restored to revision from %s', 'elementor-pro' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false, + 6 => esc_html__( 'Icon Set saved.', 'elementor-pro' ), + 7 => esc_html__( 'Icon Set saved.', 'elementor-pro' ), + 8 => esc_html__( 'Icon Set submitted.', 'elementor-pro' ), + 9 => esc_html__( 'Icon Set updated.', 'elementor-pro' ), + 10 => esc_html__( 'Icon Set draft updated.', 'elementor-pro' ), + ]; + + return $messages; + } + + /** + * Add Font manager link to admin menu + */ + private function register_admin_menu( Admin_Menu_Manager $admin_menu_manager ) { + if ( $this->can_use_custom_icons() ) { + $admin_menu_manager->register( static::MENU_SLUG, new Custom_Icons_Menu_Item() ); + } else { + $admin_menu_manager->register( static::PROMOTION_MENU_SLUG, new Custom_Icons_Promotion_Menu_Item() ); + } + } + + private function can_use_custom_icons() { + return ( API::is_license_active() || $this->has_icons() ); + } + + private function has_icons() { + if ( null !== $this->has_icons ) { + return $this->has_icons; + } + + $existing_icons = new \WP_Query( [ + 'post_type' => static::CPT, + 'posts_per_page' => 1, + ] ); + + $this->has_icons = $existing_icons->post_count > 0; + + return $this->has_icons; + } + + public function redirect_admin_old_page_to_new() { + if ( ! empty( $_GET['page'] ) && 'elementor_custom_icons' === $_GET['page'] ) { + wp_safe_redirect( admin_url( static::MENU_SLUG ) ); + die; + } + } + + /** + * Clean up admin Font manager admin listing + */ + public function clean_admin_listing_page() { + global $typenow; + + if ( self::CPT !== $typenow ) { + return; + } + + add_filter( 'months_dropdown_results', '__return_empty_array' ); + add_filter( 'screen_options_show_screen', '__return_false' ); + } + + public function post_row_actions( $actions, $post ) { + if ( self::CPT !== $post->post_type ) { + return $actions; + } + + unset( $actions['inline hide-if-no-js'] ); + + return $actions; + } + + public function add_finder_item( array $categories ) { + $categories['settings']['items']['custom-icons'] = [ + 'title' => esc_html__( 'Custom Icons', 'elementor-pro' ), + 'icon' => 'favorite', + 'url' => admin_url( static::MENU_SLUG ), + 'keywords' => [ 'custom', 'icons', 'elementor' ], + ]; + + if ( ! $this->can_use_custom_icons() ) { + $lock = new Feature_Lock( [ 'type' => 'custom-icon' ] ); + + $categories['settings']['items']['custom-icons']['lock'] = $lock->get_config(); + } + + return $categories; + } + + /** + * Register Font Manager action and filter hooks + */ + protected function actions() { + add_action( 'init', [ $this, 'register_post_type' ] ); + + if ( is_admin() ) { + add_action( 'init', [ $this, 'redirect_admin_old_page_to_new' ] ); + + add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu_manager ) { + $this->register_admin_menu( $admin_menu_manager ); + } ); + + // TODO: BC - Remove after `Admin_Menu_Manager` will be the standard. + add_action( 'admin_menu', function () { + if ( did_action( 'elementor/admin/menu/register' ) ) { + return; + } + + $menu_title = _x( 'Custom Icons', 'Elementor Font', 'elementor-pro' ); + + add_submenu_page( + Settings::PAGE_ID, + $menu_title, + $menu_title, + self::CAPABILITY, + static::MENU_SLUG + ); + }, 50 ); + + add_action( 'admin_head', [ $this, 'clean_admin_listing_page' ] ); + } + + // TODO: Maybe just ignore all of those when the user can't use custom icons? + add_filter( 'post_updated_messages', [ $this, 'post_updated_messages' ] ); + add_filter( 'post_row_actions', [ $this, 'post_row_actions' ], 10, 2 ); + + add_filter( 'elementor/finder/categories', [ $this, 'add_finder_item' ] ); + + /** + * Elementor icons manager loaded. + * + * Fires after the icons manager was fully loaded and instantiated. + * + * @since 2.0.0 + * + * @param Fonts_Manager $this An instance of icons manager. + */ + do_action( 'elementor_pro/icons_manager_loaded', $this ); + } + + /** + * Fonts_Manager constructor. + */ + public function __construct() { + $this->actions(); + $this->add_icon_type( 'custom', new Icons\Custom_Icons() ); + $this->add_icon_type( 'font-awesome-pro', new Icons\Font_Awesome_Pro() ); + } +} diff --git a/modules/assets-manager/asset-types/icons/custom-icons.php b/modules/assets-manager/asset-types/icons/custom-icons.php new file mode 100644 index 0000000..bb4977b --- /dev/null +++ b/modules/assets-manager/asset-types/icons/custom-icons.php @@ -0,0 +1,503 @@ +ID ); + + $fields = [ + [ + 'id' => 'open_div', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'attributes' => [ + 'class' => 'elementor-custom-icons-metabox', + ], + ], + [ + 'id' => 'zip_upload', + 'field_type' => 'dropzone', + 'accept' => 'zip,application/octet-stream,application/zip,application/x-zip,application/x-zip-compressed', + 'label' => false, + 'sub-label' => esc_html__( 'Your Fontello, IcoMoon or Fontastic .zip file', 'elementor-pro' ), + ], + [ + 'id' => 'close_div', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'close' => true, + ], + [ + 'id' => self::META_KEY, + 'name' => self::META_KEY, + 'field_type' => 'input', + 'input_type' => 'hidden', + 'label' => false, + 'value' => $save_data, + 'saved' => $save_data, + ], + [ + 'id' => Icons_Manager::CPT . '_nonce', + 'name' => Icons_Manager::CPT . '_nonce', + 'field_type' => 'input', + 'input_type' => 'hidden', + 'label' => false, + 'value' => wp_create_nonce( Icons_Manager::CPT ), + ], + ]; + + foreach ( $fields as $field ) { + $field['saved'] = isset( $field['saved'] ) ? $field['saved'] : ''; + } + + $this->print_metabox( $fields ); + } + + public function save_post_meta( $post_id, $post, $update ) { + // If this is an autosave, our form has not been submitted, + // so we don't want to do anything. + if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { + return $post_id; + } + + // Check the user's permissions. + if ( ! current_user_can( 'edit_post', $post_id ) ) { + return $post_id; + } + + // Check if our nonce is set. + if ( ! isset( $_POST[ Icons_Manager::CPT . '_nonce' ] ) ) { + return $post_id; + } + + // Verify that the nonce is valid. + if ( ! wp_verify_nonce( + Utils::_unstable_get_super_global_value( $_POST, Icons_Manager::CPT . '_nonce' ), + Icons_Manager::CPT + ) ) { + return $post_id; + } + + if ( ! isset( $_POST[ self::META_KEY ] ) ) { + return delete_post_meta( $post_id, self::META_KEY ); + } + + // PHPCS - It will be sanitized in the next line. + $json = json_decode( stripslashes_deep( $_POST[ self::META_KEY ] ), true ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + foreach ( $json as $property => $value ) { + $json[ $property ] = $this->sanitize_text_field_recursive( $value ); + } + + // All good save the files array + update_post_meta( $post_id, self::META_KEY, json_encode( $json ) ); + + // Force refresh of list in Options Table + self::clear_icon_list_option(); + } + + public static function get_supported_icon_sets() { + $icon_sets = [ + 'fontastic' => __NAMESPACE__ . '\IconSets\Fontastic', + 'fontello' => __NAMESPACE__ . '\IconSets\Fontello', + 'icomoon' => __NAMESPACE__ . '\IconSets\Icomoon', + ]; + + $additional_icon_sets = []; + + /** + * Additional icon sets. + * + * Filters the icon types supported by Elementor Pro. + * + * By default Elementor Pro supports 'fontastic', 'fontello' and 'icomoon'. + * This hook allows developers to add additional icon sets. + * + * @param array $additional_icon_sets Additional icon sets. + */ + $additional_icon_sets = apply_filters( 'elementor_pro/icons_manager/custom_icons/additional_supported_types', $additional_icon_sets ); + + return array_merge( $additional_icon_sets, $icon_sets ); + } + + private function get_active_icon_sets() { + $icons = new \WP_Query( [ + 'post_type' => Icons_Manager::CPT, + 'posts_per_page' => -1, + ] ); + $custom_icon_sets = []; + foreach ( $icons->posts as $icon_set ) { + $set_config = json_decode( self::get_icon_set_config( $icon_set->ID ), true ); + $set_config['custom_icon_post_id'] = $icon_set->ID; + $set_config['label'] = $icon_set->post_title; + $custom_icon_sets[ $set_config['name'] ] = $set_config; + } + return $custom_icon_sets; + } + + /** + * get_wp_filesystem + * @return \WP_Filesystem_Base + */ + public static function get_wp_filesystem() { + global $wp_filesystem; + if ( empty( $wp_filesystem ) ) { + require_once ABSPATH . '/wp-admin/includes/file.php'; + WP_Filesystem(); + } + return $wp_filesystem; + } + + private function upload() { + $file = Utils::_unstable_get_super_global_value( $_FILES, 'zip_upload' ); + $filename = $file['name']; + $ext = pathinfo( $filename, PATHINFO_EXTENSION ); + if ( 'zip' !== $ext ) { + unlink( $filename ); + return new \WP_Error( 'unsupported_file', esc_html__( 'Only zip files are allowed', 'elementor-pro' ) ); + } + if ( ! function_exists( 'wp_handle_upload' ) ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } + // Handler upload archive file. + $upload_result = wp_handle_upload( $file, [ 'test_form' => false ] ); + if ( isset( $upload_result['error'] ) ) { + unlink( $filename ); + return new \WP_Error( 'upload_error', $upload_result['error'] ); + } + return $upload_result['file']; + } + + private function extract_zip( $file, $to ) { + // TODO: Move to core as a util. + $valid_field_types = [ + 'css', + 'eot', + 'html', + 'json', + 'otf', + 'svg', + 'ttf', + 'txt', + 'woff', + 'woff2', + ]; + + $zip = new \ZipArchive(); + + $zip->open( $file ); + + $valid_entries = []; + + // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase + for ( $i = 0; $i < $zip->numFiles; $i++ ) { + $zipped_file_name = $zip->getNameIndex( $i ); + $dirname = pathinfo( $zipped_file_name, PATHINFO_DIRNAME ); + + // Skip the OS X-created __MACOSX directory. + if ( '__MACOSX/' === substr( $dirname, 0, 9 ) ) { + continue; + } + + $zipped_extension = pathinfo( $zipped_file_name, PATHINFO_EXTENSION ); + // Skip files with transversal paths. + if ( strpos( $zipped_file_name, '..' ) !== false ) { + continue; + } + + if ( in_array( $zipped_extension, $valid_field_types, true ) ) { + $valid_entries[] = $zipped_file_name; + } + } + + $unzip_result = false; + + if ( ! empty( $valid_entries ) ) { + $unzip_result = $zip->extractTo( $to, $valid_entries ); + } + + if ( ! $unzip_result ) { + $unzip_result = new \WP_Error( 'error', esc_html__( 'Could not unzip or empty archive.', 'elementor-pro' ) ); + } + + @unlink( $file ); + + return $unzip_result; // TRUE | WP_Error instance. + } + + private function upload_and_extract_zip() { + $zip_file = $this->upload(); + if ( is_wp_error( $zip_file ) ) { + return $zip_file; + } + $filesystem = self::get_wp_filesystem(); + $extract_to = trailingslashit( get_temp_dir() . pathinfo( $zip_file, PATHINFO_FILENAME ) ); + + $unzipped = $this->extract_zip( $zip_file, $extract_to ); + if ( is_wp_error( $unzipped ) ) { + return $unzipped; + } + + // Find the right folder. + $source_files = array_keys( $filesystem->dirlist( $extract_to ) ); + if ( count( $source_files ) === 0 ) { + return new \WP_Error( 'incompatible_archive', esc_html__( 'Incompatible archive', 'elementor-pro' ) ); + } + + if ( 1 === count( $source_files ) && $filesystem->is_dir( $extract_to . $source_files[0] ) ) { + $directory = $extract_to . trailingslashit( $source_files[0] ); + } else { + $directory = $extract_to; + } + return [ + 'directory' => $directory, + 'extracted_to' => $extract_to, + ]; + } + + public function custom_icons_upload_handler( $data ) { + if ( ! current_user_can( Icons_Manager::CAPABILITY ) ) { + return new \WP_Error( Exceptions::FORBIDDEN, 'Access denied.' ); + } + + $this->current_post_id = $data['post_id']; + $results = $this->upload_and_extract_zip(); + if ( is_wp_error( $results ) ) { + return $results; + } + $supported_icon_sets = self::get_supported_icon_sets(); + foreach ( $supported_icon_sets as $key => $handler ) { + /** + * @var IconSets\Icon_Set_Base $icon_set_handler + */ + $icon_set_handler = new $handler( $results['directory'] ); + + if ( ! $icon_set_handler ) { + continue; + } + if ( ! $icon_set_handler->is_valid() ) { + continue; + } + $icon_set_handler->handle_new_icon_set(); + $icon_set_handler->move_files( $this->current_post_id ); + $config = $icon_set_handler->build_config(); + + // Notify about duplicate prefix + if ( self::icon_set_prefix_exists( $config['prefix'] ) ) { + $config['duplicate_prefix'] = true; + } + return [ + 'config' => $config, + ]; + } + return new \WP_Error( 'unsupported_zip_format', esc_html__( 'The zip file provided is not supported!', 'elementor-pro' ) ); + } + + public function handle_delete_icon_set( $post_id ) { + if ( Icons_Manager::CPT !== get_post_type( $post_id ) ) { + return; + } + + // remove all assets related to this icon set + $attachments = get_attached_media( '', $post_id ); + + foreach ( $attachments as $attachment ) { + wp_delete_attachment( $attachment->ID, 'true' ); + } + + // remove icon set assets directory + $icon_set_dir = get_post_meta( $post_id, '_elementor_icon_set_path', true ); + if ( ! empty( $icon_set_dir ) && is_dir( $icon_set_dir ) ) { + $this::get_wp_filesystem()->rmdir( $icon_set_dir, true ); + } + + // Force refresh of list in Options Table + self::clear_icon_list_option(); + } + + public static function clear_icon_list_option() { + delete_option( self::OPTION_NAME ); + } + + public function display_post_states( $post_states, $post ) { + if ( 'publish' !== $post->post_status || Icons_Manager::CPT !== $post->post_type ) { + return $post_states; + } + + $data = json_decode( self::get_icon_set_config( $post->ID ) ); + if ( ! empty( $data->count ) ) { + echo sprintf( '%d', esc_html( $data->count ) ); + } + + return $post_states; + } + + /** + * Render preview column in font manager admin listing + * + * @param $column + * @param $post_id + */ + public function render_columns( $column, $post_id ) { + if ( 'icons_prefix' === $column ) { + $data = json_decode( self::get_icon_set_config( $post_id ) ); + if ( ! empty( $data->prefix ) ) { + echo '
' . esc_html( '.' . $data->prefix ) . '
'; + } + } + } + + /** + * Define which columns to display in font manager admin listing + * + * @param $columns + * + * @return array + */ + public function manage_columns( $columns ) { + return [ + 'cb' => '', + 'title' => esc_html__( 'Icon Set', 'elementor-pro' ), + 'icons_prefix' => esc_html__( 'CSS Prefix', 'elementor-pro' ), + ]; + } + + public function update_enter_title_here( $title, $post ) { + if ( isset( $post->post_type ) && Icons_Manager::CPT === $post->post_type ) { + return esc_html__( 'Enter Icon Set Name', 'elementor-pro' ); + } + + return $title; + } + + public function register_ajax_actions( Ajax $ajax ) { + $ajax->register_ajax_action( 'pro_assets_manager_custom_icon_upload', [ $this, 'custom_icons_upload_handler' ] ); + } + + public function register_icon_libraries_control( $additional_sets ) { + return array_replace( $additional_sets, self::get_custom_icons_config() ); + } + + public function add_custom_icon_templates( $current_screen ) { + if ( 'elementor_icons' !== $current_screen->id || 'post' !== $current_screen->base ) { + return; + } + Plugin::elementor()->common->add_template( __DIR__ . '/templates.php' ); + } + + public function add_custom_icons_url( $config ) { + $config['customIconsURL'] = admin_url( 'edit.php?post_type=' . Icons_Manager::CPT ); + return $config; + } + + public static function get_custom_icons_config() { + $config = get_option( self::OPTION_NAME, false ); + if ( false === $config ) { + $icons = new \WP_Query( [ + 'post_type' => Icons_Manager::CPT, + 'posts_per_page' => -1, + 'post_status' => 'publish', + ] ); + $config = []; + foreach ( $icons->posts as $icon_set ) { + $set_config = json_decode( self::get_icon_set_config( $icon_set->ID ), true ); + $set_config['custom_icon_post_id'] = $icon_set->ID; + $set_config['label'] = $icon_set->post_title; + if ( isset( $set_config['fetchJson'] ) ) { + unset( $set_config['icons'] ); + } + $config[ $set_config['name'] ] = $set_config; + } + update_option( self::OPTION_NAME, $config ); + } + return $config; + } + + public static function icon_set_prefix_exists( $prefix ) { + $config = self::get_custom_icons_config(); + if ( empty( $config ) ) { + return false; + } + foreach ( $config as $icon_set_name => $icon_config ) { + if ( $prefix === $icon_config['prefix'] ) { + return true; + } + } + return false; + } + + public function transition_post_status( $new_status, $old_status, $post ) { + if ( Icons_Manager::CPT !== $post->post_type ) { + return; + } + + if ( 'publish' === $old_status && 'publish' !== $new_status ) { + $this->clear_icon_list_option(); + } + } + + protected function actions() { + + parent::actions(); + if ( is_admin() ) { + add_action( 'add_meta_boxes_' . Icons_Manager::CPT, [ $this, 'add_meta_box' ] ); + add_action( 'save_post_' . Icons_Manager::CPT, [ $this, 'save_post_meta' ], 10, 3 ); + add_filter( 'display_post_states', [ $this, 'display_post_states' ], 10, 2 ); + add_action( 'manage_' . Icons_Manager::CPT . '_posts_custom_column', [ $this, 'render_columns' ], 10, 2 ); + add_filter( 'enter_title_here', [ $this, 'update_enter_title_here' ], 10, 2 ); + add_filter( 'manage_' . Icons_Manager::CPT . '_posts_columns', [ $this, 'manage_columns' ], 100 ); + add_action( 'current_screen', [ $this, 'add_custom_icon_templates' ] ); + } + + add_action( 'transition_post_status', [ $this, 'transition_post_status' ], 10, 3 ); + add_action( 'before_delete_post', [ $this, 'handle_delete_icon_set' ] ); + add_filter( 'elementor/icons_manager/additional_tabs', [ $this, 'register_icon_libraries_control' ] ); + add_filter( 'elementor/editor/localize_settings', [ $this, 'add_custom_icons_url' ] ); + + // Ajax. + add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); + } +} diff --git a/modules/assets-manager/asset-types/icons/font-awesome-pro.php b/modules/assets-manager/asset-types/icons/font-awesome-pro.php new file mode 100644 index 0000000..c15e70d --- /dev/null +++ b/modules/assets-manager/asset-types/icons/font-awesome-pro.php @@ -0,0 +1,161 @@ + 'fa-regular', + 'label' => esc_html__( 'Font Awesome - Regular Pro', 'elementor-pro' ), + 'url' => false, + 'enqueue' => false, + 'prefix' => 'fa-', + 'displayPrefix' => 'far', + 'labelIcon' => 'fab fa-font-awesome-alt', + 'ver' => '5.15.1-pro', + 'fetchJson' => sprintf( $json_url, 'regular' ), + 'native' => true, + ]; + $icons['fa-solid'] = [ + 'name' => 'fa-solid', + 'label' => esc_html__( 'Font Awesome - Solid Pro', 'elementor-pro' ), + 'url' => false, + 'enqueue' => false, + 'prefix' => 'fa-', + 'displayPrefix' => 'fas', + 'labelIcon' => 'fab fa-font-awesome', + 'ver' => '5.15.1-pro', + 'fetchJson' => sprintf( $json_url, 'solid' ), + 'native' => true, + ]; + $icons['fa-brands'] = [ + 'name' => 'fa-brands', + 'label' => esc_html__( 'Font Awesome - Brands Pro', 'elementor-pro' ), + 'url' => false, + 'enqueue' => false, + 'prefix' => 'fa-', + 'displayPrefix' => 'fab', + 'labelIcon' => 'fab fa-font-awesome-flag', + 'ver' => '5.15.1-pro', + 'fetchJson' => sprintf( $json_url, 'brands' ), + 'native' => true, + ]; + $icons['fa-light'] = [ + 'name' => 'fa-light', + 'label' => esc_html__( 'Font Awesome - Light Pro', 'elementor-pro' ), + 'url' => false, + 'enqueue' => false, + 'prefix' => 'fa-', + 'displayPrefix' => 'fal', + 'labelIcon' => 'fal fa-flag', + 'ver' => '5.15.1-pro', + 'fetchJson' => sprintf( $json_url, 'light' ), + 'native' => true, + ]; + $icons['fa-duotone'] = [ + 'name' => 'fa-duotone', + 'label' => esc_html__( 'Font Awesome - Duotone Pro', 'elementor-pro' ), + 'url' => false, + 'enqueue' => false, + 'prefix' => 'fa-', + 'displayPrefix' => 'fad', + 'labelIcon' => 'fad fa-flag', + 'ver' => '5.15.1-pro', + 'fetchJson' => sprintf( $json_url, 'duotone' ), + 'native' => true, + ]; + // remove Free + unset( + $settings['fa-solid'], + $settings['fa-regular'], + $settings['fa-brands'] + ); + return array_merge( $icons, $settings ); + } + + public function register_admin_fields( Settings $settings ) { + $settings->add_section( Settings::TAB_INTEGRATIONS, 'font_awesome_pro', [ + 'callback' => function() { + echo '

' . esc_html__( 'Font Awesome Pro', 'elementor-pro' ) . '

'; + esc_html_e( 'Font Awesome, the web\'s most popular icon set and toolkit, Pro Integration', 'elementor-pro' ); + }, + 'fields' => [ + self::FA_KIT_ID_OPTION_NAME => [ + 'label' => esc_html__( 'Kit ID', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + 'desc' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'Enter Your %1$sFont Awesome Pro Kit ID%2$s.', 'elementor-pro' ), + '', + '' + ), + ], + 'setting_args' => [ + 'sanitize_callback' => [ $this, 'sanitize_kit_id_settings' ], + ], + ], + 'validate_api_data' => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => sprintf( '

', + self::FA_KIT_ID_OPTION_NAME . '_fetch', + wp_create_nonce( self::FA_KIT_ID_OPTION_NAME ), + __( 'Validate Kit ID', 'elementor-pro' ) + ), + ], + ], + ], + ] ); + } + + public function enqueue_kit_js() { + wp_enqueue_script( 'font-awesome-pro', sprintf( self::FA_KIT_SCRIPT_LINK, $this->get_kit_id() ), [], ELEMENTOR_PRO_VERSION ); + } + + public function sanitize_kit_id_settings( $input ) { + if ( empty( $input ) ) { + delete_option( 'elementor_' . self::FA_KIT_ID_OPTION_NAME ); + } + + return $input; + } + + protected function actions() { + parent::actions(); + + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 100 ); + } + + if ( $this->get_kit_id() ) { + add_filter( 'elementor/icons_manager/native', [ $this, 'replace_font_awesome_pro' ] ); + add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_kit_js' ] ); + add_action( 'elementor/frontend/after_enqueue_scripts', [ $this, 'enqueue_kit_js' ] ); + } + } +} diff --git a/modules/assets-manager/asset-types/icons/icon-sets/fontastic.php b/modules/assets-manager/asset-types/icons/icon-sets/fontastic.php new file mode 100644 index 0000000..3b0d79b --- /dev/null +++ b/modules/assets-manager/asset-types/icons/icon-sets/fontastic.php @@ -0,0 +1,76 @@ +data = Utils::_unstable_file_get_contents( $this->directory . $this->stylesheet_file ); + $this->dir_name = $this->get_unique_name(); + } + + public function get_type() { + return esc_html__( 'Fontastic', 'elementor-pro' ); + } + + public function is_valid() { + if ( ! file_exists( $this->directory . $this->data_file ) ) { + return false; // missing data file + } + return true; + } + + protected function extract_icon_list() { + $pattern = '/\.' . $this->get_prefix() . '(.*)\:before\s\{/'; + preg_match_all( $pattern, $this->data, $icons_matches ); + if ( empty( $icons_matches[1] ) ) { + return false; // missing icons list + } + $icons = []; + foreach ( $icons_matches[1] as $icon ) { + $icons[] = $icon; + } + return $icons; + } + + protected function get_prefix() { + static $set_prefix = null; + if ( null === $set_prefix ) { + $pattern = '/class\^="(.*)?"/'; + preg_match_all( $pattern, $this->data, $prefix ); + if ( ! isset( $prefix[1][0] ) ) { + return false; // missing css_prefix_text + } + $set_prefix = $prefix[1][0]; + } + return $set_prefix; + } + + public function get_name() { + static $set_name = null; + if ( null === $set_name ) { + $pattern = '/font-family: "(.*)"/'; + preg_match_all( $pattern, $this->data, $name ); + if ( ! isset( $name[1][0] ) ) { + return false; // missing name + } + $set_name = $name[1][0]; + } + return $set_name; + } + + protected function get_stylesheet( $unique_name = '' ) { + return $this->get_url() . '/' . $this->stylesheet_file; + } +} diff --git a/modules/assets-manager/asset-types/icons/icon-sets/fontello.php b/modules/assets-manager/asset-types/icons/icon-sets/fontello.php new file mode 100644 index 0000000..d646a47 --- /dev/null +++ b/modules/assets-manager/asset-types/icons/icon-sets/fontello.php @@ -0,0 +1,79 @@ +remove_fontello_styling(); + $this->dir_name = $this->get_unique_name(); + } + + public function get_type() { + return esc_html__( 'Fontello', 'elementor-pro' ); + } + + public function is_valid() { + if ( ! file_exists( $this->directory . $this->data_file ) ) { + return false; // missing data file + } + return true; + } + + private function remove_fontello_styling() { + $filename = $this->directory . 'css/' . $this->get_name() . '.css'; + $stylesheet = Utils::_unstable_file_get_contents( $filename ); + $stylesheet = str_replace( [ 'margin-left: .2em;', 'margin-right: .2em;' ], [ '', '' ], $stylesheet ); + file_put_contents( $filename, $stylesheet ); + } + + private function get_json() { + return json_decode( Utils::_unstable_file_get_contents( $this->directory . $this->data_file ) ); + } + + protected function extract_icon_list() { + $config = $this->get_json(); + if ( ! isset( $config->glyphs ) ) { + return false; // missing icons list + } + $icons = []; + foreach ( $config->glyphs as $icon ) { + $icons[] = $icon->css; + } + return $icons; + } + + protected function get_prefix() { + $config = $this->get_json(); + if ( ! isset( $config->css_prefix_text ) ) { + return false; // missing css_prefix_text + } + return $config->css_prefix_text; + } + + public function get_name() { + $config = $this->get_json(); + if ( ! isset( $config->name ) ) { + return false; // missing name + } + return $config->name; + } + + protected function get_stylesheet() { + $name = $this->get_name(); + if ( ! $name ) { + return false; // missing name + } + return $this->get_url() . '/css/' . $name . '.css'; + } +} diff --git a/modules/assets-manager/asset-types/icons/icon-sets/icomoon.php b/modules/assets-manager/asset-types/icons/icon-sets/icomoon.php new file mode 100644 index 0000000..1ccc312 --- /dev/null +++ b/modules/assets-manager/asset-types/icons/icon-sets/icomoon.php @@ -0,0 +1,76 @@ +dir_name = $this->get_unique_name(); + return []; + } + + public function get_type() { + return esc_html__( 'Icomoon', 'elementor-pro' ); + } + + public function is_valid() { + if ( ! file_exists( $this->directory . $this->data_file ) ) { + return false; // missing data file + } + return true; + } + + private function get_json() { + return json_decode( Utils::_unstable_file_get_contents( $this->directory . $this->data_file ) ); + } + + protected function extract_icon_list() { + $config = $this->get_json(); + if ( ! isset( $config->icons ) ) { + return false; // missing icons list + } + $icons = []; + foreach ( $config->icons as $icon ) { + $icons[] = $icon->properties->name; + } + return $icons; + } + + protected function get_prefix() { + $config = $this->get_json(); + if ( ! isset( $config->preferences->fontPref->prefix ) ) { + return false; // missing css_prefix_text + } + return $config->preferences->fontPref->prefix; + } + + protected function get_display_prefix() { + $config = $this->get_json(); + if ( ! isset( $config->preferences->fontPref->classSelector ) ) { + return false; // missing css_prefix_text + } + return str_replace( '.', '', $config->preferences->fontPref->classSelector ); + } + + public function get_name() { + $config = $this->get_json(); + if ( ! isset( $config->metadata->name ) ) { + return false; // missing name + } + return $config->metadata->name; + } + + protected function get_stylesheet() { + return $this->get_url( '/' . $this->stylesheet_file ); + } +} diff --git a/modules/assets-manager/asset-types/icons/icon-sets/icon-set-base.php b/modules/assets-manager/asset-types/icons/icon-sets/icon-set-base.php new file mode 100644 index 0000000..91c41dd --- /dev/null +++ b/modules/assets-manager/asset-types/icons/icon-sets/icon-set-base.php @@ -0,0 +1,254 @@ +directory . $path_name; + if ( ! file_exists( $check ) ) { + return false; + } + if ( $this->is_path_dir( $path_name ) ) { + return is_dir( $check ); + } + return true; + } + + /** + * is icon set + * + * validate that the current uploaded zip is in this icon set format + * @return bool + */ + public function is_icon_set() { + foreach ( $this->allowed_zipped_files as $file ) { + if ( ! $this->is_file_allowed( $file ) ) { + return false; + } + } + return true; + } + + public function is_valid() { + return false; + } + + protected function get_display_prefix() { + return ''; + } + + protected function get_prefix() { + return ''; + } + + public function handle_new_icon_set() { + return $this->prepare(); + } + + /** + * cleanup_temp_files + * @param \WP_Filesystem_Base $wp_filesystem + */ + protected function cleanup_temp_files( $wp_filesystem ) { + $wp_filesystem->rmdir( $this->directory, true ); + } + + /** + * Gets the URL to uploaded file. + * + * @param $file_name + * + * @return string + */ + protected function get_file_url( $file_name ) { + $wp_upload_dir = wp_upload_dir(); + $url = $wp_upload_dir['baseurl'] . '/elementor/custom-icons/' . $file_name; + + /** + * Upload file URL. + * + * Filters the URL to a file uploaded using custom icons. + * + * By default URL to a file uploaded is set to `/elementor/custom-icons/{file_name}` + * inside the WordPress uploads folder. This hook allows developers to change this URL. + * + * @since 1.0.0 + * + * @param string $url File URL. + * @param string $file_name File name. + */ + $url = apply_filters( 'elementor_pro/icons_manager/custom_icons/url', $url, $file_name ); + + return $url; + } + + protected function get_icon_sets_dir() { + $wp_upload_dir = wp_upload_dir(); + $path = $wp_upload_dir['basedir'] . '/elementor/custom-icons'; + + /** + * Upload file path. + * + * Filters the path to a folder uploaded using custom icons. + * + * By default the folder path to custom icon files is set to `/elementor/custom-icons` + * inside the WordPress uploads folder. This hook allows developers to change this path. + * + * @param string $path Path to custom icons uploads directory. + */ + $path = apply_filters( 'elementor_pro/icons_manager/custom_icons/dir', $path ); + + Utils::get_ensure_upload_dir( $path ); + return $path; + } + + protected function get_ensure_upload_dir( $dir = '' ) { + $path = $this->get_icon_sets_dir(); + if ( ! empty( $dir ) ) { + $path .= '/' . $dir; + } + return Utils::get_ensure_upload_dir( $path ); + } + + public function move_files( $post_id ) { + // @todo: save only needed files + $wp_filesystem = Custom_Icons::get_wp_filesystem(); + $to = $this->get_ensure_upload_dir( $this->dir_name ) . '/'; + + foreach ( $wp_filesystem->dirlist( $this->directory, false, true ) as $file ) { + $full_path = $this->directory . $file['name']; + if ( $wp_filesystem->is_dir( $full_path ) ) { + $wp_filesystem->mkdir( $to . $file['name'] ); + + foreach ( $file['files'] as $filename => $sub_file ) { + $new_path = $to . $file['name'] . DIRECTORY_SEPARATOR . $filename; + $wp_filesystem->move( $full_path . DIRECTORY_SEPARATOR . $filename, $new_path ); + $this->insert_attachment( $this->get_url() . '/' . $file['name'] . '/' . $filename, $new_path, $post_id ); + } + } else { + $new_path = $to . $file['name']; + $wp_filesystem->move( $full_path, $new_path ); + $this->insert_attachment( $this->get_url() . '/' . $file['name'], $new_path, $post_id ); + } + } + + $this->cleanup_temp_files( $wp_filesystem ); + update_post_meta( $post_id, '_elementor_icon_set_path', $to ); + $this->directory = $to; + } + + private function insert_attachment( $file_url, $filename, $post_id = 0 ) { + $attachment = [ + 'file' => $filename, + 'guid' => $file_url, + 'post_parent' => $post_id, + 'post_type' => 'attachment', + ]; + $id = wp_insert_attachment( $attachment ); + return $id; + } + + public function get_unique_name() { + $name = $this->get_name(); + $basename = $name; + $counter = 1; + while ( ! $this->is_name_unique( $name ) ) { + $name = $basename . '-' . $counter; + $counter++; + } + return $name; + } + + private function is_name_unique( $name ) { + return ! is_dir( $this->get_icon_sets_dir() . '/' . $name ); + } + + protected function get_url( $filename = '' ) { + return $this->get_file_url( $this->dir_name . $filename ); + } + + protected function get_stylesheet() { + return ''; + } + + protected function get_version() { + return '1.0.0'; + } + + protected function get_enqueue() { + return false; + } + + public function build_config() { + $icon_set_config = [ + 'name' => $this->dir_name, + 'label' => ucwords( str_replace( [ '-', '_' ], ' ', $this->dir_name ) ), + 'url' => $this->get_stylesheet(), + 'enqueue' => $this->get_enqueue(), + 'prefix' => $this->get_prefix(), + 'displayPrefix' => $this->get_display_prefix(), + 'labelIcon' => 'eicon eicon-folder', + 'ver' => $this->get_version(), + 'custom_icon_type' => $this->get_type(), + ]; + + $icons = $this->extract_icon_list(); + $icon_set_config['count'] = count( $icons ); + $icon_set_config['icons'] = $icons; + + if ( 25 < $icon_set_config['count'] ) { + $icon_set_config['fetchJson'] = $this->store_icon_list_json( $icons ); + } + + return $icon_set_config; + } + + private function store_icon_list_json( $icons ) { + $wp_filesystem = Custom_Icons::get_wp_filesystem(); + $json_file = $this->get_ensure_upload_dir( $this->dir_name ) . '/e_icons.js'; + $wp_filesystem->put_contents( $json_file, json_encode( [ 'icons' => $icons ] ) ); + return $this->get_url() . '/e_icons.js'; + } + + /** + * Icon Set Base constructor. + * + * @param $directory + */ + public function __construct( $directory ) { + $this->directory = $directory; + return $this->is_icon_set() ? $this : false; + } +} diff --git a/modules/assets-manager/asset-types/icons/templates.php b/modules/assets-manager/asset-types/icons/templates.php new file mode 100644 index 0000000..bd4f895 --- /dev/null +++ b/modules/assets-manager/asset-types/icons/templates.php @@ -0,0 +1,21 @@ + + + + + + diff --git a/modules/assets-manager/classes/assets-base.php b/modules/assets-manager/classes/assets-base.php new file mode 100644 index 0000000..ada7d88 --- /dev/null +++ b/modules/assets-manager/classes/assets-base.php @@ -0,0 +1,378 @@ + +
+ get_metabox_field_html( $field, $field['saved'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + endforeach; + ?> +
+ get_html_field( $field ); + + return $html; + break; + + case 'html_tag': + $html = $this->get_html_tag( $field ); + + return $html; + break; + + case 'toolbar': + $html = $this->get_repeater_tools( $field ); + break; + + case 'input': + $html = $this->get_input_field( $field ); + break; + + case 'select': + $html = $this->get_select_field( $field, $saved ); + break; + + case 'textarea': + $html = $this->get_textarea_field( $field, $saved ); + break; + + case 'file': + $html = $this->get_file_field( $field, $saved ); + break; + + case 'repeater': + $html = $this->get_repeater_field( $field, $saved ); + break; + + case 'dropzone': + $html = $this->get_dropzone_field( $field, $saved ); + break; + + case 'checkbox': + return $this->get_checkbox_field( $field, $saved ); + + default: + $method = 'get_' . $field['field_type'] . 'field'; + if ( method_exists( $this, $method ) ) { + $html = call_user_func( [ $this, $method ], $field, $saved ); + } + break; + } + + return $this->get_field_row( $field, $html ); + } + + public function get_field_label( $field ) { + if ( ! isset( $field['label'] ) || false === $field['label'] ) { + return ''; + } + $id = $field['id']; + if ( 'file' === $field['field_type'] ) { + $id .= $field['field_type']; + } + + return '

'; + } + + public function get_input_field( $attributes ) { + if ( isset( $attributes['input_type'] ) ) { + $attributes['type'] = $attributes['input_type']; + unset( $attributes['input_type'] ); + } + $input = 'get_attribute_string( $attributes ) . '>'; + + return $input; + } + + public function get_attribute_string( $attributes, $field = [] ) { + if ( isset( $field['extra_attributes'] ) && is_array( $field['extra_attributes'] ) ) { + $attributes = array_merge( $attributes, $field['extra_attributes'] ); + } + $attributes_array = []; + foreach ( $attributes as $name => $value ) { + $attributes_array[] = sprintf( '%s="%s"', $name, esc_attr( $value ) ); + } + + return implode( ' ', $attributes_array ); + } + + public function get_select_field( $field, $selected = '' ) { + $input = ''; + } + + public function get_textarea_field( $field, $html ) { + $input = ''; + + return $input; + } + + public function get_file_field( $field, $saved ) { + $value = [ + 'id' => '', + 'url' => '', + ]; + + if ( isset( $saved['id'] ) && isset( $saved['url'] ) ) { + $value = $saved; + } + + $html = '
    '; + $html .= $this->get_input_field( + [ + 'type' => 'hidden', + 'name' => $field['id'] . '[id]', + 'value' => $value['id'], + ] + ); + + $html .= $this->get_input_field( + [ + 'type' => 'text', + 'name' => $field['id'] . '[url]', + 'value' => $value['url'], + 'placeholder' => $field['description'], + 'class' => 'elementor-field-input', + ] + ); + + $html .= $this->get_input_field( + [ + 'type' => 'button', + 'class' => 'button elementor-button elementor-upload-btn', + 'name' => $field['id'], + 'id' => $field['id'], + 'value' => '', + 'data-preview_anchor' => isset( $field['preview_anchor'] ) ? $field['preview_anchor'] : 'none', + 'data-mime_type' => isset( $field['mine'] ) ? $field['mine'] : '', + 'data-ext' => isset( $field['ext'] ) ? $field['ext'] : '', + 'data-upload_text' => esc_html__( 'Upload', 'elementor-pro' ), + 'data-remove_text' => esc_html__( 'Delete', 'elementor-pro' ), + 'data-box_title' => isset( $field['box_title'] ) ? $field['box_title'] : '', + 'data-box_action' => isset( $field['box_action'] ) ? $field['box_action'] : '', + ] + ); + + return $html; + } + + public function get_html_field( $field ) { + return $field['raw_html']; + } + + public function get_dropzone_field( $field ) { + ob_start(); + $input_attributes = [ + 'type' => 'file', + 'name' => $field['id'], + 'id' => $field['id'], + 'accept' => $field['accept'], + 'class' => 'box__file', + ]; + if ( ! empty( $field['multiple'] ) ) { + $input_attributes['multiple'] = true; + } + $input_html = $this->get_input_field( $input_attributes ); + $field['label'] = '

    ' . esc_html__( 'Drag & Drop to Upload', 'elementor-pro' ) . '

    '; + if ( ! empty( $field['sub-label'] ) ) { + $field['label'] .= '
    ' . $field['sub-label'] . '
    '; + } + ?> +
    +
    +
    + +
    + + get_field_label( $field ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> +
    + +
    +
    +
    +
    +
    .
    +
    + 'row_label_' . $js_id, + 'class' => 'repeater-title hidden', + ]; + + if ( is_array( $row_label ) ) { + $label = $row_label['default']; + $row_label_html_args['data-default'] = $row_label['default']; + $row_label_html_args['data-selector'] = $row_label['selector']; + } else { + $label = $row_label; + $row_label_html_args['data-default'] = $row_label; + } + + $row_label_html = 'get_attribute_string( $row_label_html_args ) . '>' . $label . ''; + ob_start(); + ?> + + get_attribute_string( $row_label_html_args ) . '>' . $label . ''; + if ( is_array( $saved ) && count( $saved ) > 0 ) { + foreach ( (array) $saved as $key => $item ) { + echo '
    '; + echo $row_label_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $this->get_repeater_tools( $field ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo ''; // end table + echo '
    '; + $counter++; + } + } + echo ''; + + return ob_get_clean(); + } + + public function get_checkbox_field( $field, $saved ) { + Utils::print_unescaped_internal_string( $this->get_field_row( $field, '' ) ); + + echo '
    '; + + foreach ( $field['options'] as $checkbox_key => $label ) { + $name = $field['id'] . '_' . $checkbox_key; + + $checked = ! empty( $saved ) && in_array( $checkbox_key, $saved, true ) ? 'checked' : ''; + + echo '' . esc_html( $label ) . ''; + } + + echo '
    '; + } + + private function get_html_tag( $field ) { + $tag = isset( $field['tag'] ) ? $field['tag'] : 'div'; + if ( isset( $field['close'] ) && true === $field['close'] ) { + return ''; + } + + return '<' . $tag . ' ' . $this->get_attribute_string( $field['attributes'] ) . '>'; + } + + private function get_repeater_tools( $field ) { + $confirm = isset( $field['confirm'] ) ? $field['confirm'] : esc_html__( 'Are you sure?', 'elementor-pro' ); + $remove_title = isset( $field['remove_title'] ) ? $field['remove_title'] : esc_html__( 'Delete', 'elementor-pro' ); + $toggle_title = isset( $field['toggle_title'] ) ? $field['toggle_title'] : esc_html__( 'Edit', 'elementor-pro' ); + $close_title = isset( $field['close_title'] ) ? $field['close_title'] : esc_html__( 'Close', 'elementor-pro' ); + + return ' + ' . $close_title . ' + + + ' . $toggle_title . ' + + + ' . $remove_title . ' + '; + } + + public function get_field_row( $field, $field_html ) { + $description = ''; + $css_id = isset( $field['id'] ) ? ' ' . $field['id'] : ''; + + if ( isset( $field['real_id'] ) ) { + $css_id = ' ' . $field['real_id']; + } + + $css_id .= ' elementor-field-' . $field['field_type']; + + return '
    ' . $this->get_field_label( $field ) . $field_html . $description . '
    '; + } + + public function sanitize_text_field_recursive( $data ) { + if ( is_array( $data ) ) { + foreach ( $data as $key => $value ) { + $data[ $key ] = $this->sanitize_text_field_recursive( $value ); + } + + return $data; + } + + return sanitize_text_field( $data ); + } + + public function __construct() { + $this->actions(); + } +} diff --git a/modules/assets-manager/classes/font-base.php b/modules/assets-manager/classes/font-base.php new file mode 100644 index 0000000..fd2cc93 --- /dev/null +++ b/modules/assets-manager/classes/font-base.php @@ -0,0 +1,48 @@ +font_preview_phrase = esc_html__( 'Elementor Is Making the Web Beautiful!!!', 'elementor-pro' ); + } + + public function get_name() { + return ''; + } + + public function get_type() { + return ''; + } + + public function handle_panel_request( array $data ) { + return []; + } + + public function get_fonts( $force = false ) {} + + public function enqueue_font( $font_family, $font_data, $post_css ) {} + + public function get_font_family_type( $post_id, $post_title ) {} + + public function get_font_data( $post_id, $post_title ) {} + + public function render_preview_column( $post_id ) {} + + public function get_font_variations_count( $post_id ) {} + + public function save_meta( $post_id, $data ) {} +} diff --git a/modules/assets-manager/module.php b/modules/assets-manager/module.php new file mode 100644 index 0000000..9eb8410 --- /dev/null +++ b/modules/assets-manager/module.php @@ -0,0 +1,51 @@ +asset_managers[ $name ] = $instance; + } + + public function get_assets_manager( $id = null ) { + if ( $id ) { + if ( ! isset( $this->asset_managers[ $id ] ) ) { + return null; + } + + return $this->asset_managers[ $id ]; + } + + return $this->asset_managers; + } + + /** + * @deprecated 3.1.0 + */ + public function localize_settings() { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); + + return []; + } + + public function __construct() { + parent::__construct(); + + $this->add_asset_manager( 'font', new AssetTypes\Fonts_Manager() ); + $this->add_asset_manager( 'icon', new AssetTypes\Icons_Manager() ); + } +} diff --git a/modules/blockquote/module.php b/modules/blockquote/module.php new file mode 100644 index 0000000..ec8139a --- /dev/null +++ b/modules/blockquote/module.php @@ -0,0 +1,21 @@ +start_controls_section( + 'section_blockquote_content', + [ + 'label' => esc_html__( 'Blockquote', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'blockquote_skin', + [ + 'label' => esc_html__( 'Skin', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'border' => esc_html__( 'Border', 'elementor-pro' ), + 'quotation' => esc_html__( 'Quotation', 'elementor-pro' ), + 'boxed' => esc_html__( 'Boxed', 'elementor-pro' ), + 'clean' => esc_html__( 'Clean', 'elementor-pro' ), + ], + 'default' => 'border', + 'prefix_class' => 'elementor-blockquote--skin-', + ] + ); + + $this->add_control( + 'alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'prefix_class' => 'elementor-blockquote--align-', + 'condition' => [ + 'blockquote_skin!' => 'border', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'blockquote_content', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'dynamic' => [ + 'active' => true, + ], + 'default' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor-pro' ) . esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Enter your quote', 'elementor-pro' ), + 'rows' => 10, + ] + ); + + $this->add_control( + 'author_name', + [ + 'label' => esc_html__( 'Author', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'John Doe', 'elementor-pro' ), + 'separator' => 'after', + ] + ); + + $this->add_control( + 'tweet_button', + [ + 'label' => esc_html__( 'Tweet Button', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'tweet_button_view', + [ + 'label' => esc_html__( 'View', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'icon-text' => esc_html__( 'Icon & Text', 'elementor-pro' ), + 'icon' => esc_html__( 'Icon', 'elementor-pro' ), + 'text' => esc_html__( 'Text', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-blockquote--button-view-', + 'default' => 'icon-text', + 'render_type' => 'template', + 'condition' => [ + 'tweet_button' => 'yes', + ], + ] + ); + + $this->add_control( + 'tweet_button_skin', + [ + 'label' => esc_html__( 'Skin', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'classic' => esc_html__( 'Classic', 'elementor-pro' ), + 'bubble' => esc_html__( 'Bubble', 'elementor-pro' ), + 'link' => esc_html__( 'Link', 'elementor-pro' ), + ], + 'default' => 'classic', + 'prefix_class' => 'elementor-blockquote--button-skin-', + 'condition' => [ + 'tweet_button' => 'yes', + ], + ] + ); + + $this->add_control( + 'tweet_button_label', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Tweet', 'elementor-pro' ), + 'condition' => [ + 'tweet_button' => 'yes', + 'tweet_button_view!' => 'icon', + ], + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'user_name', + [ + 'label' => esc_html__( 'Username', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => '@username', + 'condition' => [ + 'tweet_button' => 'yes', + ], + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'url_type', + [ + 'label' => esc_html__( 'Target URL', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'current_page' => esc_html__( 'Current Page', 'elementor-pro' ), + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'current_page', + 'condition' => [ + 'tweet_button' => 'yes', + ], + ] + ); + + $this->add_control( + 'url', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'input_type' => 'url', + 'dynamic' => [ + 'active' => true, + 'categories' => [ + TagsModule::POST_META_CATEGORY, + TagsModule::URL_CATEGORY, + ], + ], + 'ai' => [ + 'active' => false, + ], + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'label_block' => true, + 'condition' => [ + 'url_type' => 'custom', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_content_style', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'content_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__content' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'content_typography', + 'selector' => '{{WRAPPER}} .elementor-blockquote__content', + ] + ); + + $this->add_responsive_control( + 'content_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__content +.e-q-footer' => 'margin-top: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'heading_author_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Author', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'author_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__author' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'author_typography', + 'selector' => '{{WRAPPER}} .elementor-blockquote__author', + ] + ); + + $this->add_responsive_control( + 'author_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 20, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__author' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'alignment' => 'center', + 'tweet_button' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_button_style', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'button_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0.5, + 'max' => 2, + 'step' => 0.1, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__tweet-button' => 'font-size: calc({{SIZE}}{{UNIT}} * 10);', + ], + ] + ); + + $this->add_control( + 'button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__tweet-button' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'min' => 0, + 'max' => 5, + ], + 'rem' => [ + 'min' => 0, + 'max' => 5, + ], + ], + ] + ); + + $this->add_control( + 'button_color_source', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'official' => esc_html__( 'Official', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'official', + 'prefix_class' => 'elementor-blockquote--button-color-', + ] + ); + + $this->start_controls_tabs( 'tabs_button_style' ); + + $this->start_controls_tab( + 'tab_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + 'condition' => [ + 'button_color_source' => 'custom', + ], + ] + ); + + $this->add_control( + 'button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__tweet-button' => 'background-color: {{VALUE}}', + 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote__tweet-button:before, body {{WRAPPER}}.elementor-blockquote--align-left .elementor-blockquote__tweet-button:before' => 'border-right-color: {{VALUE}}; border-left-color: transparent', + 'body.rtl {{WRAPPER}} .elementor-blockquote__tweet-button:before, body {{WRAPPER}}.elementor-blockquote--align-right .elementor-blockquote__tweet-button:before' => 'border-left-color: {{VALUE}}; border-right-color: transparent', + ], + 'condition' => [ + 'button_color_source' => 'custom', + 'tweet_button_skin!' => 'link', + ], + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__tweet-button' => 'color: {{VALUE}}', + '{{WRAPPER}} .elementor-blockquote__tweet-button svg' => 'fill: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + 'condition' => [ + 'button_color_source' => 'custom', + ], + ] + ); + + $this->add_control( + 'button_background_color_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__tweet-button:hover' => 'background-color: {{VALUE}}', + + 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote__tweet-button:hover:before, body {{WRAPPER}}.elementor-blockquote--align-left .elementor-blockquote__tweet-button:hover:before' => 'border-right-color: {{VALUE}}; border-left-color: transparent', + + 'body.rtl {{WRAPPER}} .elementor-blockquote__tweet-button:before, body {{WRAPPER}}.elementor-blockquote--align-right .elementor-blockquote__tweet-button:hover:before' => 'border-left-color: {{VALUE}}; border-right-color: transparent', + ], + 'condition' => [ + 'button_color_source' => 'custom', + 'tweet_button_skin!' => 'link', + ], + ] + ); + + $this->add_control( + 'button_text_color_hover', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__tweet-button:hover' => 'color: {{VALUE}}', + '{{WRAPPER}} .elementor-blockquote__tweet-button:hover svg' => 'fill: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'button_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__tweet-button' => 'transition-duration: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $default_fonts = Plugin::elementor()->kits_manager->get_current_settings( 'default_generic_fonts' ); + + if ( $default_fonts ) { + $default_fonts = ', ' . $default_fonts; + } + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'button_typography', + 'selector' => '{{WRAPPER}} .elementor-blockquote__tweet-button span, {{WRAPPER}} .elementor-blockquote__tweet-button i', + 'separator' => 'before', + 'fields_options' => [ + 'font_family' => [ + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__tweet-button' => 'font-family: "{{VALUE}}"' . $default_fonts . ';', + ], + ], + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_border_style', + [ + 'label' => esc_html__( 'Border', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'blockquote_skin' => 'border', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_border_style' ); + + $this->start_controls_tab( + 'tab_border_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote' => 'border-left-width: {{SIZE}}{{UNIT}}', + 'body.rtl {{WRAPPER}} .elementor-blockquote' => 'border-right-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'border_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote' => 'padding-left: {{SIZE}}{{UNIT}}', + 'body.rtl {{WRAPPER}} .elementor-blockquote' => 'padding-right: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_border_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'border_color_hover', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote:hover' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'border_width_hover', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote:hover' => 'border-left-width: {{SIZE}}{{UNIT}}', + 'body.rtl {{WRAPPER}} .elementor-blockquote:hover' => 'border-right-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'border_gap_hover', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote:hover' => 'padding-left: {{SIZE}}{{UNIT}}', + 'body.rtl {{WRAPPER}} .elementor-blockquote:hover' => 'padding-right: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'border_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote' => 'transition-duration: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_responsive_control( + 'border_vertical_padding', + [ + 'label' => esc_html__( 'Vertical Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote' => 'padding-top: {{SIZE}}{{UNIT}}; padding-bottom: {{SIZE}}{{UNIT}}', + ], + 'separator' => 'before', + 'condition' => [ + 'blockquote_skin' => 'border', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_box_style', + [ + 'label' => esc_html__( 'Box', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'blockquote_skin' => 'boxed', + ], + ] + ); + + $this->add_responsive_control( + 'box_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote' => 'padding: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_box_style' ); + + $this->start_controls_tab( + 'tab_box_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'box_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'box_border', + 'selector' => '{{WRAPPER}} .elementor-blockquote', + ] + ); + + $this->add_responsive_control( + 'box_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_box_shadow', + 'exclude' => [ + 'box_shadow_position', + ], + 'selector' => '{{WRAPPER}} .elementor-blockquote', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_box_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'box_background_color_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote:hover' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'box_border_hover', + 'selector' => '{{WRAPPER}} .elementor-blockquote:hover', + ] + ); + + $this->add_responsive_control( + 'box_border_radius_hover', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote:hover' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_box_shadow_hover', + 'exclude' => [ + 'box_shadow_position', + ], + 'selector' => '{{WRAPPER}} .elementor-blockquote:hover', + ] + ); + + $this->add_control( + 'box_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote' => 'transition-duration: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_quote_style', + [ + 'label' => esc_html__( 'Quote', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'blockquote_skin' => 'quotation', + ], + ] + ); + + $this->add_control( + 'quote_text_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote:before' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'quote_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0.5, + 'max' => 2, + 'step' => 0.1, + ], + ], + 'default' => [ + 'size' => 1, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote:before' => 'font-size: calc({{SIZE}}{{UNIT}} * 100)', + ], + ] + ); + + $this->add_responsive_control( + 'quote_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-blockquote__content' => 'margin-top: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + if ( empty( $settings['blockquote_content'] ) && empty( $settings['author_name'] ) && 'yes' !== $settings['tweet_button'] ) { + return; + } + + $tweet_button_view = $settings['tweet_button_view']; + $share_link = 'https://twitter.com/intent/tweet'; + + $text = rawurlencode( $settings['blockquote_content'] ); + + if ( ! empty( $settings['author_name'] ) ) { + $text .= ' — ' . $settings['author_name']; + } + + $share_link = add_query_arg( 'text', $text, $share_link ); + + if ( 'current_page' === $settings['url_type'] ) { + $share_link = add_query_arg( 'url', rawurlencode( home_url() . add_query_arg( false, false ) ), $share_link ); + } elseif ( 'custom' === $settings['url_type'] ) { + $share_link = add_query_arg( 'url', rawurlencode( $settings['url'] ), $share_link ); + } + + if ( ! empty( $settings['user_name'] ) ) { + $user_name = $settings['user_name']; + if ( '@' === substr( $user_name, 0, 1 ) ) { + $user_name = substr( $user_name, 1 ); + } + $share_link = add_query_arg( 'via', rawurlencode( $user_name ), $share_link ); + } + + $this->add_render_attribute( [ + 'blockquote_content' => [ 'class' => 'elementor-blockquote__content' ], + 'author_name' => [ 'class' => 'elementor-blockquote__author' ], + 'tweet_button_label' => [ 'class' => 'elementor-blockquote__tweet-label' ], + ] ); + + $this->add_inline_editing_attributes( 'blockquote_content' ); + $this->add_inline_editing_attributes( 'author_name', 'none' ); + $this->add_inline_editing_attributes( 'tweet_button_label', 'none' ); + ?> +
    +

    print_render_attribute_string( 'blockquote_content' ); ?>> + print_unescaped_setting( 'blockquote_content' ); ?> +

    + + + +
    + + <# + if ( '' === settings.blockquote_content && '' === settings.author_name && 'yes' !== settings.tweet_button) { + return; + } + + var tweetButtonView = settings.tweet_button_view; + #> +
    +

    + {{{ settings.blockquote_content }}} +

    + <# if ( 'yes' === settings.tweet_button || settings.author_name ) { #> + + <# } #> +
    + start_controls_section( + 'section_main_image', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'skin', + [ + 'label' => esc_html__( 'Skin', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'classic' => esc_html__( 'Classic', 'elementor-pro' ), + 'cover' => esc_html__( 'Cover', 'elementor-pro' ), + ], + 'render_type' => 'template', + 'prefix_class' => 'elementor-cta--skin-', + 'default' => 'classic', + ] + ); + + $this->add_responsive_control( + 'layout', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'above' => [ + 'title' => esc_html__( 'Above', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'prefix_class' => 'elementor-cta-%s-layout-image-', + 'condition' => [ + 'skin!' => 'cover', + ], + ] + ); + + $this->add_control( + 'bg_image', + [ + 'label' => esc_html__( 'Choose Image', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'dynamic' => [ + 'active' => true, + ], + 'default' => [ + 'url' => Utils::get_placeholder_image_src(), + ], + ] + ); + + $this->add_group_control( + Group_Control_Image_Size::get_type(), + [ + 'name' => 'bg_image', // Actually its `image_size` + 'label' => esc_html__( 'Image Resolution', 'elementor-pro' ), + 'default' => 'large', + 'condition' => [ + 'bg_image[id]!' => '', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'graphic_element', + [ + 'label' => esc_html__( 'Graphic Element', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'none' => [ + 'title' => esc_html__( 'None', 'elementor-pro' ), + 'icon' => 'eicon-ban', + ], + 'image' => [ + 'title' => esc_html__( 'Image', 'elementor-pro' ), + 'icon' => 'eicon-image-bold', + ], + 'icon' => [ + 'title' => esc_html__( 'Icon', 'elementor-pro' ), + 'icon' => 'eicon-star', + ], + ], + 'default' => 'none', + ] + ); + + $this->add_control( + 'graphic_image', + [ + 'label' => esc_html__( 'Choose Image', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'dynamic' => [ + 'active' => true, + ], + 'default' => [ + 'url' => Utils::get_placeholder_image_src(), + ], + 'condition' => [ + 'graphic_element' => 'image', + ], + 'show_label' => false, + ] + ); + + $this->add_group_control( + Group_Control_Image_Size::get_type(), + [ + 'name' => 'graphic_image', // Actually its `image_size` + 'default' => 'thumbnail', + 'condition' => [ + 'graphic_element' => 'image', + 'graphic_image[id]!' => '', + ], + ] + ); + + $this->add_control( + 'selected_icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'default' => [ + 'value' => 'fas fa-star', + 'library' => 'fa-solid', + ], + 'condition' => [ + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'icon_view', + [ + 'label' => esc_html__( 'View', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'stacked' => esc_html__( 'Stacked', 'elementor-pro' ), + 'framed' => esc_html__( 'Framed', 'elementor-pro' ), + ], + 'default' => 'default', + 'condition' => [ + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'icon_shape', + [ + 'label' => esc_html__( 'Shape', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'circle' => esc_html__( 'Circle', 'elementor-pro' ), + 'square' => esc_html__( 'Square', 'elementor-pro' ), + ], + 'default' => 'circle', + 'condition' => [ + 'icon_view!' => 'default', + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'default' => esc_html__( 'This is the heading', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Enter your title', 'elementor-pro' ), + 'label_block' => true, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'title_tag', + [ + 'label' => esc_html__( 'Title HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + ], + 'default' => 'h2', + 'condition' => [ + 'title!' => '', + ], + ] + ); + + $this->add_control( + 'description', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'dynamic' => [ + 'active' => true, + ], + 'default' => esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Enter your description', 'elementor-pro' ), + 'separator' => 'before', + 'rows' => 5, + ] + ); + + $this->add_control( + 'description_tag', + [ + 'label' => esc_html__( 'Description HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + ], + 'default' => 'div', + 'condition' => [ + 'description!' => '', + ], + ] + ); + + $this->add_control( + 'button', + [ + 'label' => esc_html__( 'Button Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'default' => esc_html__( 'Click Here', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + + ] + ); + + $this->add_control( + 'link_click', + [ + 'label' => esc_html__( 'Apply Link On', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'box' => esc_html__( 'Whole Box', 'elementor-pro' ), + 'button' => esc_html__( 'Button Only', 'elementor-pro' ), + ], + 'default' => 'button', + 'condition' => [ + 'link[url]!' => '', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_ribbon', + [ + 'label' => esc_html__( 'Ribbon', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'ribbon_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'ribbon_horizontal_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'condition' => [ + 'ribbon_title!' => '', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'box_style', + [ + 'label' => esc_html__( 'Box', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'min-height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 100, + 'max' => 1000, + ], + 'em' => [ + 'min' => 10, + 'max' => 100, + ], + 'rem' => [ + 'min' => 10, + 'max' => 100, + ], + 'vh' => [ + 'min' => 10, + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__content' => 'min-height: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'default' => 'center', + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__content' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'vertical_position', + [ + 'label' => esc_html__( 'Vertical Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'middle' => [ + 'title' => esc_html__( 'Middle', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'prefix_class' => 'elementor-cta--valign-', + ] + ); + + $this->add_responsive_control( + 'padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__content' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'heading_bg_image_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'condition' => [ + 'bg_image[url]!' => '', + 'skin' => 'classic', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'image_min_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 500, + ], + 'em' => [ + 'max' => 50, + ], + 'rem' => [ + 'max' => 50, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__bg-wrapper' => 'min-width: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'skin' => 'classic', + 'layout!' => 'above', + ], + ] + ); + + $this->add_responsive_control( + 'image_min_height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 500, + ], + 'em' => [ + 'max' => 50, + ], + 'rem' => [ + 'max' => 50, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__bg-wrapper' => 'min-height: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'skin' => 'classic', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'graphic_element_style', + [ + 'label' => esc_html__( 'Graphic Element', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'graphic_element!' => [ + 'none', + '', + ], + ], + ] + ); + + $this->add_control( + 'graphic_image_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__image' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'graphic_element' => 'image', + ], + ] + ); + + $this->add_control( + 'graphic_image_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'range' => [ + '%' => [ + 'min' => 5, + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__image img' => 'width: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'graphic_element' => 'image', + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'graphic_image_border', + 'selector' => '{{WRAPPER}} .elementor-cta__image img', + 'condition' => [ + 'graphic_element' => 'image', + ], + ] + ); + + $this->add_control( + 'graphic_image_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__image img' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'graphic_element' => 'image', + ], + ] + ); + + $this->add_control( + 'icon_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-wrapper' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'icon_primary_color', + [ + 'label' => esc_html__( 'Primary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-view-stacked .elementor-icon' => 'background-color: {{VALUE}}', + '{{WRAPPER}} .elementor-view-stacked .elementor-icon svg' => 'stroke: {{VALUE}}', + '{{WRAPPER}} .elementor-view-framed .elementor-icon, {{WRAPPER}} .elementor-view-default .elementor-icon' => 'color: {{VALUE}}; border-color: {{VALUE}}', + '{{WRAPPER}} .elementor-view-framed .elementor-icon, {{WRAPPER}} .elementor-view-default .elementor-icon svg' => 'fill: {{VALUE}};', + ], + 'condition' => [ + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'icon_secondary_color', + [ + 'label' => esc_html__( 'Secondary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'condition' => [ + 'graphic_element' => 'icon', + 'icon_view!' => 'default', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-view-framed .elementor-icon' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .elementor-view-framed .elementor-icon svg' => 'stroke: {{VALUE}};', + '{{WRAPPER}} .elementor-view-stacked .elementor-icon' => 'color: {{VALUE}};', + '{{WRAPPER}} .elementor-view-stacked .elementor-icon svg' => 'fill: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'icon_size', + [ + 'label' => esc_html__( 'Icon Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 6, + 'max' => 300, + ], + 'em' => [ + 'min' => 0.6, + 'max' => 30, + ], + 'rem' => [ + 'min' => 0.6, + 'max' => 30, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon' => 'font-size: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'icon_padding', + [ + 'label' => esc_html__( 'Icon Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon' => 'padding: {{SIZE}}{{UNIT}};', + ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'min' => 0, + 'max' => 5, + ], + 'rem' => [ + 'min' => 0, + 'max' => 5, + ], + ], + 'condition' => [ + 'graphic_element' => 'icon', + 'icon_view!' => 'default', + ], + ] + ); + + $this->add_control( + 'icon_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon' => 'border-width: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'graphic_element' => 'icon', + 'icon_view' => 'framed', + ], + ] + ); + + $this->add_control( + 'icon_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'graphic_element' => 'icon', + 'icon_view!' => 'default', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_content_style', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'title', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'description', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ] + ); + + $this->add_control( + 'heading_style_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'condition' => [ + 'title!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'title_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-cta__title', + 'condition' => [ + 'title!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Stroke::get_type(), + [ + 'name' => 'text_stroke', + 'selector' => '{{WRAPPER}} .elementor-cta__title', + ] + ); + + $this->add_responsive_control( + 'title_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__title:not(:last-child)' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'title!' => '', + ], + ] + ); + + $this->add_control( + 'heading_style_description', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'separator' => 'before', + 'condition' => [ + 'description!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'description_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-cta__description', + 'condition' => [ + 'description!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'description_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__description:not(:last-child)' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'description!' => '', + ], + ] + ); + + $this->add_control( + 'heading_content_colors', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Colors', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->start_controls_tabs( 'color_tabs' ); + + $this->start_controls_tab( 'colors_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'content_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__content' => 'background-color: {{VALUE}}', + ], + 'condition' => [ + 'skin' => 'classic', + ], + ] + ); + + $this->add_control( + 'title_color', + [ + 'label' => esc_html__( 'Title Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__title' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'title!' => '', + ], + ] + ); + + $this->add_control( + 'description_color', + [ + 'label' => esc_html__( 'Description Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__description' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'description!' => '', + ], + ] + ); + + $this->add_control( + 'button_color', + [ + 'label' => esc_html__( 'Button Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__button' => 'color: {{VALUE}}; border-color: {{VALUE}}', + ], + 'condition' => [ + 'button!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'colors_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'content_bg_color_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta:hover .elementor-cta__content' => 'background-color: {{VALUE}}', + ], + 'condition' => [ + 'skin' => 'classic', + ], + ] + ); + + $this->add_control( + 'title_color_hover', + [ + 'label' => esc_html__( 'Title Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta:hover .elementor-cta__title' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'title!' => '', + ], + ] + ); + + $this->add_control( + 'description_color_hover', + [ + 'label' => esc_html__( 'Description Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta:hover .elementor-cta__description' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'description!' => '', + ], + ] + ); + + $this->add_control( + 'button_color_hover', + [ + 'label' => esc_html__( 'Button Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta:hover .elementor-cta__button' => 'color: {{VALUE}}; border-color: {{VALUE}}', + ], + 'condition' => [ + 'button!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'button_style', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'button!' => '', + ], + ] + ); + + $this->add_control( + 'button_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'xs' => esc_html__( 'Extra Small', 'elementor-pro' ), + 'sm' => esc_html__( 'Small', 'elementor-pro' ), + 'md' => esc_html__( 'Medium', 'elementor-pro' ), + 'lg' => esc_html__( 'Large', 'elementor-pro' ), + 'xl' => esc_html__( 'Extra Large', 'elementor-pro' ), + ], + 'default' => 'sm', + 'condition' => [ + 'button_size!' => 'sm', // a workaround to hide the control, unless it's in use (not default). + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'button_typography', + 'selector' => '{{WRAPPER}} .elementor-cta__button', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'button_text_shadow', + 'selector' => '{{WRAPPER}} .elementor-cta__button', + ] + ); + + $this->start_controls_tabs( 'button_tabs' ); + + $this->start_controls_tab( 'button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__button' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__button' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__button' => 'border-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'button-hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__button:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_hover_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__button:hover' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__button:hover' => 'border-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'button_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__button' => 'border-width: {{SIZE}}{{UNIT}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__button' => 'border-radius: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'button_box_shadow', + 'selector' => '{{WRAPPER}} .elementor-cta__button', + ] + ); + + $this->add_responsive_control( + 'button_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'before', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_ribbon_style', + [ + 'label' => esc_html__( 'Ribbon', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'show_label' => false, + 'condition' => [ + 'ribbon_title!' => '', + ], + ] + ); + + $this->add_control( + 'ribbon_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-ribbon-inner' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'ribbon_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-ribbon-inner' => 'color: {{VALUE}}', + ], + ] + ); + + $ribbon_distance_transform = is_rtl() ? 'translateY(-50%) translateX({{SIZE}}{{UNIT}}) rotate(-45deg)' : 'translateY(-50%) translateX(-50%) translateX({{SIZE}}{{UNIT}}) rotate(-45deg)'; + + $this->add_responsive_control( + 'ribbon_distance', + [ + 'label' => esc_html__( 'Distance', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'em' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-ribbon-inner' => 'margin-top: {{SIZE}}{{UNIT}}; transform: ' . $ribbon_distance_transform, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'ribbon_typography', + 'selector' => '{{WRAPPER}} .elementor-ribbon-inner', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_shadow', + 'selector' => '{{WRAPPER}} .elementor-ribbon-inner', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'hover_effects', + [ + 'label' => esc_html__( 'Hover Effects', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'content_hover_heading', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'condition' => [ + 'skin' => 'cover', + ], + ] + ); + + $this->add_control( + 'content_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => [ + [ + 'label' => esc_html__( 'None', 'elementor-pro' ), + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + ], + ], + [ + 'label' => esc_html__( 'Entrance', 'elementor-pro' ), + 'options' => [ + 'enter-from-right' => 'Slide In Right', + 'enter-from-left' => 'Slide In Left', + 'enter-from-top' => 'Slide In Up', + 'enter-from-bottom' => 'Slide In Down', + 'enter-zoom-in' => 'Zoom In', + 'enter-zoom-out' => 'Zoom Out', + 'fade-in' => 'Fade In', + ], + ], + [ + 'label' => esc_html__( 'Reaction', 'elementor-pro' ), + 'options' => [ + 'grow' => 'Grow', + 'shrink' => 'Shrink', + 'move-right' => 'Move Right', + 'move-left' => 'Move Left', + 'move-up' => 'Move Up', + 'move-down' => 'Move Down', + ], + ], + [ + 'label' => esc_html__( 'Exit', 'elementor-pro' ), + 'options' => [ + 'exit-to-right' => 'Slide Out Right', + 'exit-to-left' => 'Slide Out Left', + 'exit-to-top' => 'Slide Out Up', + 'exit-to-bottom' => 'Slide Out Down', + 'exit-zoom-in' => 'Zoom In', + 'exit-zoom-out' => 'Zoom Out', + 'fade-out' => 'Fade Out', + ], + ], + ], + 'default' => 'grow', + 'condition' => [ + 'skin' => 'cover', + ], + ] + ); + + /* + * + * Add class 'elementor-animated-content' to widget when assigned content animation + * + */ + $this->add_control( + 'animation_class', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HIDDEN, + 'default' => 'animated-content', + 'prefix_class' => 'elementor-', + 'condition' => [ + 'content_animation!' => '', + ], + ] + ); + + $this->add_control( + 'content_animation_duration', + [ + 'label' => esc_html__( 'Animation Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'render_type' => 'template', + 'default' => [ + 'size' => 1000, + ], + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__content-item' => 'transition-duration: {{SIZE}}ms', + '{{WRAPPER}}.elementor-cta--sequenced-animation .elementor-cta__content-item:nth-child(2)' => 'transition-delay: calc( {{SIZE}}ms / 3 )', + '{{WRAPPER}}.elementor-cta--sequenced-animation .elementor-cta__content-item:nth-child(3)' => 'transition-delay: calc( ( {{SIZE}}ms / 3 ) * 2 )', + '{{WRAPPER}}.elementor-cta--sequenced-animation .elementor-cta__content-item:nth-child(4)' => 'transition-delay: calc( ( {{SIZE}}ms / 3 ) * 3 )', + ], + 'condition' => [ + 'content_animation!' => '', + 'skin' => 'cover', + ], + ] + ); + + $this->add_control( + 'sequenced_animation', + [ + 'label' => esc_html__( 'Sequenced Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'return_value' => 'elementor-cta--sequenced-animation', + 'prefix_class' => '', + 'condition' => [ + 'content_animation!' => '', + 'skin' => 'cover', + ], + ] + ); + + $this->add_control( + 'background_hover_heading', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Background', 'elementor-pro' ), + 'separator' => 'before', + 'condition' => [ + 'skin' => 'cover', + ], + ] + ); + + $this->add_control( + 'transformation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => 'None', + 'zoom-in' => 'Zoom In', + 'zoom-out' => 'Zoom Out', + 'move-left' => 'Move Left', + 'move-right' => 'Move Right', + 'move-up' => 'Move Up', + 'move-down' => 'Move Down', + ], + 'default' => 'zoom-in', + 'prefix_class' => 'elementor-bg-transform elementor-bg-transform-', + ] + ); + + $this->start_controls_tabs( 'bg_effects_tabs' ); + + $this->start_controls_tab( 'normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'overlay_color', + [ + 'label' => esc_html__( 'Overlay Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta:not(:hover) .elementor-cta__bg-overlay' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Css_Filter::get_type(), + [ + 'name' => 'bg_filters', + 'selector' => '{{WRAPPER}} .elementor-cta__bg', + ] + ); + + $this->add_control( + 'overlay_blend_mode', + [ + 'label' => esc_html__( 'Blend Mode', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Normal', 'elementor-pro' ), + 'multiply' => 'Multiply', + 'screen' => 'Screen', + 'overlay' => 'Overlay', + 'darken' => 'Darken', + 'lighten' => 'Lighten', + 'color-dodge' => 'Color Dodge', + 'color-burn' => 'Color Burn', + 'hue' => 'Hue', + 'saturation' => 'Saturation', + 'color' => 'Color', + 'exclusion' => 'Exclusion', + 'luminosity' => 'Luminosity', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta__bg-overlay' => 'mix-blend-mode: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'overlay_color_hover', + [ + 'label' => esc_html__( 'Overlay Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-cta:hover .elementor-cta__bg-overlay' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Css_Filter::get_type(), + [ + 'name' => 'bg_filters_hover', + 'selector' => '{{WRAPPER}} .elementor-cta:hover .elementor-cta__bg', + ] + ); + + $this->add_control( + 'effect_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'render_type' => 'template', + 'default' => [ + 'size' => 1500, + ], + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-cta .elementor-cta__bg, {{WRAPPER}} .elementor-cta .elementor-cta__bg-overlay' => 'transition-duration: {{SIZE}}ms', + ], + 'separator' => 'before', + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + $wrapper_tag = 'div'; + $button_tag = 'a'; + $title_tag = Utils::validate_html_tag( $settings['title_tag'] ); + $description_tag = Utils::validate_html_tag( $settings['description_tag'] ); + $bg_image = ''; + $content_animation = $settings['content_animation']; + $animation_class = ''; + $print_bg = true; + $print_content = true; + + if ( ! empty( $settings['bg_image']['id'] ) ) { + $bg_image = Group_Control_Image_Size::get_attachment_image_src( $settings['bg_image']['id'], 'bg_image', $settings ); + } elseif ( ! empty( $settings['bg_image']['url'] ) ) { + $bg_image = $settings['bg_image']['url']; + } + + if ( empty( $bg_image ) && 'classic' == $settings['skin'] ) { + $print_bg = false; + } + + if ( empty( $settings['title'] ) && empty( $settings['description'] ) && empty( $settings['button'] ) && 'none' == $settings['graphic_element'] ) { + $print_content = false; + } + + $this->add_render_attribute( 'wrapper', 'class', 'elementor-cta' ); + + /*$this->add_render_attribute( 'background_image', 'style', [ + 'background-image: url(' . $bg_image . ');', + ] );*/ + + $this->add_render_attribute( + 'background_image', + [ + 'style' => 'background-image: url(' . esc_url( $bg_image ) . ');', + 'role' => 'img', + 'aria-label' => Control_Media::get_image_alt( $settings['bg_image'] ), + ] + ); + + $this->add_render_attribute( 'title', 'class', [ + 'elementor-cta__title', + 'elementor-cta__content-item', + 'elementor-content-item', + ] ); + + $this->add_render_attribute( 'description', 'class', [ + 'elementor-cta__description', + 'elementor-cta__content-item', + 'elementor-content-item', + ] ); + + $this->add_render_attribute( 'button', 'class', [ + 'elementor-cta__button', + 'elementor-button', + 'elementor-size-' . $settings['button_size'], + ] ); + + $this->add_render_attribute( 'graphic_element', 'class', + [ + 'elementor-content-item', + 'elementor-cta__content-item', + ] + ); + + if ( 'icon' === $settings['graphic_element'] ) { + $this->add_render_attribute( 'graphic_element', 'class', + [ + 'elementor-icon-wrapper', + 'elementor-cta__icon', + ] + ); + $this->add_render_attribute( 'graphic_element', 'class', 'elementor-view-' . $settings['icon_view'] ); + if ( 'default' != $settings['icon_view'] ) { + $this->add_render_attribute( 'graphic_element', 'class', 'elementor-shape-' . $settings['icon_shape'] ); + } + + if ( ! isset( $settings['icon'] ) && ! Icons_Manager::is_migration_allowed() ) { + // add old default + $settings['icon'] = 'fa fa-star'; + } + + if ( ! empty( $settings['icon'] ) ) { + $this->add_render_attribute( 'icon', 'class', $settings['icon'] ); + } + } elseif ( 'image' === $settings['graphic_element'] && ! empty( $settings['graphic_image']['url'] ) ) { + $this->add_render_attribute( 'graphic_element', 'class', 'elementor-cta__image' ); + } + + if ( ! empty( $content_animation ) && 'cover' == $settings['skin'] ) { + + $animation_class = 'elementor-animated-item--' . $content_animation; + + $this->add_render_attribute( 'title', 'class', $animation_class ); + + $this->add_render_attribute( 'graphic_element', 'class', $animation_class ); + + $this->add_render_attribute( 'description', 'class', $animation_class ); + + } + + if ( ! empty( $settings['link']['url'] ) ) { + $link_element = 'button'; + + if ( 'box' === $settings['link_click'] ) { + $wrapper_tag = 'a'; + $button_tag = 'span'; + $link_element = 'wrapper'; + } + + $this->add_link_attributes( $link_element, $settings['link'] ); + } + + $this->add_inline_editing_attributes( 'title' ); + $this->add_inline_editing_attributes( 'description' ); + $this->add_inline_editing_attributes( 'button' ); + + $migrated = isset( $settings['__fa4_migrated']['selected_icon'] ); + $is_new = empty( $settings['icon'] ) && Icons_Manager::is_migration_allowed(); + + ?> + < print_render_attribute_string( 'wrapper' ); ?>> + +
    +
    print_render_attribute_string( 'background_image' ); ?>>
    +
    +
    + + +
    + +
    print_render_attribute_string( 'graphic_element' ); ?>> + +
    + +
    print_render_attribute_string( 'graphic_element' ); ?>> +
    + 'true' ] ); + else : ?> + print_render_attribute_string( 'icon' ); ?>> + +
    +
    + + + + < print_render_attribute_string( 'title' ); ?>> + print_unescaped_setting( 'title' ); ?> + > + + + + < print_render_attribute_string( 'description' ); ?>> + print_unescaped_setting( 'description' ); ?> + > + + + +
    + < print_render_attribute_string( 'button' ); ?>> + print_unescaped_setting( 'button' ); ?> + > +
    + +
    + + add_render_attribute( 'ribbon-wrapper', 'class', 'elementor-ribbon' ); + + if ( ! empty( $settings['ribbon_horizontal_position'] ) ) { + $this->add_render_attribute( 'ribbon-wrapper', 'class', 'elementor-ribbon-' . $settings['ribbon_horizontal_position'] ); + } + ?> +
    print_render_attribute_string( 'ribbon-wrapper' ); ?>> +
    print_unescaped_setting( 'ribbon_title' ); ?>
    +
    + + > + + <# + var wrapperTag = 'div', + buttonTag = 'a', + titleTag = elementor.helpers.validateHTMLTag( settings.title_tag ), + descriptionTag = elementor.helpers.validateHTMLTag( settings.description_tag ), + contentAnimation = settings.content_animation, + animationClass, + btnSizeClass = 'elementor-size-' + settings.button_size, + printBg = true, + printContent = true, + iconHTML = elementor.helpers.renderIcon( view, settings.selected_icon, { 'aria-hidden': true }, 'i' , 'object' ), + migrated = elementor.helpers.isIconMigrated( settings, 'selected_icon' ); + + if ( 'box' === settings.link_click ) { + wrapperTag = 'a'; + buttonTag = 'span'; + view.addRenderAttribute( 'wrapper', 'href', '#' ); + } + + if ( '' !== settings.bg_image.url ) { + var bg_image = { + id: settings.bg_image.id, + url: settings.bg_image.url, + size: settings.bg_image_size, + dimension: settings.bg_image_custom_dimension, + model: view.getEditModel() + }; + + var bgImageUrl = elementor.imagesManager.getImageUrl( bg_image ); + } + + if ( ! bg_image && 'classic' == settings.skin ) { + printBg = false; + } + + if ( ! settings.title && ! settings.description && ! settings.button && 'none' == settings.graphic_element ) { + printContent = false; + } + + if ( 'icon' === settings.graphic_element ) { + var iconWrapperClasses = 'elementor-icon-wrapper'; + iconWrapperClasses += ' elementor-cta__image'; + iconWrapperClasses += ' elementor-view-' + settings.icon_view; + if ( 'default' !== settings.icon_view ) { + iconWrapperClasses += ' elementor-shape-' + settings.icon_shape; + } + view.addRenderAttribute( 'graphic_element', 'class', iconWrapperClasses ); + + } else if ( 'image' === settings.graphic_element && '' !== settings.graphic_image.url ) { + var image = { + id: settings.graphic_image.id, + url: settings.graphic_image.url, + size: settings.graphic_image_size, + dimension: settings.graphic_image_custom_dimension, + model: view.getEditModel() + }; + + var imageUrl = elementor.imagesManager.getImageUrl( image ); + view.addRenderAttribute( 'graphic_element', 'class', 'elementor-cta__image' ); + } + + if ( contentAnimation && 'cover' === settings.skin ) { + + var animationClass = 'elementor-animated-item--' + contentAnimation; + + view.addRenderAttribute( 'title', 'class', animationClass ); + + view.addRenderAttribute( 'description', 'class', animationClass ); + + view.addRenderAttribute( 'graphic_element', 'class', animationClass ); + } + + view.addRenderAttribute( + 'background_image', + { + 'style': 'background-image: url(' + bgImageUrl + ');', + 'role': 'img', + 'aria-label': '', + } + ); + + view.addRenderAttribute( 'title', 'class', [ 'elementor-cta__title', 'elementor-cta__content-item', 'elementor-content-item' ] ); + view.addRenderAttribute( 'description', 'class', [ 'elementor-cta__description', 'elementor-cta__content-item', 'elementor-content-item' ] ); + view.addRenderAttribute( 'button', 'class', [ 'elementor-cta__button', 'elementor-button', btnSizeClass ] ); + view.addRenderAttribute( 'graphic_element', 'class', [ 'elementor-cta__content-item', 'elementor-content-item' ] ); + + view.addInlineEditingAttributes( 'title' ); + view.addInlineEditingAttributes( 'description' ); + view.addInlineEditingAttributes( 'button' ); + #> + + <{{ wrapperTag }} class="elementor-cta" {{{ view.getRenderAttributeString( 'wrapper' ) }}}> + + <# if ( printBg ) { #> +
    +
    +
    +
    + <# } #> + <# if ( printContent ) { #> +
    + <# if ( 'image' === settings.graphic_element && '' !== settings.graphic_image.url ) { #> +
    + +
    + <# } else if ( 'icon' === settings.graphic_element && ( settings.icon || settings.selected_icon ) ) { #> +
    +
    + <# if ( iconHTML && iconHTML.rendered && ( ! settings.icon || migrated ) ) { #> + {{{ iconHTML.value }}} + <# } else { #> + + <# } #> +
    +
    + <# } #> + <# if ( settings.title ) { #> + <{{ titleTag }} {{{ view.getRenderAttributeString( 'title' ) }}}>{{{ settings.title }}} + <# } #> + + <# if ( settings.description ) { #> + <{{ descriptionTag }} {{{ view.getRenderAttributeString( 'description' ) }}}>{{{ settings.description }}} + <# } #> + + <# if ( settings.button ) { #> +
    + <{{ buttonTag }} href="#" {{{ view.getRenderAttributeString( 'button' ) }}}>{{{ settings.button }}} +
    + <# } #> +
    + <# } #> + <# if ( settings.ribbon_title ) { + var ribbonClasses = 'elementor-ribbon'; + + if ( settings.ribbon_horizontal_position ) { + ribbonClasses += ' elementor-ribbon-' + settings.ribbon_horizontal_position; + } #> +
    +
    {{{ settings.ribbon_title }}}
    +
    + <# } #> + + start_controls_section( + 'section_slides', + [ + 'label' => esc_html__( 'Slides', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $repeater = new Repeater(); + + $this->add_repeater_controls( $repeater ); + + $this->add_control( + 'slides', + [ + 'label' => esc_html__( 'Slides', 'elementor-pro' ), + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'default' => $this->get_repeater_defaults(), + 'separator' => 'after', + ] + ); + + $this->add_control( + 'effect', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Effect', 'elementor-pro' ), + 'default' => 'slide', + 'options' => [ + 'slide' => esc_html__( 'Slide', 'elementor-pro' ), + 'fade' => esc_html__( 'Fade', 'elementor-pro' ), + 'cube' => esc_html__( 'Cube', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $slides_per_view = range( 1, 10 ); + $slides_per_view = array_combine( $slides_per_view, $slides_per_view ); + + $this->add_responsive_control( + 'slides_per_view', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Slides Per View', 'elementor-pro' ), + 'options' => [ '' => esc_html__( 'Default', 'elementor-pro' ) ] + $slides_per_view, + 'inherit_placeholders' => false, + 'condition' => [ + 'effect' => 'slide', + ], + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( + 'slides_to_scroll', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Slides to Scroll', 'elementor-pro' ), + 'description' => esc_html__( 'Set how many slides are scrolled per swipe.', 'elementor-pro' ), + 'options' => [ '' => esc_html__( 'Default', 'elementor-pro' ) ] + $slides_per_view, + 'inherit_placeholders' => false, + 'condition' => [ + 'effect' => 'slide', + ], + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( + 'height', + [ + 'type' => Controls_Manager::SLIDER, + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 100, + 'max' => 1000, + ], + 'em' => [ + 'min' => 10, + 'max' => 100, + ], + 'rem' => [ + 'min' => 10, + 'max' => 100, + ], + 'vh' => [ + 'min' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-main-swiper' => 'height: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'width', + [ + 'type' => Controls_Manager::SLIDER, + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 100, + 'max' => 1140, + ], + '%' => [ + 'min' => 50, + ], + ], + 'default' => [ + 'unit' => '%', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-main-swiper' => 'width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_additional_options', + [ + 'label' => esc_html__( 'Additional Options', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'show_arrows', + [ + 'type' => Controls_Manager::SWITCHER, + 'label' => esc_html__( 'Arrows', 'elementor-pro' ), + 'default' => 'yes', + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'prefix_class' => 'elementor-arrows-', + 'render_type' => 'template', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'pagination', + [ + 'label' => esc_html__( 'Pagination', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'bullets', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'bullets' => esc_html__( 'Dots', 'elementor-pro' ), + 'fraction' => esc_html__( 'Fraction', 'elementor-pro' ), + 'progressbar' => esc_html__( 'Progress', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-pagination-type-', + 'render_type' => 'template', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'speed', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 500, + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'autoplay', + [ + 'label' => esc_html__( 'Autoplay', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'separator' => 'before', + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'autoplay_speed', + [ + 'label' => esc_html__( 'Autoplay Speed', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 5000, + 'condition' => [ + 'autoplay' => 'yes', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'loop', + [ + 'label' => esc_html__( 'Infinite Loop', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'pause_on_hover', + [ + 'label' => esc_html__( 'Pause on Hover', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'condition' => [ + 'autoplay' => 'yes', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'pause_on_interaction', + [ + 'label' => esc_html__( 'Pause on Interaction', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'condition' => [ + 'autoplay' => 'yes', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->add_group_control( + Group_Control_Image_Size::get_type(), + [ + 'name' => 'image_size', + 'default' => 'full', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'lazyload', + [ + 'label' => esc_html__( 'Lazyload', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'separator' => 'before', + 'frontend_available' => true, + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_slides_style', + [ + 'label' => esc_html__( 'Slides', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $space_between_config = [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'max' => 50, + ], + ], + 'render_type' => 'none', + 'frontend_available' => true, + ]; + + // TODO: Once Core 3.4.0 is out, get the active devices using Breakpoints/Manager::get_active_devices_list(). + $active_breakpoint_instances = Plugin::elementor()->breakpoints->get_active_breakpoints(); + // Devices need to be ordered from largest to smallest. + $active_devices = array_reverse( array_keys( $active_breakpoint_instances ) ); + + // Add desktop in the correct position. + if ( in_array( 'widescreen', $active_devices, true ) ) { + $active_devices = array_merge( array_slice( $active_devices, 0, 1 ), [ 'desktop' ], array_slice( $active_devices, 1 ) ); + } else { + $active_devices = array_merge( [ 'desktop' ], $active_devices ); + } + + foreach ( $active_devices as $active_device ) { + $space_between_config[ $active_device . '_default' ] = [ + 'size' => 10, + ]; + } + + $this->add_responsive_control( + 'space_between', + $space_between_config + ); + + $this->add_control( + 'slide_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-main-swiper .swiper-slide' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'slide_border_size', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-main-swiper .swiper-slide' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'slide_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + '%' => [ + 'max' => 50, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-main-swiper .swiper-slide' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'slide_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-main-swiper .swiper-slide' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'slide_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-main-swiper .swiper-slide' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + 'separator' => 'before', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_navigation', + [ + 'label' => esc_html__( 'Navigation', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'heading_arrows', + [ + 'label' => esc_html__( 'Arrows', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_responsive_control( + 'arrows_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 20, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-swiper-button' => 'font-size: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'arrows_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-swiper-button' => 'color: {{VALUE}}', + '{{WRAPPER}} .elementor-swiper-button svg' => 'fill: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'heading_pagination', + [ + 'label' => esc_html__( 'Pagination', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'condition' => [ + 'pagination!' => '', + ], + ] + ); + + $this->add_control( + 'pagination_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'outside', + 'options' => [ + 'outside' => esc_html__( 'Outside', 'elementor-pro' ), + 'inside' => esc_html__( 'Inside', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-pagination-position-', + 'condition' => [ + 'pagination!' => '', + ], + ] + ); + + $swiper_class = Plugin::elementor()->experiments->is_feature_active( 'e_swiper_latest' ) ? 'swiper' : 'swiper-container'; + + $this->add_responsive_control( + 'pagination_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .swiper-pagination-bullet' => 'height: {{SIZE}}{{UNIT}}; width: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .' . $swiper_class . '-horizontal .swiper-pagination-progressbar' => 'height: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .swiper-pagination-fraction' => 'font-size: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'pagination!' => '', + ], + ] + ); + + $this->add_control( + 'pagination_color_inactive', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + // The opacity property will override the default inactive dot color which is opacity 0.2. + '{{WRAPPER}} .swiper-pagination-bullet:not(.swiper-pagination-bullet-active)' => 'background-color: {{VALUE}}; opacity: 1;', + ], + 'condition' => [ + 'pagination!' => '', + ], + ] + ); + + $this->add_control( + 'pagination_color', + [ + 'label' => esc_html__( 'Active Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .swiper-pagination-bullet-active, {{WRAPPER}} .swiper-pagination-progressbar-fill' => 'background-color: {{VALUE}}', + '{{WRAPPER}} .swiper-pagination-fraction' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'pagination!' => '', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function print_slider( array $settings = null ) { + if ( null === $settings ) { + $settings = $this->get_settings_for_display(); + } + + $default_settings = [ + 'container_class' => 'elementor-main-swiper', + 'video_play_icon' => true, + ]; + + $settings = array_merge( $default_settings, $settings ); + + $slides_count = count( $settings['slides'] ); + $swiper_class = Plugin::elementor()->experiments->is_feature_active( 'e_swiper_latest' ) ? 'swiper' : 'swiper-container'; + ?> +
    +
    +
    + $slide ) : + $this->slide_prints_count++; + ?> +
    + print_slide( $slide, $settings, 'slide-' . $index . '-' . $this->slide_prints_count ); ?> +
    + +
    + + +
    + + +
    + render_swiper_button( 'previous' ); ?> + +
    +
    + render_swiper_button( 'next' ); ?> + +
    + + +
    +
    + 'eicons', + 'value' => $icon_value, + ], [ 'aria-hidden' => 'true' ] ); + } +} diff --git a/modules/carousel/widgets/media-carousel.php b/modules/carousel/widgets/media-carousel.php new file mode 100644 index 0000000..c9a5f6d --- /dev/null +++ b/modules/carousel/widgets/media-carousel.php @@ -0,0 +1,828 @@ +get_active_settings(); + + if ( $settings['overlay'] ) { + $this->add_render_attribute( 'image-overlay', 'class', [ + 'elementor-carousel-image-overlay', + 'e-overlay-animation-' . $settings['overlay_animation'], + ] ); + } + + $this->print_slider(); + + if ( 'slideshow' !== $settings['skin'] || count( $settings['slides'] ) <= 1 ) { + return; + } + + $settings['thumbs_slider'] = true; + $settings['container_class'] = 'elementor-thumbnails-swiper'; + $settings['show_arrows'] = false; + + $this->print_slider( $settings ); + } + + protected function register_controls() { + parent::register_controls(); + + $this->start_controls_section( + 'section_lightbox_style', + [ + 'label' => esc_html__( 'Lightbox', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'lightbox_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '#elementor-lightbox-slideshow-{{ID}}' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'lightbox_ui_color', + [ + 'label' => esc_html__( 'UI Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '#elementor-lightbox-slideshow-{{ID}} .dialog-lightbox-close-button, #elementor-lightbox-slideshow-{{ID}} .elementor-swiper-button' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'lightbox_ui_hover_color', + [ + 'label' => esc_html__( 'UI Hover Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '#elementor-lightbox-slideshow-{{ID}} .dialog-lightbox-close-button:hover, #elementor-lightbox-slideshow-{{ID}} .elementor-swiper-button:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'lightbox_video_width', + [ + 'label' => esc_html__( 'Video Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'range' => [ + '%' => [ + 'min' => 50, + ], + ], + 'selectors' => [ + '#elementor-lightbox-slideshow-{{ID}} .elementor-video-container' => 'width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->add_injections(); + + $this->update_controls(); + } + + protected function add_repeater_controls( Repeater $repeater ) { + $repeater->add_control( + 'type', + [ + 'type' => Controls_Manager::CHOOSE, + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'default' => 'image', + 'options' => [ + 'image' => [ + 'title' => esc_html__( 'Image', 'elementor-pro' ), + 'icon' => 'eicon-image-bold', + ], + 'video' => [ + 'title' => esc_html__( 'Video', 'elementor-pro' ), + 'icon' => 'eicon-video-camera', + ], + ], + 'toggle' => false, + ] + ); + + $repeater->add_control( + 'image', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'image_link_to_type', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'file' => esc_html__( 'Media File', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom URL', 'elementor-pro' ), + ], + 'condition' => [ + 'type' => 'image', + ], + ] + ); + + $repeater->add_control( + 'image_link_to', + [ + 'type' => Controls_Manager::URL, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'show_external' => 'true', + 'condition' => [ + 'type' => 'image', + 'image_link_to_type' => 'custom', + ], + 'show_label' => false, + ] + ); + + $repeater->add_control( + 'video', + [ + 'label' => esc_html__( 'Video Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'Enter your video link', 'elementor-pro' ), + 'description' => esc_html__( 'YouTube or Vimeo link', 'elementor-pro' ), + 'options' => false, + 'condition' => [ + 'type' => 'video', + ], + ] + ); + } + + protected function get_default_slides_count() { + return 5; + } + + protected function get_repeater_defaults() { + $placeholder_image_src = Utils::get_placeholder_image_src(); + + return array_fill( 0, $this->get_default_slides_count(), [ + 'image' => [ + 'url' => $placeholder_image_src, + ], + ] ); + } + + protected function get_image_caption( $slide ) { + $caption_type = $this->get_settings( 'caption' ); + + if ( empty( $caption_type ) ) { + return ''; + } + + $attachment_post = get_post( $slide['image']['id'] ); + + if ( 'caption' === $caption_type ) { + return $attachment_post->post_excerpt; + } + + if ( 'title' === $caption_type ) { + return $attachment_post->post_title; + } + + return $attachment_post->post_content; + } + + protected function get_image_link_to( $slide ) { + if ( ! empty( $slide['video']['url'] ) ) { + return $slide['image']['url'] ? $slide['image']['url'] : '#'; + } + + if ( ! $slide['image_link_to_type'] ) { + return ''; + } + + if ( 'custom' === $slide['image_link_to_type'] ) { + return $slide['image_link_to']['url']; + } + + return $slide['image']['url']; + } + + protected function print_slider( array $settings = null ) { + $this->lightbox_slide_index = 0; + + parent::print_slider( $settings ); + } + + protected function print_slide( array $slide, array $settings, $element_key ) { + if ( ! empty( $settings['thumbs_slider'] ) ) { + $settings['video_play_icon'] = false; + } + + $this->add_render_attribute( $element_key . '-image', [ + 'class' => 'elementor-carousel-image', + 'role' => 'img', + 'aria-label' => Control_Media::get_image_alt( $slide['image'] ), + ] ); + + $img_src = $this->get_slide_image_url( $slide, $settings ); + + if ( 'yes' === $settings['lazyload'] ) { + $img_attribute['class'] = 'swiper-lazy'; + $img_attribute['data-background'] = $img_src; + } else { + $img_attribute['style'] = "background-image: url('" . $img_src . "')"; + } + + $this->add_render_attribute( $element_key . '-image', $img_attribute ); + + $image_link_to = $this->get_image_link_to( $slide ); + + if ( $image_link_to && empty( $settings['thumbs_slider'] ) ) { + if ( 'custom' === $slide['image_link_to_type'] ) { + $this->add_link_attributes( $element_key . '_link', $slide['image_link_to'] ); + } else { + $this->add_render_attribute( $element_key . '_link', 'href', esc_url( $image_link_to ) ); + + $this->add_lightbox_data_attributes( $element_key . '_link', $slide['image']['id'], 'yes', $this->get_id() ); + + if ( Plugin::elementor()->editor->is_edit_mode() ) { + $this->add_render_attribute( $element_key . '_link', 'class', 'elementor-clickable' ); + } + + $this->lightbox_slide_index++; + } + + if ( 'video' === $slide['type'] && $slide['video']['url'] ) { + $embed_url_params = [ + 'autoplay' => 1, + 'rel' => 0, + 'controls' => 0, + ]; + + $this->add_render_attribute( $element_key . '_link', 'data-elementor-lightbox-video', Embed::get_embed_url( $slide['video']['url'], $embed_url_params ) ); + } + + // PHPCS - the method get_render_attribute_string is safe. + echo 'get_render_attribute_string( $element_key . '_link' ) . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + $this->print_slide_image( $slide, $element_key, $settings ); + + if ( $image_link_to ) { + echo ''; + } + } + + protected function print_slide_image( array $slide, $element_key, array $settings ) { + ?> +
    print_render_attribute_string( $element_key . '-image' ); ?>> + + +
    + + + +
    + 'eicons', + 'value' => 'eicon-play', + ], [ 'aria-hidden' => 'true' ] ); + ?> + +
    + +
    + +
    print_render_attribute_string( 'image-overlay' ); ?>> + get_image_caption( $slide ) ); + } else { + $this->render_overlay_icon( $settings['icon'] ); + } + ?> +
    + start_injection( [ + 'type' => 'section', + 'at' => 'start', + 'of' => 'section_slides', + ] ); + + $this->add_control( + 'skin', + [ + 'label' => esc_html__( 'Skin', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'carousel', + 'options' => [ + 'carousel' => esc_html__( 'Carousel', 'elementor-pro' ), + 'slideshow' => esc_html__( 'Slideshow', 'elementor-pro' ), + 'coverflow' => esc_html__( 'Coverflow', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-skin-', + 'render_type' => 'template', + 'frontend_available' => true, + ] + ); + + $this->end_injection(); + + $this->start_injection( [ + 'of' => 'image_size_custom_dimension', + ] ); + + $this->add_control( + 'image_fit', + [ + 'label' => esc_html__( 'Image Fit', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'Cover', 'elementor-pro' ), + 'contain' => esc_html__( 'Contain', 'elementor-pro' ), + 'auto' => esc_html__( 'Auto', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-main-swiper .elementor-carousel-image' => 'background-size: {{VALUE}}', + ], + ] + ); + + $this->end_injection(); + + $this->start_injection( [ + 'of' => 'pagination_color', + ] ); + + $this->add_control( + 'play_icon_title', + [ + 'label' => esc_html__( 'Play Icon', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'play_icon_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-custom-embed-play i' => 'color: {{VALUE}}', + '{{WRAPPER}} .elementor-custom-embed-play svg' => 'fill: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'play_icon_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 20, + 'max' => 150, + ], + 'em' => [ + 'min' => 2, + 'max' => 15, + ], + 'rem' => [ + 'min' => 2, + 'max' => 15, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-custom-embed-play i' => 'font-size: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'play_icon_text_shadow', + 'selector' => '{{WRAPPER}} .elementor-custom-embed-play i', + 'fields_options' => [ + 'text_shadow_type' => [ + 'label' => _x( 'Shadow', 'Text Shadow Control', 'elementor-pro' ), + ], + ], + ] + ); + + $this->end_injection(); + + $this->start_injection( [ + 'of' => 'pause_on_interaction', + ] ); + + $this->add_control( + 'overlay', + [ + 'label' => esc_html__( 'Overlay', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'text' => esc_html__( 'Text', 'elementor-pro' ), + 'icon' => esc_html__( 'Icon', 'elementor-pro' ), + ], + 'condition' => [ + 'skin!' => 'slideshow', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'caption', + [ + 'label' => esc_html__( 'Caption', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'title', + 'options' => [ + 'title' => esc_html__( 'Title', 'elementor-pro' ), + 'caption' => esc_html__( 'Caption', 'elementor-pro' ), + 'description' => esc_html__( 'Description', 'elementor-pro' ), + ], + 'condition' => [ + 'skin!' => 'slideshow', + 'overlay' => 'text', + ], + ] + ); + + $this->add_control( + 'icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'search-plus', + 'options' => [ + 'search-plus' => [ + 'icon' => 'eicon-search-bold', + ], + 'plus-circle' => [ + 'icon' => 'eicon-plus-circle', + ], + 'eye' => [ + 'icon' => 'eicon-preview-medium', + ], + 'link' => [ + 'icon' => 'eicon-link', + ], + ], + 'condition' => [ + 'skin!' => 'slideshow', + 'overlay' => 'icon', + ], + ] + ); + + $this->add_control( + 'overlay_animation', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'fade', + 'options' => [ + 'fade' => 'Fade', + 'slide-up' => 'Slide Up', + 'slide-down' => 'Slide Down', + 'slide-right' => 'Slide Right', + 'slide-left' => 'Slide Left', + 'zoom-in' => 'Zoom In', + ], + 'condition' => [ + 'skin!' => 'slideshow', + 'overlay!' => '', + ], + ] + ); + + $this->end_injection(); + + $this->start_injection( [ + 'type' => 'section', + 'of' => 'section_navigation', + ] ); + + $this->start_controls_section( + 'section_overlay', + [ + 'label' => esc_html__( 'Overlay', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'skin!' => 'slideshow', + 'overlay!' => '', + ], + ] + ); + + $this->add_control( + 'overlay_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-carousel-image-overlay' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'overlay_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-carousel-image-overlay' => '--e-carousel-image-overlay-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'caption_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}} .elementor-carousel-image-overlay', + 'condition' => [ + 'overlay' => 'text', + ], + ] + ); + + $this->add_control( + 'icon_size', + [ + 'label' => esc_html__( 'Icon Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-carousel-image-overlay' => '--e-carousel-image-overlay-icon-size: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'overlay' => 'icon', + ], + ] + ); + + $this->end_controls_section(); + + $this->end_injection(); + + // Slideshow + + $this->start_injection( [ + 'of' => 'effect', + ] ); + + $this->add_responsive_control( + 'slideshow_height', + [ + 'type' => Controls_Manager::SLIDER, + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 20, + 'max' => 1000, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-main-swiper' => 'height: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'skin' => 'slideshow', + ], + ] + ); + + $this->add_control( + 'thumbs_title', + [ + 'label' => esc_html__( 'Thumbnails', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'condition' => [ + 'skin' => 'slideshow', + ], + ] + ); + + $this->end_injection(); + + $this->start_injection( [ + 'of' => 'slides_per_view', + ] ); + + $this->add_control( + 'thumbs_ratio', + [ + 'label' => esc_html__( 'Ratio', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '169' => '16:9', + '219' => '21:9', + '43' => '4:3', + '11' => '1:1', + ], + 'selectors_dictionary' => [ + '169' => '16 / 9', + '219' => '21 / 9', + '43' => '4 / 3', + '11' => '1 / 1', + ], + 'default' => '219', + 'condition' => [ + 'skin' => 'slideshow', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-thumbnails-swiper .elementor-carousel-image' => 'aspect-ratio: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'centered_slides', + [ + 'label' => esc_html__( 'Centered Slides', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'skin' => 'slideshow', + ], + 'frontend_available' => true, + ] + ); + + $this->end_injection(); + + $this->start_injection( [ + 'of' => 'slides_per_view', + ] ); + + $slides_per_view = range( 1, 10 ); + + $slides_per_view = array_combine( $slides_per_view, $slides_per_view ); + + $this->add_responsive_control( + 'slideshow_slides_per_view', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Slides Per View', 'elementor-pro' ), + 'options' => [ '' => esc_html__( 'Default', 'elementor-pro' ) ] + $slides_per_view, + 'condition' => [ + 'skin' => 'slideshow', + ], + 'frontend_available' => true, + ] + ); + + $this->end_injection(); + } + + private function update_controls() { + $carousel_controls = [ + 'slides_to_scroll', + 'pagination', + 'heading_pagination', + 'pagination_size', + 'pagination_position', + 'pagination_color', + ]; + + $carousel_responsive_controls = [ + 'width', + 'height', + 'slides_per_view', + ]; + + foreach ( $carousel_controls as $control_id ) { + $this->update_control( + $control_id, + [ + 'condition' => [ + 'skin!' => 'slideshow', + ], + ], + [ 'recursive' => true ] + ); + } + + foreach ( $carousel_responsive_controls as $control_id ) { + $this->update_responsive_control( + $control_id, + [ + 'condition' => [ + 'skin!' => 'slideshow', + ], + ], + [ 'recursive' => true ] + ); + } + + $this->update_responsive_control( + 'space_between', + [ + 'selectors' => [ + '{{WRAPPER}}.elementor-skin-slideshow .elementor-main-swiper' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + 'render_type' => 'ui', + ] + ); + + $this->update_control( + 'effect', + [ + 'condition' => [ + 'skin!' => 'coverflow', + ], + ] + ); + } + + public function get_group_name() { + return 'carousel'; + } + + private function render_overlay_icon( $icon_name ) { + $icon_value = 'fas fa-' . $icon_name; + + $icon = [ + 'library' => 'fa-solid', + 'value' => $icon_value, + ]; + + Icons_Manager::render_icon( $icon ); + } +} diff --git a/modules/carousel/widgets/reviews.php b/modules/carousel/widgets/reviews.php new file mode 100644 index 0000000..24d9b7c --- /dev/null +++ b/modules/carousel/widgets/reviews.php @@ -0,0 +1,908 @@ +get_settings_for_display( 'slides' ); + + foreach ( $slides as $slide ) { + if ( $slide['rating'] ) { + return [ + [ + 'name' => 'star-rating', + 'is_core_dependency' => true, + ], + ]; + } + } + + return []; + } + + protected function register_controls() { + parent::register_controls(); + + $this->update_control( + 'slide_padding', + [ + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__header' => 'padding-top: {{TOP}}{{UNIT}}; padding-left: {{LEFT}}{{UNIT}}; padding-right: {{RIGHT}}{{UNIT}};', + '{{WRAPPER}} .elementor-testimonial__content' => 'padding-bottom: {{BOTTOM}}{{UNIT}}; padding-left: {{LEFT}}{{UNIT}}; padding-right: {{RIGHT}}{{UNIT}};', + ], + ] + ); + + $this->start_injection( [ + 'of' => 'slide_padding', + ] ); + + $this->add_control( + 'heading_header', + [ + 'label' => esc_html__( 'Header', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'header_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__header' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'content_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__header' => 'padding-block-end: calc( {{SIZE}}{{UNIT}} / 2 )', + '{{WRAPPER}} .elementor-testimonial__content' => 'padding-block-start: calc( {{SIZE}}{{UNIT}} / 2 )', + ], + ] + ); + + $this->add_control( + 'show_separator', + [ + 'label' => esc_html__( 'Separator', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'default' => 'has-separator', + 'return_value' => 'has-separator', + 'prefix_class' => 'elementor-review--', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'separator_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__header' => 'border-block-end-color: {{VALUE}}', + ], + 'condition' => [ + 'show_separator!' => '', + ], + ] + ); + + $this->add_control( + 'separator_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'condition' => [ + 'show_separator!' => '', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__header' => 'border-block-end-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_injection(); + + $this->start_injection( [ + 'at' => 'before', + 'of' => 'section_navigation', + ] ); + + $this->start_controls_section( + 'section_content_style', + [ + 'label' => esc_html__( 'Text', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'name_title_style', + [ + 'label' => esc_html__( 'Name', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'name_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__name' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'name_typography', + 'selector' => '{{WRAPPER}} .elementor-testimonial__header, {{WRAPPER}} .elementor-testimonial__name', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + ] + ); + + $this->add_control( + 'heading_title_style', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__title' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'title_typography', + 'selector' => '{{WRAPPER}} .elementor-testimonial__title', + ] + ); + + $this->add_control( + 'heading_review_style', + [ + 'label' => esc_html__( 'Review', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'content_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__text' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'content_typography', + 'selector' => '{{WRAPPER}} .elementor-testimonial__text', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_image_style', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'image_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__image img' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'image_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__image + cite' => 'margin-inline-start: {{SIZE}}{{UNIT}}; margin-inline-end: 0;', + ], + ] + ); + + $this->add_control( + 'image_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__image img' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_icon_style', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'icon_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'default', + 'options' => [ + 'default' => esc_html__( 'Official', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'icon_custom_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'condition' => [ + 'icon_color' => 'custom', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__icon:not(.elementor-testimonial__rating)' => 'color: {{VALUE}};', + '{{WRAPPER}} .elementor-testimonial__icon:not(.elementor-testimonial__rating) svg' => 'fill: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'icon_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__icon' => 'font-size: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .elementor-testimonial__icon svg' => 'width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_rating_style', + [ + 'label' => esc_html__( 'Rating', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'star_style', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'star_fontawesome' => 'Font Awesome', + 'star_unicode' => 'Unicode', + ], + 'default' => 'star_fontawesome', + 'render_type' => 'template', + 'prefix_class' => 'elementor--star-style-', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'unmarked_star_style', + [ + 'label' => esc_html__( 'Unmarked Style', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'solid' => [ + 'title' => esc_html__( 'Solid', 'elementor-pro' ), + 'icon' => 'eicon-star', + ], + 'outline' => [ + 'title' => esc_html__( 'Outline', 'elementor-pro' ), + 'icon' => 'eicon-star-o', + ], + ], + 'default' => 'solid', + ] + ); + + $this->add_control( + 'star_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-star-rating' => 'font-size: {{SIZE}}{{UNIT}}', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'star_space', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-star-rating i:not(:last-of-type)' => 'margin-inline-end: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'stars_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-star-rating i:before' => 'color: {{VALUE}}', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'stars_unmarked_color', + [ + 'label' => esc_html__( 'Unmarked Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-star-rating i' => 'color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_section(); + + $this->end_injection(); + + $this->update_responsive_control( + 'width', + [ + 'selectors' => [ + '{{WRAPPER}}.elementor-arrows-yes .elementor-main-swiper' => 'width: calc( {{SIZE}}{{UNIT}} - 40px )', + '{{WRAPPER}} .elementor-main-swiper' => 'width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->update_responsive_control( + 'slides_per_view', + [ + 'condition' => null, + ] + ); + + $this->update_control( + 'slides_to_scroll', + [ + 'condition' => null, + ] + ); + + $this->remove_control( 'effect' ); + $this->remove_responsive_control( 'height' ); + $this->remove_control( 'pagination_position' ); + } + + protected function add_repeater_controls( Repeater $repeater ) { + $repeater->add_control( + 'image', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'default' => [ + 'url' => Utils::get_placeholder_image_src(), + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'name', + [ + 'label' => esc_html__( 'Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'John Doe', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $repeater->add_control( + 'title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '@username', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'rating', + [ + 'label' => esc_html__( 'Rating', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'min' => 0, + 'max' => 5, + 'step' => 0.1, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'selected_social_icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'social_icon', + 'default' => [ + 'value' => 'fab fa-twitter', + 'library' => 'fa-brands', + ], + 'recommended' => [ + 'fa-solid' => [ + 'rss', + 'shopping-cart', + 'thumbtack', + ], + 'fa-brands' => [ + 'android', + 'apple', + 'behance', + 'bitbucket', + 'codepen', + 'delicious', + 'digg', + 'dribbble', + 'envelope', + 'facebook', + 'flickr', + 'foursquare', + 'github', + 'google-plus', + 'houzz', + 'instagram', + 'jsfiddle', + 'linkedin', + 'medium', + 'meetup', + 'mix', + 'mixcloud', + 'odnoklassniki', + 'pinterest', + 'product-hunt', + 'reddit', + 'skype', + 'slideshare', + 'snapchat', + 'soundcloud', + 'spotify', + 'stack-overflow', + 'steam', + 'telegram', + 'threads', + 'tripadvisor', + 'tumblr', + 'twitch', + 'twitter', + 'vimeo', + 'fa-vk', + 'weibo', + 'weixin', + 'whatsapp', + 'wordpress', + 'x-twitter', + 'xing', + 'yelp', + 'youtube', + '500px', + ], + ], + ] + ); + + $repeater->add_control( + 'link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + + ] + ); + + $repeater->add_control( + 'content', + [ + 'label' => esc_html__( 'Review', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + } + + protected function get_repeater_defaults() { + $placeholder_image_src = Utils::get_placeholder_image_src(); + + return [ + [ + 'content' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor-pro' ), + 'name' => esc_html__( 'John Doe', 'elementor-pro' ), + 'title' => '@username', + 'image' => [ + 'url' => $placeholder_image_src, + ], + ], + [ + 'content' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor-pro' ), + 'name' => esc_html__( 'John Doe', 'elementor-pro' ), + 'title' => '@username', + 'image' => [ + 'url' => $placeholder_image_src, + ], + ], + [ + 'content' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor-pro' ), + 'name' => esc_html__( 'John Doe', 'elementor-pro' ), + 'title' => '@username', + 'image' => [ + 'url' => $placeholder_image_src, + ], + ], + ]; + } + + private function print_cite( $slide, $settings ) { + if ( empty( $slide['name'] ) && empty( $slide['title'] ) ) { + return ''; + } + + $html = ''; + + if ( ! empty( $slide['name'] ) ) { + $html .= '' . $slide['name'] . ''; + } + + if ( ! empty( $slide['rating'] ) ) { + $html .= $this->render_stars( $slide, $settings ); + } + + if ( ! empty( $slide['title'] ) ) { + $html .= '' . $slide['title'] . ''; + } + $html .= ''; + + echo wp_kses_post( $html ); + } + + protected function render_stars( $slide, $settings ) { + $icon = ''; + + if ( 'star_fontawesome' === $settings['star_style'] ) { + if ( 'outline' === $settings['unmarked_star_style'] ) { + $icon = ''; + } + } elseif ( 'star_unicode' === $settings['star_style'] ) { + $icon = '★'; + + if ( 'outline' === $settings['unmarked_star_style'] ) { + $icon = '☆'; + } + } + + $rating = (float) $slide['rating'] > 5 ? 5 : $slide['rating']; + $floored_rating = (int) $rating; + $stars_html = ''; + + for ( $stars = 1; $stars <= 5; $stars++ ) { + if ( $stars <= $floored_rating ) { + $stars_html .= '' . $icon . ''; + } elseif ( $floored_rating + 1 === $stars && $rating !== $floored_rating ) { + $stars_html .= '' . $icon . ''; + } else { + $stars_html .= '' . $icon . ''; + } + } + + return '
    ' . $stars_html . '
    '; + } + + private function print_icon( $slide, $element_key ) { + $migration_allowed = Icons_Manager::is_migration_allowed(); + if ( ! isset( $slide['social_icon'] ) && ! $migration_allowed ) { + // add old default + $slide['social_icon'] = 'fa fa-twitter'; + } + + if ( empty( $slide['social_icon'] ) && empty( $slide['selected_social_icon'] ) ) { + return ''; + } + + $migrated = isset( $slide['__fa4_migrated']['selected_social_icon'] ); + $is_new = empty( $slide['social_icon'] ) && $migration_allowed; + $social = ''; + + if ( $is_new || $migrated ) { + ob_start(); + Icons_Manager::render_icon( $slide['selected_social_icon'], [ 'aria-hidden' => 'true' ] ); + $icon = ob_get_clean(); + } else { + $icon = ''; + } + + if ( ! empty( $slide['social_icon'] ) ) { + $social = str_replace( 'fa fa-', '', $slide['social_icon'] ); + } + + if ( ( $is_new || $migrated ) && 'svg' !== $slide['selected_social_icon']['library'] ) { + $social = explode( ' ', $slide['selected_social_icon']['value'], 2 ); + if ( empty( $social[1] ) ) { + $social = ''; + } else { + $social = str_replace( 'fa-', '', $social[1] ); + } + } + if ( 'svg' === $slide['selected_social_icon']['library'] ) { + $social = ''; + } + + $this->add_render_attribute( 'icon_wrapper_' . $element_key, 'class', 'elementor-testimonial__icon elementor-icon' ); + + $icon .= '' . esc_html__( 'Read More', 'elementor-pro' ) . ''; + $this->add_render_attribute( 'icon_wrapper_' . $element_key, 'class', 'elementor-icon-' . $social ); + + // Icon is escaped above, get_render_attribute_string() is safe + echo '
    get_render_attribute_string( 'icon_wrapper_' . $element_key ) . '>' . $icon . '
    '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + protected function print_slide( array $slide, array $settings, $element_key ) { + $lazyload = 'yes' === $this->get_settings( 'lazyload' ); + + $this->add_render_attribute( $element_key . '-testimonial', [ + 'class' => 'elementor-testimonial', + ] ); + + $this->add_render_attribute( $element_key . '-testimonial', [ + 'class' => 'elementor-repeater-item-' . $slide['_id'], + ] ); + + if ( ! empty( $slide['image']['url'] ) ) { + $img_src = $this->get_slide_image_url( $slide, $settings ); + + if ( $lazyload ) { + $img_attribute['class'] = 'swiper-lazy'; + $img_attribute['data-src'] = $img_src; + } else { + $img_attribute['src'] = $img_src; + } + + $img_attribute['alt'] = $this->get_slide_image_alt_attribute( $slide ); + + $this->add_render_attribute( $element_key . '-image', $img_attribute ); + } + + ?> +
    print_render_attribute_string( $element_key . '-testimonial' ); ?>> + add_render_attribute( $header_element, 'class', 'elementor-testimonial__header' ); + + if ( ! empty( $link_url ) ) { + $this->add_link_attributes( $header_element, $slide['link'] ); + } + ?> + < print_render_attribute_string( $header_element ); ?>> + +
    + print_render_attribute_string( $element_key . '-image' ); ?>> + +
    + +
    + + print_cite( $slide, $settings ); ?> + print_icon( $slide, $element_key ); ?> + > + + +
    +
    + +
    +
    + +
    + print_slider(); + } + + public function get_group_name() { + return 'carousel'; + } +} diff --git a/modules/carousel/widgets/testimonial-carousel.php b/modules/carousel/widgets/testimonial-carousel.php new file mode 100644 index 0000000..684858a --- /dev/null +++ b/modules/carousel/widgets/testimonial-carousel.php @@ -0,0 +1,710 @@ +start_injection( [ + 'of' => 'slides', + ] ); + + $this->add_control( + 'skin', + [ + 'label' => esc_html__( 'Skin', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'default', + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'bubble' => esc_html__( 'Bubble', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-testimonial--skin-', + 'render_type' => 'template', + ] + ); + + $this->add_control( + 'layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'image_inline', + 'options' => [ + 'image_inline' => esc_html__( 'Image Inline', 'elementor-pro' ), + 'image_stacked' => esc_html__( 'Image Stacked', 'elementor-pro' ), + 'image_above' => esc_html__( 'Image Above', 'elementor-pro' ), + 'image_left' => esc_html__( 'Image Left', 'elementor-pro' ), + 'image_right' => esc_html__( 'Image Right', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-testimonial--layout-', + 'render_type' => 'template', + ] + ); + + $this->add_responsive_control( + 'alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'center', + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'prefix_class' => 'elementor-testimonial-%s-align-', + ] + ); + + $this->end_injection(); + + $this->start_controls_section( + 'section_skin_style', + [ + 'label' => esc_html__( 'Bubble', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'skin' => 'bubble', + ], + ] + ); + + $this->add_control( + 'background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'alpha' => false, + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__content, {{WRAPPER}} .elementor-testimonial__content:after' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'text_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'top' => '20', + 'bottom' => '20', + 'left' => '20', + 'right' => '20', + 'unit' => 'px', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__content' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + '{{WRAPPER}}.elementor-testimonial--layout-image_left .elementor-testimonial__footer, + {{WRAPPER}}.elementor-testimonial--layout-image_right .elementor-testimonial__footer' => 'padding-top: {{TOP}}{{UNIT}}', + '{{WRAPPER}}.elementor-testimonial--layout-image_above .elementor-testimonial__footer, + {{WRAPPER}}.elementor-testimonial--layout-image_inline .elementor-testimonial__footer, + {{WRAPPER}}.elementor-testimonial--layout-image_stacked .elementor-testimonial__footer' => 'padding: 0 {{RIGHT}}{{UNIT}} 0 {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__content' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'border', + [ + 'label' => esc_html__( 'Border', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__content, {{WRAPPER}} .elementor-testimonial__content:after' => 'border-style: solid', + ], + ] + ); + + $this->add_control( + 'border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#000', + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__content' => 'border-color: {{VALUE}}', + '{{WRAPPER}} .elementor-testimonial__content:after' => 'border-color: transparent {{VALUE}} {{VALUE}} transparent', + ], + 'condition' => [ + 'border' => 'yes', + ], + ] + ); + + $this->add_responsive_control( + 'border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__content, {{WRAPPER}} .elementor-testimonial__content:after' => 'border-width: {{SIZE}}{{UNIT}}', + '{{WRAPPER}}.elementor-testimonial--layout-image_stacked .elementor-testimonial__content:after, + {{WRAPPER}}.elementor-testimonial--layout-image_inline .elementor-testimonial__content:after' => 'margin-top: -{{SIZE}}{{UNIT}}', + '{{WRAPPER}}.elementor-testimonial--layout-image_above .elementor-testimonial__content:after' => 'margin-bottom: -{{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'border' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_injection( [ + 'at' => 'before', + 'of' => 'section_navigation', + ] ); + + $this->start_controls_section( + 'section_content_style', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'content_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-testimonial--layout-image_inline .elementor-testimonial__footer, + {{WRAPPER}}.elementor-testimonial--layout-image_stacked .elementor-testimonial__footer' => 'margin-top: {{SIZE}}{{UNIT}}', + '{{WRAPPER}}.elementor-testimonial--layout-image_above .elementor-testimonial__footer' => 'margin-bottom: {{SIZE}}{{UNIT}}', + '{{WRAPPER}}.elementor-testimonial--layout-image_left .elementor-testimonial__footer' => 'padding-right: {{SIZE}}{{UNIT}}', + '{{WRAPPER}}.elementor-testimonial--layout-image_right .elementor-testimonial__footer' => 'padding-left: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'content_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__text' => 'color: {{VALUE}}', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'content_typography', + 'selector' => '{{WRAPPER}} .elementor-testimonial__text', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Stroke::get_type(), + [ + 'name' => 'text_stroke', + 'selector' => '{{WRAPPER}} .elementor-testimonial__text', + ] + ); + + $this->add_control( + 'name_title_style', + [ + 'label' => esc_html__( 'Name', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'name_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__name' => 'color: {{VALUE}}', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'name_typography', + 'selector' => '{{WRAPPER}} .elementor-testimonial__name', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + ] + ); + + $this->add_control( + 'heading_title_style', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'title_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__title' => 'color: {{VALUE}}', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'title_typography', + 'selector' => '{{WRAPPER}} .elementor-testimonial__title', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_image_style', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'image_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__image img' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}}', + '{{WRAPPER}}.elementor-testimonial--layout-image_left .elementor-testimonial__content:after, + {{WRAPPER}}.elementor-testimonial--layout-image_right .elementor-testimonial__content:after' => 'top: calc( {{text_padding.TOP}}{{text_padding.UNIT}} + ({{SIZE}}{{UNIT}} / 2) - 8px );', + + 'body:not(.rtl) {{WRAPPER}}.elementor-testimonial--layout-image_stacked:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-right) .elementor-testimonial__content:after, + body:not(.rtl) {{WRAPPER}}.elementor-testimonial--layout-image_inline:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-right) .elementor-testimonial__content:after, + {{WRAPPER}}.elementor-testimonial--layout-image_stacked.elementor-testimonial--align-left .elementor-testimonial__content:after, + {{WRAPPER}}.elementor-testimonial--layout-image_inline.elementor-testimonial--align-left .elementor-testimonial__content:after' => 'left: calc( {{text_padding.LEFT}}{{text_padding.UNIT}} + ({{SIZE}}{{UNIT}} / 2) - 8px ); right:auto;', + + 'body.rtl {{WRAPPER}}.elementor-testimonial--layout-image_stacked:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-left) .elementor-testimonial__content:after, + body.rtl {{WRAPPER}}.elementor-testimonial--layout-image_inline:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-left) .elementor-testimonial__content:after, + {{WRAPPER}}.elementor-testimonial--layout-image_stacked.elementor-testimonial--align-right .elementor-testimonial__content:after, + {{WRAPPER}}.elementor-testimonial--layout-image_inline.elementor-testimonial--align-right .elementor-testimonial__content:after' => 'right: calc( {{text_padding.RIGHT}}{{text_padding.UNIT}} + ({{SIZE}}{{UNIT}} / 2) - 8px ); left:auto;', + + 'body:not(.rtl) {{WRAPPER}}.elementor-testimonial--layout-image_above:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-right) .elementor-testimonial__content:after, + {{WRAPPER}}.elementor-testimonial--layout-image_above.elementor-testimonial--align-left .elementor-testimonial__content:after' => 'left: calc( {{text_padding.LEFT}}{{text_padding.UNIT}} + ({{SIZE}}{{UNIT}} / 2) - 8px ); right:auto;', + + 'body.rtl {{WRAPPER}}.elementor-testimonial--layout-image_above:not(.elementor-testimonial--align-center):not(.elementor-testimonial--align-left) .elementor-testimonial__content:after, + {{WRAPPER}}.elementor-testimonial--layout-image_above.elementor-testimonial--align-right .elementor-testimonial__content:after' => 'right: calc( {{text_padding.RIGHT}}{{text_padding.UNIT}} + ({{SIZE}}{{UNIT}} / 2) - 8px ); left:auto;', + ], + ] + ); + + $this->add_responsive_control( + 'image_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + 'body.rtl {{WRAPPER}}.elementor-testimonial--layout-image_inline.elementor-testimonial--align-left .elementor-testimonial__image + cite, + body.rtl {{WRAPPER}}.elementor-testimonial--layout-image_above.elementor-testimonial--align-left .elementor-testimonial__image + cite, + body:not(.rtl) {{WRAPPER}}.elementor-testimonial--layout-image_inline .elementor-testimonial__image + cite, + body:not(.rtl) {{WRAPPER}}.elementor-testimonial--layout-image_above .elementor-testimonial__image + cite' => 'margin-left: {{SIZE}}{{UNIT}}; margin-right: 0;', + + 'body:not(.rtl) {{WRAPPER}}.elementor-testimonial--layout-image_inline.elementor-testimonial--align-right .elementor-testimonial__image + cite, + body:not(.rtl) {{WRAPPER}}.elementor-testimonial--layout-image_above.elementor-testimonial--align-right .elementor-testimonial__image + cite, + body.rtl {{WRAPPER}}.elementor-testimonial--layout-image_inline .elementor-testimonial__image + cite, + body.rtl {{WRAPPER}}.elementor-testimonial--layout-image_above .elementor-testimonial__image + cite' => 'margin-right: {{SIZE}}{{UNIT}}; margin-left:0;', + + '{{WRAPPER}}.elementor-testimonial--layout-image_stacked .elementor-testimonial__image + cite, + {{WRAPPER}}.elementor-testimonial--layout-image_left .elementor-testimonial__image + cite, + {{WRAPPER}}.elementor-testimonial--layout-image_right .elementor-testimonial__image + cite' => 'margin-top: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'image_border', + [ + 'label' => esc_html__( 'Border', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__image img' => 'border-style: solid', + ], + ] + ); + + $this->add_control( + 'image_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#000', + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__image img' => 'border-color: {{VALUE}}', + ], + 'condition' => [ + 'image_border' => 'yes', + ], + ] + ); + + $this->add_responsive_control( + 'image_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__image img' => 'border-width: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'image_border' => 'yes', + ], + ] + ); + + $this->add_control( + 'image_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-testimonial__image img' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + + $this->end_injection(); + + $this->update_responsive_control( + 'width', + [ + 'selectors' => [ + '{{WRAPPER}}.elementor-arrows-yes .elementor-main-swiper' => 'width: calc( {{SIZE}}{{UNIT}} - 40px )', + '{{WRAPPER}} .elementor-main-swiper' => 'width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->update_responsive_control( + 'slides_per_view', + [ + 'condition' => null, + ] + ); + + $this->update_responsive_control( + 'slides_to_scroll', + [ + 'condition' => null, + ] + ); + + $this->remove_control( 'effect' ); + $this->remove_responsive_control( 'height' ); + $this->remove_control( 'pagination_position' ); + } + + protected function add_repeater_controls( Repeater $repeater ) { + $repeater->add_control( + 'content', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'image', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'name', + [ + 'label' => esc_html__( 'Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'John Doe', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $repeater->add_control( + 'title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'CEO', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + } + + protected function get_repeater_defaults() { + $placeholder_image_src = Utils::get_placeholder_image_src(); + + return [ + [ + 'content' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor-pro' ), + 'name' => esc_html__( 'John Doe', 'elementor-pro' ), + 'title' => esc_html__( 'CEO', 'elementor-pro' ), + 'image' => [ + 'url' => $placeholder_image_src, + ], + ], + [ + 'content' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor-pro' ), + 'name' => esc_html__( 'John Doe', 'elementor-pro' ), + 'title' => esc_html__( 'CEO', 'elementor-pro' ), + 'image' => [ + 'url' => $placeholder_image_src, + ], + ], + [ + 'content' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor-pro' ), + 'name' => esc_html__( 'John Doe', 'elementor-pro' ), + 'title' => esc_html__( 'CEO', 'elementor-pro' ), + 'image' => [ + 'url' => $placeholder_image_src, + ], + ], + ]; + } + + private function print_cite( $slide, $location ) { + if ( empty( $slide['name'] ) && empty( $slide['title'] ) ) { + return ''; + } + + $skin = $this->get_settings( 'skin' ); + $layout = 'bubble' === $skin ? 'image_inline' : $this->get_settings( 'layout' ); + $locations_outside = [ 'image_above', 'image_right', 'image_left' ]; + $locations_inside = [ 'image_inline', 'image_stacked' ]; + + $print_outside = ( 'outside' === $location && in_array( $layout, $locations_outside ) ); + $print_inside = ( 'inside' === $location && in_array( $layout, $locations_inside ) ); + + $html = ''; + if ( $print_outside || $print_inside ) { + $html = ''; + if ( ! empty( $slide['name'] ) ) { + $html .= '' . $slide['name'] . ''; + } + if ( ! empty( $slide['title'] ) ) { + $html .= '' . $slide['title'] . ''; + } + $html .= ''; + } + + // PHPCS - the main text of a widget should not be escaped. + echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + protected function print_slide( array $slide, array $settings, $element_key ) { + $lazyload = 'yes' === $this->get_settings( 'lazyload' ); + + $this->add_render_attribute( $element_key . '-testimonial', [ + 'class' => 'elementor-testimonial', + ] ); + + if ( ! empty( $slide['image']['url'] ) ) { + $img_src = $this->get_slide_image_url( $slide, $settings ); + + if ( $lazyload ) { + $img_attribute['class'] = 'swiper-lazy'; + $img_attribute['data-src'] = $img_src; + } else { + $img_attribute['src'] = $img_src; + } + + $img_attribute['alt'] = ! empty( $slide['image']['alt'] ) ? $slide['image']['alt'] : $slide['name']; + + $this->add_render_attribute( $element_key . '-image', $img_attribute ); + } + + ?> +
    print_render_attribute_string( $element_key . '-testimonial' ); ?>> + +
    +
    + +
    + print_cite( $slide, 'outside' ); ?> +
    + + +
    + print_slider(); + } + + public function get_group_name() { + return 'carousel'; + } +} diff --git a/modules/code-highlight/module.php b/modules/code-highlight/module.php new file mode 100644 index 0000000..4987307 --- /dev/null +++ b/modules/code-highlight/module.php @@ -0,0 +1,40 @@ + true, + 'prismjs_loader' => true, + 'prismjs_normalize' => true, + 'highlight_handler' => true, + 'prismjs_line_numbers' => true, + 'prismjs_line_highlight' => true, + 'prismjs_copy_to_clipboard' => true, + ]; + + if ( ! Plugin::elementor()->preview->is_preview_mode() ) { + $settings = $this->get_settings_for_display(); + + if ( ! $settings['line_numbers'] ) { + unset( $depends['prismjs_line_numbers'] ); + } + + if ( ! $settings['highlight_lines'] ) { + unset( $depends['prismjs_line_highlight'] ); + } + + if ( ! $settings['copy_to_clipboard'] ) { + unset( $depends['prismjs_copy_to_clipboard'] ); + } + } + + return array_keys( $depends ); + } + + public function get_css_config() { + // This widget is loading its own CSS using get_style_depends. + return [ + 'key' => $this->get_group_name(), + 'version' => ELEMENTOR_PRO_VERSION, + 'file_path' => '', + 'data' => [], + ]; + } + + protected function register_controls() { + $this->start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'Code Highlight', 'elementor-pro' ), + ] + ); + + $language_option = [ + 'markup' => 'Markup', + 'html' => 'HTML', + 'css' => 'CSS', + 'sass' => 'Sass (Sass)', + 'scss' => 'Sass (Scss)', + 'less' => 'Less', + 'javascript' => 'JavaScript', + 'typescript' => 'TypeScript', + 'jsx' => 'React JSX', + 'tsx' => 'React TSX', + 'php' => 'PHP', + 'ruby' => 'Ruby', + 'json' => 'JSON + Web App Manifest', + 'http' => 'HTTP', + 'xml' => 'XML', + 'svg' => 'SVG', + 'rust' => 'Rust', + 'csharp' => 'C#', + 'dart' => 'Dart', + 'git' => 'Git', + 'java' => 'Java', + 'sql' => 'SQL', + 'go' => 'Go', + 'kotlin' => 'Kotlin + Kotlin Script', + 'julia' => 'Julia', + 'python' => 'Python', + 'swift' => 'Swift', + 'bash' => 'Bash + Shell', + 'scala' => 'Scala', + 'haskell' => 'Haskell', + 'perl' => 'Perl', + 'objectivec' => 'Objective-C', + 'visual-basic,' => 'Visual Basic + VBA', + 'r' => 'R', + 'c' => 'C', + 'cpp' => 'C++', + 'aspnet' => 'ASP.NET (C#)', + ]; + + /** + * Code highlight languages. + * + * Filters the available programming languages in the code highlight. + * + * By default supports a code list of programming languages. This hook + * allows developers to add or remove languages. + * + * @param array $language_option An array of languages. + */ + $language_option = apply_filters( 'elementor_pro/code_highlight/languages', $language_option ); + + $this->add_control( + 'language', + [ + 'label' => esc_html__( 'Language', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => false, + 'options' => $language_option, + 'default' => 'javascript', + ] + ); + + $this->add_control( + 'code', + [ + 'label' => esc_html__( 'Code', 'elementor-pro' ), + 'type' => Controls_Manager::CODE, + 'default' => 'console.log( \'Code is Poetry\' );', + 'dynamic' => [ + 'active' => true, + 'categories' => [ + TagsModule::TEXT_CATEGORY, + ], + ], + ] + ); + + $this->add_control( + 'line_numbers', + [ + 'label' => esc_html__( 'Line Numbers', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'return_value' => 'line-numbers', + 'default' => 'line-numbers', + ] + ); + + $this->add_control( + 'copy_to_clipboard', + [ + 'label' => esc_html__( 'Copy to Clipboard', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'return_value' => 'copy-to-clipboard', + 'default' => 'copy-to-clipboard', + ] + ); + + $this->add_control( + 'highlight_lines', + [ + 'label' => esc_html__( 'Highlight Lines', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'placeholder' => '1, 3-6', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'word_wrap', + [ + 'label' => esc_html__( 'Word Wrap', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'return_value' => 'word-wrap', + 'default' => '', + ] + ); + + $this->add_control( + 'theme', + [ + 'label' => esc_html__( 'Theme', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'default', + 'options' => [ + 'default' => 'Solid', + 'dark' => 'Dark', + 'okaidia' => 'Okaidia', + 'solarizedlight' => 'Solarizedlight', + 'tomorrow' => 'Tomorrow', + 'twilight' => 'Twilight', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 115, + 'max' => 1000, + ], + 'em' => [ + 'min' => 6, + 'max' => 50, + ], + 'rem' => [ + 'min' => 6, + 'max' => 50, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .highlight-height' => 'height: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'font_size', + [ + 'label' => esc_html__( 'Font Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + 'vw' => [ + 'min' => 0.1, + 'max' => 10, + 'step' => 0.1, + ], + ], + 'responsive' => true, + 'selectors' => [ + '{{WRAPPER}} pre, {{WRAPPER}} code, {{WRAPPER}} .line-numbers .line-numbers-rows' => 'font-size: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + if ( empty( $settings['code'] ) ) { + return; + } + ?> +
    +
    +				
    +					<?php $this->print_unescaped_setting( 'code' ); ?>
    +				
    +			
    +
    + + <# + if ( '' === settings.code ) { + return; + } + #> +
    +
    +				
    +					{{{ settings.code }}}
    +				
    +			
    +
    + add_component( 'compatibility-tag-pro-handler', new Compatibility_Tag_Component() ); + } +} diff --git a/modules/countdown/module.php b/modules/countdown/module.php new file mode 100644 index 0000000..3c7527e --- /dev/null +++ b/modules/countdown/module.php @@ -0,0 +1,21 @@ +start_controls_section( + 'section_countdown', + [ + 'label' => esc_html__( 'Countdown', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'countdown_type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'due_date' => esc_html__( 'Due Date', 'elementor-pro' ), + 'evergreen' => esc_html__( 'Evergreen Timer', 'elementor-pro' ), + ], + 'default' => 'due_date', + ] + ); + + $this->add_control( + 'due_date', + [ + 'label' => esc_html__( 'Due Date', 'elementor-pro' ), + 'type' => Controls_Manager::DATE_TIME, + 'default' => gmdate( 'Y-m-d H:i', strtotime( '+1 month' ) + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) ), + /* translators: %s: Time zone. */ + 'description' => sprintf( esc_html__( 'Date set according to your timezone: %s.', 'elementor-pro' ), Utils::get_timezone_string() ), + 'condition' => [ + 'countdown_type' => 'due_date', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'evergreen_counter_hours', + [ + 'label' => esc_html__( 'Hours', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 47, + 'placeholder' => esc_html__( 'Hours', 'elementor-pro' ), + 'condition' => [ + 'countdown_type' => 'evergreen', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'evergreen_counter_minutes', + [ + 'label' => esc_html__( 'Minutes', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 59, + 'placeholder' => esc_html__( 'Minutes', 'elementor-pro' ), + 'condition' => [ + 'countdown_type' => 'evergreen', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'label_display', + [ + 'label' => esc_html__( 'View', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'block' => esc_html__( 'Block', 'elementor-pro' ), + 'inline' => esc_html__( 'Inline', 'elementor-pro' ), + ], + 'default' => 'block', + 'prefix_class' => 'elementor-countdown--label-', + ] + ); + + $this->add_control( + 'show_days', + [ + 'label' => esc_html__( 'Days', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'show_hours', + [ + 'label' => esc_html__( 'Hours', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'show_minutes', + [ + 'label' => esc_html__( 'Minutes', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'show_seconds', + [ + 'label' => esc_html__( 'Seconds', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'show_labels', + [ + 'label' => esc_html__( 'Show Label', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'custom_labels', + [ + 'label' => esc_html__( 'Custom Label', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'show_labels!' => '', + ], + ] + ); + + $this->add_control( + 'label_days', + [ + 'label' => esc_html__( 'Days', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Days', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Days', 'elementor-pro' ), + 'condition' => [ + 'show_labels!' => '', + 'custom_labels!' => '', + 'show_days' => 'yes', + ], + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'label_hours', + [ + 'label' => esc_html__( 'Hours', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Hours', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Hours', 'elementor-pro' ), + 'condition' => [ + 'show_labels!' => '', + 'custom_labels!' => '', + 'show_hours' => 'yes', + ], + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'label_minutes', + [ + 'label' => esc_html__( 'Minutes', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Minutes', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Minutes', 'elementor-pro' ), + 'condition' => [ + 'show_labels!' => '', + 'custom_labels!' => '', + 'show_minutes' => 'yes', + ], + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'label_seconds', + [ + 'label' => esc_html__( 'Seconds', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Seconds', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Seconds', 'elementor-pro' ), + 'condition' => [ + 'show_labels!' => '', + 'custom_labels!' => '', + 'show_seconds' => 'yes', + ], + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'expire_actions', + [ + 'label' => esc_html__( 'Actions After Expire', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'options' => [ + 'redirect' => esc_html__( 'Redirect', 'elementor-pro' ), + 'hide' => esc_html__( 'Hide', 'elementor-pro' ), + 'message' => esc_html__( 'Show Message', 'elementor-pro' ), + ], + 'label_block' => true, + 'separator' => 'before', + 'render_type' => 'none', + 'multiple' => true, + ] + ); + + $this->add_control( + 'message_after_expire', + [ + 'label' => esc_html__( 'Message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'separator' => 'before', + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'expire_actions' => 'message', + ], + ] + ); + + $this->add_control( + 'expire_redirect_url', + [ + 'label' => esc_html__( 'Redirect URL', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'separator' => 'before', + 'options' => false, + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'expire_actions' => 'redirect', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_box_style', + [ + 'label' => esc_html__( 'Boxes', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'container_width', + [ + 'label' => esc_html__( 'Container Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'unit' => '%', + 'size' => 100, + ], + 'tablet_default' => [ + 'unit' => '%', + ], + 'mobile_default' => [ + 'unit' => '%', + ], + 'range' => [ + 'px' => [ + 'max' => 2000, + ], + 'em' => [ + 'max' => 200, + ], + 'rem' => [ + 'max' => 200, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-countdown-wrapper' => 'max-width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'box_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-countdown-item' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'box_border', + 'selector' => '{{WRAPPER}} .elementor-countdown-item', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'box_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-countdown-item' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'box_spacing', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 10, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .elementor-countdown-item:not(:first-of-type)' => 'margin-left: calc( {{SIZE}}{{UNIT}}/2 );', + 'body:not(.rtl) {{WRAPPER}} .elementor-countdown-item:not(:last-of-type)' => 'margin-right: calc( {{SIZE}}{{UNIT}}/2 );', + 'body.rtl {{WRAPPER}} .elementor-countdown-item:not(:first-of-type)' => 'margin-right: calc( {{SIZE}}{{UNIT}}/2 );', + 'body.rtl {{WRAPPER}} .elementor-countdown-item:not(:last-of-type)' => 'margin-left: calc( {{SIZE}}{{UNIT}}/2 );', + ], + ] + ); + + $this->add_responsive_control( + 'box_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-countdown-item' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_content_style', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'heading_digits', + [ + 'label' => esc_html__( 'Digits', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'digits_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-countdown-digits' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'digits_typography', + 'selector' => '{{WRAPPER}} .elementor-countdown-digits', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_control( + 'heading_label', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'show_labels!' => '', + ], + ] + ); + + $this->add_control( + 'label_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-countdown-label' => 'color: {{VALUE}};', + ], + 'condition' => [ + 'show_labels!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'label_typography', + 'selector' => '{{WRAPPER}} .elementor-countdown-label', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + 'condition' => [ + 'show_labels!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Stroke::get_type(), + [ + 'name' => 'text_stroke', + 'selector' => '{{WRAPPER}} .elementor-countdown-label', + 'condition' => [ + 'show_labels!' => '', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_expire_message_style', + [ + 'label' => esc_html__( 'Message', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'expire_actions' => 'message', + ], + ] + ); + + $this->add_responsive_control( + 'align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-countdown-expire--message' => 'text-align: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-countdown-expire--message' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-countdown-expire--message', + ] + ); + + $this->add_responsive_control( + 'message_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-countdown-expire--message' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + } + + private function get_strftime( $instance ) { + $string = ''; + if ( $instance['show_days'] ) { + $string .= $this->render_countdown_item( $instance, 'label_days', 'elementor-countdown-days' ); + } + if ( $instance['show_hours'] ) { + $string .= $this->render_countdown_item( $instance, 'label_hours', 'elementor-countdown-hours' ); + } + if ( $instance['show_minutes'] ) { + $string .= $this->render_countdown_item( $instance, 'label_minutes', 'elementor-countdown-minutes' ); + } + if ( $instance['show_seconds'] ) { + $string .= $this->render_countdown_item( $instance, 'label_seconds', 'elementor-countdown-seconds' ); + } + + return $string; + } + + private $_default_countdown_labels; + + private function init_default_countdown_labels() { + $this->_default_countdown_labels = [ + 'label_months' => esc_html__( 'Months', 'elementor-pro' ), + 'label_weeks' => esc_html__( 'Weeks', 'elementor-pro' ), + 'label_days' => esc_html__( 'Days', 'elementor-pro' ), + 'label_hours' => esc_html__( 'Hours', 'elementor-pro' ), + 'label_minutes' => esc_html__( 'Minutes', 'elementor-pro' ), + 'label_seconds' => esc_html__( 'Seconds', 'elementor-pro' ), + ]; + } + + public function get_default_countdown_labels() { + if ( ! $this->_default_countdown_labels ) { + $this->init_default_countdown_labels(); + } + + return $this->_default_countdown_labels; + } + + private function render_countdown_item( $instance, $label, $part_class ) { + $string = '
    '; + + if ( $instance['show_labels'] ) { + $default_labels = $this->get_default_countdown_labels(); + $label = ( $instance['custom_labels'] ) ? $instance[ $label ] : $default_labels[ $label ]; + $string .= ' ' . $label . ''; + } + + $string .= '
    '; + + return $string; + } + + private function get_evergreen_interval( $instance ) { + $hours = empty( $instance['evergreen_counter_hours'] ) ? 0 : ( $instance['evergreen_counter_hours'] * HOUR_IN_SECONDS ); + $minutes = empty( $instance['evergreen_counter_minutes'] ) ? 0 : ( $instance['evergreen_counter_minutes'] * MINUTE_IN_SECONDS ); + $evergreen_interval = $hours + $minutes; + + return $evergreen_interval; + } + + private function get_actions( $settings ) { + if ( empty( $settings['expire_actions'] ) || ! is_array( $settings['expire_actions'] ) ) { + return false; + } + + $actions = []; + + foreach ( $settings['expire_actions'] as $action ) { + $action_to_run = [ 'type' => $action ]; + if ( 'redirect' === $action ) { + if ( empty( $settings['expire_redirect_url']['url'] ) ) { + continue; + } + $action_to_run['redirect_url'] = esc_url( $settings['expire_redirect_url']['url'] ); + } + $actions[] = $action_to_run; + } + + return $actions; + } + + private function is_valid_url( $url ) { + return ! preg_match( '/\bjavascript\b/i', $url ) && filter_var( $url, FILTER_VALIDATE_URL ); + } + + private function sanitize_action( $key, $value ) { + if ( 'redirect_url' === $key && is_string( $value ) ) { + return $this->is_valid_url( $value ) ? esc_url( $value ) : null; + } + + return esc_html( $value ); + } + + private function map_sanitized_action( $action ) { + $sanitized_action = []; + + foreach ( $action as $key => $value ) { + $sanitized_action[ $key ] = $this->sanitize_action( $key, $value ); + } + + return $sanitized_action; + } + + private function sanitize_redirect_url( $actions ) { + return array_map( function ( $action ) { + return $this->map_sanitized_action( $action ); + }, $actions ); + } + + + protected function render() { + $instance = $this->get_settings_for_display(); + $due_date = $instance['due_date']; + $string = $this->get_strftime( $instance ); + + if ( 'evergreen' === $instance['countdown_type'] ) { + $this->add_render_attribute( 'div', 'data-evergreen-interval', $this->get_evergreen_interval( $instance ) ); + } else { + $wp_timezone = new \DateTimeZone( wp_timezone_string() ); + $due_date = new \DateTime( $due_date, $wp_timezone ); + $due_date = $due_date->getTimestamp(); + } + + $actions = false; + + if ( ! Plugin::elementor()->editor->is_edit_mode() ) { + $actions = $this->get_actions( $instance ); + } + + if ( $actions ) { + $sanitized_actions = $this->sanitize_redirect_url( $actions ); + + $this->add_render_attribute( 'div', 'data-expire-actions', wp_json_encode( $sanitized_actions ) ); + } + + $this->add_render_attribute( 'div', [ + 'class' => 'elementor-countdown-wrapper', + 'data-date' => $due_date, + ] ); + + ?> +
    print_render_attribute_string( 'div' ); ?>> + +
    + +
    + +
    + add_actions(); + } + + public function get_name() { + return 'custom-attributes'; + } + + private function get_black_list_attributes() { + static $black_list = null; + + if ( null === $black_list ) { + $black_list = [ 'id', 'class', 'data-id', 'data-settings', 'data-element_type', 'data-widget_type', 'data-model-cid' ]; + + /** + * Elementor attributes black list. + * + * Filters the attributes that won't be rendered in the wrapper element. + * + * By default Elementor doesn't render some attributes to prevent things + * from breaking down. This hook allows developers to alter this list of + * attributes. + * + * @since 2.2.0 + * + * @param array $black_list A black list of attributes. + */ + $black_list = apply_filters( 'elementor_pro/element/attributes/black_list', $black_list ); + } + + return $black_list; + } + + /** + * @param Element_Base $element + */ + public function replace_go_pro_custom_attributes_controls( Element_Base $element ) { + Plugin::elementor()->controls_manager->remove_control_from_stack( $element->get_unique_name(), [ 'section_custom_attributes_pro', 'custom_attributes_pro' ] ); + + $this->register_custom_attributes_controls( $element ); + } + + public function register_custom_attributes_controls( Element_Base $element ) { + $element_name = $element->get_name(); + + $element->start_controls_section( + '_section_attributes', + [ + 'label' => esc_html__( 'Attributes', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_ADVANCED, + ] + ); + + $element->add_control( + '_attributes', + [ + 'label' => esc_html__( 'Custom Attributes', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'placeholder' => esc_html__( 'key|value', 'elementor-pro' ), + 'description' => sprintf( + /* translators: %s: The `|` separate char. */ + esc_html__( 'Set custom attributes for the wrapper element. Each attribute in a separate line. Separate attribute key from the value using %s character.', 'elementor-pro' ), + '|' + ), + 'classes' => 'elementor-control-direction-ltr', + ] + ); + + $element->end_controls_section(); + } + + /** + * @param $element Controls_Stack + * @param $section_id string + */ + public function register_controls( Controls_Stack $element, $section_id ) { + if ( ! $element instanceof Element_Base ) { + return; + } + + // Remove Custom CSS Banner (From free version) + if ( 'section_custom_attributes_pro' !== $section_id ) { + return; + } + + if ( ! API::is_licence_has_feature( self::LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->replace_controls_with_upgrade_promotion( $element ); + return; + } + + $this->replace_go_pro_custom_attributes_controls( $element ); + } + + /** + * @param $element Element_Base + */ + public function render_attributes( Element_Base $element ) { + $settings = $element->get_settings_for_display(); + + if ( ! empty( $settings['_attributes'] ) ) { + $attributes = Utils::parse_custom_attributes( $settings['_attributes'], "\n" ); + + $black_list = $this->get_black_list_attributes(); + + foreach ( $attributes as $attribute => $value ) { + if ( ! in_array( $attribute, $black_list, true ) ) { + $element->add_render_attribute( '_wrapper', $attribute, $value ); + } + } + } + } + + protected function add_actions() { + add_action( 'elementor/element/after_section_end', [ $this, 'register_controls' ], 10, 2 ); + + if ( API::is_licence_has_feature( static::LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + add_action( 'elementor/element/after_add_attributes', [ $this, 'render_attributes' ] ); + } + } + + private function replace_controls_with_upgrade_promotion( Element_Base $element ) { + Plugin::elementor()->controls_manager->remove_control_from_stack( $element->get_unique_name(), [ 'section_custom_attributes_pro', 'section_custom_attributes_pro' ] ); + + $element->start_controls_section( + 'section_custom_attributes_promotion', + [ + 'label' => esc_html__( 'Attributes', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_ADVANCED, + ] + ); + + $element->add_control( + 'custom_attributes_promotion', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => Tiers::get_promotion_template( [ + 'title' => esc_html__( 'Meet Our Attributes', 'elementor-pro' ), + 'messages' => [ + esc_html__( 'Add custom HTML attributes to any element.', 'elementor-pro' ), + ], + 'link' => 'https://go.elementor.com/go-pro-advanced-attributes/', + ] ), + ] + ); + + $element->end_controls_section(); + } +} diff --git a/modules/custom-code/admin-menu-items/custom-code-menu-item.php b/modules/custom-code/admin-menu-items/custom-code-menu-item.php new file mode 100644 index 0000000..09f539c --- /dev/null +++ b/modules/custom-code/admin-menu-items/custom-code-menu-item.php @@ -0,0 +1,36 @@ +license_admin->get_connect_url( [ + 'utm_source' => 'custom-code', + 'utm_medium' => 'wp-dash', + 'utm_campaign' => 'connect-and-activate-license', + ] ); + + $renew_url = 'https://go.elementor.com/renew-custom-code/'; + + return API::is_license_expired() + ? $renew_url + : $connect_url; + } + + public function get_cta_text() { + if ( ! API::active_licence_has_feature( Custom_Code_Module::MODULE_NAME ) ) { + return esc_html__( 'Upgrade Now', 'elementor-pro' ); + } + + return API::is_license_expired() + ? esc_html__( 'Renew now', 'elementor-pro' ) + : esc_html__( 'Connect & Activate', 'elementor-pro' ); + } + + public function get_label() { + return $this->get_page_title(); + } + + public function get_page_title() { + return esc_html__( 'Custom Code', 'elementor-pro' ); + } + + public function get_promotion_title(): string { + return sprintf( esc_html__( 'Enjoy Creative Freedom %s with Custom Code', 'elementor-pro' ), '
    ' ); + } + + public function get_video_url(): string { + return 'https://www.youtube-nocookie.com/embed/IOovQd1hJUg?si=JLHk3UAexnvTfU1a'; + } + + public function get_promotion_description() { + return esc_html__( + 'Add Custom Code snippets to your website.', + 'elementor-pro' + ); + } + + public function get_side_note(): string { + return esc_html__( '* Requires an Advanced subscription or higher', 'elementor-pro' ); + } + + /** + * @deprecated use get_promotion_description instead + * @return void + */ + public function render_promotion_description() { + echo $this->get_promotion_description(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + protected function get_content_lines(): array { + return [ + esc_html__( 'Add Custom Code snippets anywhere on your website, including the header or footer to measure your page’s performance*', 'elementor-pro' ), + esc_html__( 'Use Custom Code to create sophisticated custom interactions to engage visitors', 'elementor-pro' ), + esc_html__( 'Leverage Elementor AI to instantly generate Custom Code for Elementor', 'elementor-pro' ), + ]; + } +} diff --git a/modules/custom-code/custom-code-metabox.php b/modules/custom-code/custom-code-metabox.php new file mode 100644 index 0000000..3374561 --- /dev/null +++ b/modules/custom-code/custom-code-metabox.php @@ -0,0 +1,424 @@ +' . $label . '

    '; + } + + return $label; + } + + public function get_location_labels() { + return [ + self::OPTION_LOCATION_HEAD => esc_html__( 'Head', 'elementor-pro' ), + self::OPTION_LOCATION_BODY_START => esc_html__( 'Body Start', 'elementor-pro' ), + self::OPTION_LOCATION_BODY_END => esc_html__( 'Body End', 'elementor-pro' ), + ]; + } + + public function get_location_options() { + return [ + self::OPTION_LOCATION_HEAD => '', + self::OPTION_LOCATION_BODY_START => sprintf( + /* translators: %s: Body opening tag. */ + esc_html__( '%s - Start', 'elementor-pro' ), + '' + ), + self::OPTION_LOCATION_BODY_END => sprintf( + /* translators: %s: Body closing tag. */ + esc_html__( '%s - End', 'elementor-pro' ), + '' + ), + ]; + } + + public function get_priority_options() { + $start = 1; + $result = range( $start, self::OPTION_PRIORITY_LENGTH ); + + $result = array_combine( $result, $result ); + + return $result; + } + + /** + * Add script integrity. + * + * This is method is public, since its has to remove its own filter. + * + * @param string $html + * @param mixed $handle + * + * @return string + */ + public function add_script_integrity( $html, $handle ) { + if ( 'jshint' === $handle ) { + $html = str_replace( '>', ' integrity="sha512-qcoitUjhkmNyPmbIOlUV/zd8MJvrVcKrNqnveMWS3C6MYOl5+HLwliRKUm/Ae/dfIok6+E54hjgVrAeS+sBAGA==" crossorigin="anonymous">', $html ); + + remove_filter( 'script_loader_tag', [ $this, 'add_script_integrity' ] ); + } + + return $html; + } + + protected function actions() { + add_action( 'add_meta_boxes_' . Module::CPT, function () { + $this->add_meta_boxes(); + } ); + + add_action( 'save_post_' . Module::CPT, function( $post_id, $post, $update ) { + return $this->save_post_meta( $post_id, $post ); + }, 10, 3 ); + + add_action('post_submitbox_misc_actions', function ( $post ) { + $this->add_meta_publish_options( $post ); + } ); + } + + private function get_fields() { + return [ + [ + 'id' => 'open-div-meta-box', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'attributes' => [ + 'class' => 'elementor-custom-code-meta-box', + ], + ], + [ + 'id' => 'open-div-panel', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'attributes' => [ + 'class' => 'elementor-custom-code-panel', + ], + ], + [ + 'id' => 'open-div-placement', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'attributes' => [ + 'class' => 'elementor-custom-code-panel-placement', + ], + ], + [ + 'id' => self::FIELD_LOCATION, + 'field_type' => 'select', + 'label' => esc_html__( 'Location', 'elementor-pro' ) . ':', + 'options' => $this->get_location_options(), + 'info' => esc_html__( 'Define where the Custom Code will appear', 'elementor-pro' ), + ], + [ + 'id' => 'open-div-placement', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'attributes' => [ + 'class' => 'elementor-custom-code-options-placement', + ], + ], + [ + 'id' => self::FILED_EXTRA_OPTIONS, + 'field_type' => 'checkbox', + 'options' => [ + self::INPUT_OPTION_ENSURE_JQUERY => esc_html__( 'Always load jQuery', 'elementor-pro' ), + ], + 'info' => esc_html__( 'If your snippet includes jQuery, this will ensure it will work for all visitors. It may have a minor impact on loading speed.', 'elementor-pro' ), + ], + [ + 'id' => 'close-div-placement', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'close' => true, + ], + [ + 'id' => self::FIELD_PRIORITY, + 'field_type' => 'select', + 'label' => esc_html__( 'Priority', 'elementor-pro' ) . ':', + 'options' => $this->get_priority_options(), + 'info' => esc_html__( 'Define in which order the Custom Code will appear', 'elementor-pro' ), + ], + [ + 'id' => 'close-div-placement', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'close' => true, + ], + [ + 'id' => 'close-div-panel', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'close' => true, + ], + [ + 'id' => 'close-div-meta-box', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'close' => true, + ], + [ + 'id' => 'open-div-code-mirror-holder', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'attributes' => [ + 'class' => 'elementor-custom-code-codemirror-holder', + ], + ], + [ + 'id' => 'open-div-code-mirror', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'attributes' => [ + 'class' => 'elementor-custom-code-codemirror', + ], + ], + [ + 'id' => self::FIELD_CODE, + 'field_type' => 'textarea', + 'label' => '', + 'extra_attributes' => [ + 'class' => 'hidden', + ], + ], + [ + 'id' => 'close-div-code-mirror', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'close' => true, + ], + [ + 'id' => 'close-div-code-mirror-holder', + 'field_type' => 'html_tag', + 'label' => false, + 'tag' => 'div', + 'close' => true, + ], + ]; + } + + private function get_code_editor_settings() { + // TODO: Handle `enqueue_code_editor_scripts` to work with `lint => 'true'`. + return [ + 'type' => 'text/html', + 'codemirror' => [ + 'indentUnit' => 2, + 'tabSize' => 2, + 'gutters' => [ 'CodeMirror-lint-markers' ], + ], + ]; + } + + private function enqueue_code_editor_scripts( $field_code_id ) { + // Add integrity attribute to jshint. + add_filter( 'script_loader_tag', [ $this, 'add_script_integrity' ], 10, 2 ); + + wp_enqueue_script( 'htmlhint' ); + wp_enqueue_script( 'csslint' ); + + wp_deregister_script( 'jshint' ); + + wp_enqueue_script( 'jshint', + 'https://cdnjs.cloudflare.com/ajax/libs/jshint/2.12.0/jshint.min.js', + [], + '2.12.0' + ); + + /** + * Some of the plugins may load 'code-editor' for their needs and change the default behavior, so it should + * re-initialize the code editor with 'custom code' settings. + */ + if ( wp_script_is( 'code-editor' ) ) { + wp_add_inline_script( 'custom-code-metabox', sprintf( 'wp.codeEditor.initialize( jQuery( "#%s"), %s );', $field_code_id, wp_json_encode( wp_get_code_editor_settings( $this->get_code_editor_settings() ) ) ) ); + } else { + wp_enqueue_code_editor( $this->get_code_editor_settings() ); + + wp_add_inline_script( 'code-editor', sprintf( 'wp.codeEditor.initialize( jQuery( "#%s") );', $field_code_id ) ); + } + } + + private function render_meta_box() { + $fields = $this->get_fields(); + + if ( ! empty( $_REQUEST['action'] ) && 'edit' == $_REQUEST['action'] ) { + $post = get_post( \ElementorPro\Core\Utils::_unstable_get_super_global_value( $_REQUEST, 'post' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here. + foreach ( self::INPUT_FIELDS as $input_field ) { + $field_meta = get_post_meta( $post->ID, "_elementor_$input_field", true ); + + if ( ! empty( $field_meta ) ) { + $key = array_search( $input_field, array_column( $fields, 'id' ) ); + if ( false !== $key ) { + $fields[ $key ]['saved'] = $field_meta; + } + } + } + } + + // The method, support fields only. + $this->print_metabox( $fields ); + + /** + * Elementor metabox render. + * + * Fires before custom scripts are enqueued, since enqueue depends on + * render handlers. + * + * @param Custom_Code_Metabox $this An instance of custom code metabox. + * @param int|false $id The ID of the current WordPress post. + * False if post is not set. + */ + do_action( 'elementor-pro/metabox/render', $this, get_the_ID() ); + + // Init codemirror. + $this->enqueue_code_editor_scripts( self::FIELD_CODE ); + } + + private function save_post_meta( $post_id, $post ) { + if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { + return $post_id; + } + + // Check the user's permissions. + if ( ! current_user_can( 'edit_post', $post_id ) ) { + return $post_id; + } + + if ( get_post_status( $post->ID ) === 'auto-draft' ) { + return $post_id; + } + + // PHPCS - Should not validate for nonce (already done in WordPress save_post). + $post_data = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing + + foreach ( self::INPUT_FIELDS as $field ) { + if ( isset( $post_data[ $field ] ) && ! Utils::is_empty( $post_data[ $field ] ) ) { + if ( self::FIELD_CODE === $field ) { + $post_meta = $post_data[ $field ]; + } else { + $post_meta = sanitize_text_field( $post_data[ $field ] ); + } + + if ( ! current_user_can( 'unfiltered_html' ) ) { + $post_meta = wp_kses_post( $post_meta ); + } + update_post_meta( $post->ID, "_elementor_$field", $post_meta ); + + /** @var \ElementorPro\Modules\ThemeBuilder\Module $theme_builder */ + $theme_builder = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); + $theme_builder->get_conditions_manager()->get_cache()->regenerate(); + } elseif ( self::FILED_EXTRA_OPTIONS === $field ) { + $input_options = []; + + foreach ( self::INPUT_OPTIONS as $input_option ) { + $key = self::FILED_EXTRA_OPTIONS . '_' . $input_option; + $input_option_value = \ElementorPro\Core\Utils::_unstable_get_super_global_value( $post_data, $key ); + + if ( 'on' === $input_option_value ) { + $input_options [] = $input_option; + } + } + + update_post_meta( $post->ID, "_elementor_$field", $input_options ); + } + } + + // Temporary workaround for applying conditions for draft custom code post. + if ( ! empty( $post_data['_conditions'] ) ) { + $conditions = (array) json_decode( wp_unslash( $post_data['_conditions'] ) ); + + foreach ( $conditions as $key => $item ) { + $item_assoc_array = (array) $item; + + $conditions[ $key ] = [ + $item_assoc_array['type'], + $item_assoc_array['name'], + $item_assoc_array['sub'], + $item_assoc_array['subId'], + ]; + } + + /** @var \ElementorPro\Modules\ThemeBuilder\Module $theme_builder */ + $theme_builder = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); + + $theme_builder->get_conditions_manager()->save_conditions( $post_id, $conditions ); + } + } + + private function add_meta_boxes() { + add_meta_box( + 'elementor-custom-code', + __( 'Custom code', 'elementor-pro' ), + function() { + $this->render_meta_box(); + }, + module::CPT, + 'normal', + 'default' + ); + } + + private function add_meta_publish_options( $post ) { + if ( Module::CPT === $post->post_type ) { + ?> +
    + + + +
    + post->ID, '_elementor_' . Custom_Code_Metabox::FIELD_CODE, true ) . PHP_EOL; + $user_has_permission = current_user_can( Module::CAPABILITY ); + + $this->apply_snippet_options( get_post_meta( $this->get_id(), '_elementor_' . Custom_Code_Metabox::FILED_EXTRA_OPTIONS, true ) ); + + if ( $user_has_permission ) { + $this->print_snippet_with_elementor_comment( $content ); + } else { + // PHPCS - the main content of custom code + echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + } + + public static function get_create_url() { + $query_args = [ + 'post_type' => Module::CPT, + ]; + + return add_query_arg( $query_args, admin_url( 'post-new.php' ) ); + } + + private function print_snippet_with_elementor_comment( $content ) { + echo implode( PHP_EOL, [ + '', + '', + '', + ] ); + + // PHPCS - the main content of custom code + echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + + echo PHP_EOL . '' . PHP_EOL; + } + + private function apply_snippet_options( $options ) { + if ( ! is_array( $options ) || ! count( $options ) ) { + return; + } + + foreach ( $options as $option ) { + if ( ! empty( self::$applied_options[ $option ] ) ) { + continue; + } + + switch ( $option ) { + case Custom_Code_Metabox::INPUT_OPTION_ENSURE_JQUERY: + wp_enqueue_script( 'jquery' ); + + // Ensure jQuery will be first in order. + if ( 'wp_footer' === current_filter() ) { + wp_print_footer_scripts(); + } + break; + } + + self::$applied_options[ $option ] = true; + } + } +} diff --git a/modules/custom-code/module.php b/modules/custom-code/module.php new file mode 100644 index 0000000..873bea3 --- /dev/null +++ b/modules/custom-code/module.php @@ -0,0 +1,449 @@ +meta_box = new Custom_Code_Metabox(); + + $this->actions(); + + if ( $this->can_use_custom_code() ) { + $this->register_custom_post_type(); + $this->register_metabox(); + } + } + + public function get_name() { + return 'custom-code'; + } + + private function actions() { + if ( $this->can_use_custom_code() ) { + add_action( 'elementor/documents/register', function ( $documents_manager ) { + return $this->register_documents( $documents_manager ); + } ); + + add_action( 'elementor/theme/register_locations', function ( $location_manager ) { + return $this->register_location( $location_manager ); + } ); + } + + add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu_manager ) { + $this->add_admin_menu( $admin_menu_manager ); + } ); + + // TODO: BC - Remove after `Admin_Menu_Manager` will be the standard. + add_action( 'admin_menu', function () { + if ( did_action( 'elementor/admin/menu/register' ) ) { + return; + } + + $menu_title = esc_html__( 'Custom Code', 'elementor-pro' ); + add_submenu_page( + Settings::PAGE_ID, + $menu_title, + $menu_title, + self::CAPABILITY, + static::MENU_SLUG + ); + }, /* After custom icons */ 51 ); + + add_action( 'current_screen', function () { + if ( ! is_admin() ) { + return; + } + + $current_screen = get_current_screen(); + + if ( 'edit-' . self::CPT === $current_screen->id ) { + $this->admin_ui_actions(); + } elseif ( self::CPT === $current_screen->id ) { + // Enqueue assets. + add_action( 'admin_enqueue_scripts', function () { + $this->enqueue_assets(); + }, 0 /* elementor-app-base styles should be loaded in early stages */ ); + } + } ); + + $this->frontend_actions(); + } + + private function admin_ui_actions() { + // Show blank 'custom code' snippets list. + add_action( 'manage_posts_extra_tablenav', function ( $which ) { + return $this->maybe_render_blank_state( $which ); + } ); + + // Mange post columns. + add_filter( 'manage_posts_columns', function ( $columns ) { + return $this->manage_posts_columns( $columns ); + } ); + + add_action( 'manage_posts_custom_column', function ( $column_name, $post_id ) { + return $this->manage_posts_custom_column( $column_name, $post_id ); + }, 10, 2 ); + } + + private function frontend_actions() { + // Print snippets. + add_action( 'wp_head', function () { + $this->print_snippets( Custom_Code_Metabox::OPTION_LOCATION_HEAD ); + } ); + + add_action( 'wp_body_open', function () { + $this->print_snippets( Custom_Code_Metabox::OPTION_LOCATION_BODY_START ); + } ); + + add_action( 'wp_footer', function () { + $this->print_snippets( Custom_Code_Metabox::OPTION_LOCATION_BODY_END ); + }, 21 /* After 'wp_print_footer_scripts' */ ); + } + + private function register_custom_post_type() { + $labels = [ + 'name' => esc_html__( 'Custom Code', 'elementor-pro' ), + 'singular_name' => esc_html__( 'Custom Code', 'elementor-pro' ), + 'add_new' => esc_html__( 'Add new', 'elementor-pro' ), + 'add_new_item' => esc_html__( 'New code', 'elementor-pro' ), + 'edit_item' => esc_html__( 'Edit code', 'elementor-pro' ), + ]; + + register_post_type( self::CPT, [ + 'labels' => $labels, + 'public' => false, + 'rewrite' => false, + 'show_ui' => true, + 'show_in_menu' => false, + 'show_in_nav_menus' => false, + 'exclude_from_search' => true, + 'capability_type' => 'post', + 'hierarchical' => false, + 'supports' => [ 'title', 'author' ], + 'capabilities' => [ + 'publish_posts' => 'manage_options', + 'edit_posts' => 'manage_options', + 'edit_others_posts' => 'manage_options', + 'delete_posts' => 'manage_options', + 'delete_others_posts' => 'manage_options', + 'read_private_posts' => 'manage_options', + 'edit_post' => 'manage_options', + 'delete_post' => 'manage_options', + 'read_post' => 'manage_options', + ], + ] ); + + // Handling custom post type messages. + add_filter( 'post_updated_messages', function( $messages ) { + if ( self::CPT === get_post_type() ) { + $post = get_post(); + + // Thanks 'WooCommerce' for the example. + $messages[ self::CPT ] = [ + '', // Not in use. + __( 'Custom code updated.', 'elementor-pro' ), + __( 'Custom field updated.', 'elementor-pro' ), + __( 'Custom field deleted.', 'elementor-pro' ), + __( 'Custom code updated.', 'elementor-pro' ), + __( 'Revision restored.', 'elementor-pro' ), + __( 'Custom code published.', 'elementor-pro' ), + __( 'Custom code saved.', 'elementor-pro' ), + __( 'Custom code submitted.', 'elementor-pro' ), + sprintf( + /* translators: %s: The scheduled date. */ + __( 'Custom code scheduled for %s.', 'elementor-pro' ), + '' . date_i18n( esc_html__( 'M j, Y @ G:i', 'elementor-pro' ), strtotime( $post->post_date ) ) . '' + ), + __( 'Custom code draft updated.', 'elementor-pro' ), + ]; + } + + return $messages; + } ); + + add_filter( 'bulk_post_updated_messages', function ( $messages, $counters ) { + $current_screen = get_current_screen(); + + if ( $current_screen && self::CPT === $current_screen->post_type ) { + $messages[ self::CPT ] = [ + 'updated' => _n( '%s custom code updated.', '%s custom codes updated.', $counters['updated'], 'elementor-pro' ), + 'locked' => _n( '%s custom code cannot be not updated, someone else is editing it.', '%s custom codes cannot be not updated, someone else is editing them.', $counters['locked'], 'elementor-pro' ), + 'deleted' => _n( '%s custom code permanently deleted.', '%s custom codes permanently deleted.', $counters['deleted'], 'elementor-pro' ), + 'trashed' => _n( '%s custom code moved to trash.', '%s custom codes moved to trash.', $counters['trashed'], 'elementor-pro' ), + 'untrashed' => _n( '%s custom code restored.', '%s custom code restored.', $counters['untrashed'], 'elementor-pro' ), + ]; + } + + return $messages; + }, 10, 12 ); + } + + /** + * Function register metabox. + * + * Add meta box for custom-code post. + */ + private function register_metabox() { + if ( ! is_admin() ) { + return; + } + // Remove 'author' meta_box from 'add-new.php', 'author' is required only in list ( enabled via 'supports' arg ). + add_action( 'add_meta_boxes_' . self::CPT, function () { + remove_meta_box( 'authordiv', self::CPT, 'normal' ); + } ); + } + + private function enqueue_assets() { + wp_enqueue_style( 'elementor-app-base', $this->get_css_assets_url( 'app-base', null, 'default', true ), [ + 'elementor-icons', + 'select2', + ], ELEMENTOR_VERSION ); + + wp_register_style( + 'select2', + $this->get_css_assets_url( 'e-select2', 'assets/lib/e-select2/css/' ), + [], + '4.0.6-rc.1' + ); + + wp_register_script( + 'select2', + $this->get_js_assets_url( 'e-select2.full', 'assets/lib/e-select2/js/' ), + [ + 'jquery', + ], + '4.0.6-rc.1', + true + ); + + wp_register_style( + 'elementor-icons', + $this->get_css_assets_url( 'elementor-icons', 'assets/lib/eicons/css/' ), + [], + Icons_Manager::ELEMENTOR_ICONS_VERSION + ); + + wp_enqueue_script( 'react' ); + wp_enqueue_script( 'react-dom' ); + + wp_enqueue_script( + 'elementor-app-packages', + $this->get_js_assets_url( 'app-packages' ), + [ + 'wp-i18n', + 'react', + ], + ELEMENTOR_VERSION, + true + ); + + add_action( 'elementor-pro/metabox/render', function ( $metabox, $post_id ) { + $min_suffix = Utils::is_script_debug() ? '' : '.min'; + + wp_enqueue_script( + 'tipsy', + ELEMENTOR_ASSETS_URL . 'lib/tipsy/tipsy' . $min_suffix . '.js', + [ + 'jquery', + ], + '1.0.0', + true + ); + + $direction_suffix = is_rtl() ? '-rtl' : ''; + wp_enqueue_style( + 'custom-code', + ELEMENTOR_PRO_ASSETS_URL . 'css/modules/custom-code' . $direction_suffix . $min_suffix . '.css', + [ + 'elementor-app-base', + ], + ELEMENTOR_PRO_VERSION + ); + + // Load 'admin.js` module JS entry. + wp_enqueue_script( + 'custom-code-metabox', + ELEMENTOR_PRO_ASSETS_URL . 'js/custom-code' . $min_suffix . '.js', + [ + 'elementor-v2-ui', + 'elementor-v2-icons', + 'react', + 'select2', + // Temporary dependency until we will have a better way to load AI app in the admin. + 'elementor-ai-admin', + ], + ELEMENTOR_PRO_VERSION + ); + + $post = [ + 'ID' => $post_id, + 'post_status' => get_post_status( $post_id ), + ]; + + wp_add_inline_script( 'custom-code-metabox', sprintf( 'elementorProAdmin.customCode.post = %s;', wp_json_encode( $post ) ) ); + }, 10, 2 ); + } + + private function add_admin_menu( Admin_Menu_Manager $admin_menu_manager ) { + if ( $this->can_use_custom_code() ) { + $admin_menu_manager->register( static::MENU_SLUG, new Custom_Code_Menu_Item() ); + } else { + $admin_menu_manager->register( static::PROMOTION_MENU_SLUG, new Custom_Code_Promotion_Menu_Item() ); + } + } + + private function can_use_custom_code() { + return ( API::is_license_active() && API::is_licence_has_feature( static::MODULE_NAME, API::BC_VALIDATION_CALLBACK ) || $this->has_custom_code_snippets() ); + } + + private function has_custom_code_snippets() { + $existing_snippets = get_posts( [ + 'posts_per_page' => 1, // Avoid fetching too much data + 'post_type' => static::CPT, + ] ); + + return ! empty( $existing_snippets ); + } + + private function register_documents( Documents_Manager $documents_manager ) { + $documents_manager->register_document_type( self::DOCUMENT_TYPE, Document::get_class_full_name() ); + } + + private function register_location( Locations_Manager $location_manager ) { + foreach ( array_keys( $this->meta_box->get_location_options() ) as $location ) { + $location_manager->register_location( $location, [ + 'multiple' => true, + 'public' => false, + 'edit_in_content' => false, + ] ); + } + } + + private function maybe_render_blank_state( $which ) { + $counts = (array) wp_count_posts( self::CPT ); + unset( $counts['auto-draft'] ); + + if ( ! array_sum( $counts ) ) { + /** @var Source_Local $source */ + $source = Plugin::elementor()->templates_manager->get_source( 'local' ); + + $source->maybe_render_blank_state( $which, [ + 'post_type' => self::DOCUMENT_TYPE, + 'cpt' => self::CPT, + 'description' => esc_html__( 'Add pixels, meta tags and any other scripts to your site.', 'elementor-pro' ) . sprintf( '
    %s', esc_html__( 'Learn more about adding custom code', 'elementor-pro' ) ), + 'href' => esc_url( admin_url( '/post-new.php?post_type=' . self::CPT ) ), + ] ); + } + } + + private function manage_posts_columns( $columns ) { + $new = [ + self::ADDITIONAL_COLUMN_INSTANCES => esc_html__( 'Instances', 'elementor-pro' ), + Custom_Code_Metabox::FIELD_LOCATION => esc_html__( 'Location', 'elementor-pro' ), + Custom_Code_Metabox::FIELD_PRIORITY => esc_html__( 'Priority', 'elementor-pro' ), + ]; + + // Insert after 'author'. + $keys = array_keys( $columns ); + $pos = array_search( 'author', $keys ) + 1; + $columns = array_merge( array_slice( $columns, 0, $pos ), $new, array_slice( $columns, $pos ) ); + + return $columns; + } + + private function manage_posts_custom_column( $column_name, $post_id ) { + if ( in_array( $column_name, Custom_Code_Metabox::INPUT_FIELDS ) ) { + $value = get_post_meta( $post_id, '_elementor_' . $column_name, true ); + + if ( Custom_Code_Metabox::FIELD_LOCATION === $column_name ) { + $location_labels = $this->meta_box->get_location_labels(); + + if ( isset( $location_labels[ $value ] ) ) { + $value = $location_labels[ $value ]; + } + } + + echo esc_html( $value ); + } elseif ( self::ADDITIONAL_COLUMN_INSTANCES === $column_name ) { + /** @var Conditions_Manager $conditions_manager */ + $conditions_manager = Plugin::instance()->modules_manager->get_modules( 'theme-builder' )->get_conditions_manager(); + + echo esc_html( implode( ', ', $conditions_manager->get_document_instances( $post_id ) ) ); + } + } + + private function get_snippets_by_location( $location ) { + return get_posts( [ + 'numberposts' => -1, + 'post_type' => self::CPT, + 'meta_query' => [ + [ + 'key' => '_elementor_' . Custom_Code_Metabox::FIELD_LOCATION, + 'value' => $location, + ], + ], + // Order. + 'order' => 'ASC', + 'orderby' => 'meta_value_num', + 'meta_key' => '_elementor_' . Custom_Code_Metabox::FIELD_PRIORITY, + ] ); + } + + private function print_snippets( $location ) { + // Do not print snippets on safe mode. + if ( isset( $_REQUEST['elementor-mode'] ) && 'safe' === $_REQUEST['elementor-mode'] ) { + return; + } + + $snippets = $this->get_snippets_by_location( $location ); + + /** @var \ElementorPro\Modules\ThemeBuilder\Module $theme_builder */ + $theme_builder = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); + $documents_by_conditions = $theme_builder->get_conditions_manager()->get_documents_for_location( $location ); + $location_manager = $theme_builder->get_locations_manager(); + + foreach ( $snippets as $snippet ) { + // Add snippet to location. + // Also handling situation without conditions, bind current snippet id with current location. + if ( isset( $documents_by_conditions[ $snippet->ID ] ) || ! get_post_meta( $snippet->ID, '_elementor_conditions', true ) ) { + $location_manager->add_doc_to_location( $location, $snippet->ID ); + } + } + + elementor_theme_do_location( $location ); + } +} diff --git a/modules/custom-css/admin-menu-items/settings-custom-css-pro.php b/modules/custom-css/admin-menu-items/settings-custom-css-pro.php new file mode 100644 index 0000000..0018e3b --- /dev/null +++ b/modules/custom-css/admin-menu-items/settings-custom-css-pro.php @@ -0,0 +1,20 @@ + esc_html__( 'Meet Our Custom CSS', 'elementor-pro' ), + 'messages' => [ + esc_html__( 'Apply CSS globally across your site to elevate your designs.', 'elementor-pro' ), + ], + 'link' => 'https://go.elementor.com/go-pro-advanced-global-css/', + ] ); + + Custom_Css::instance()->replace_controls_with_upgrade_promotion( $this->parent, $this->get_id(), $template ); + } +} diff --git a/modules/custom-css/module.php b/modules/custom-css/module.php new file mode 100644 index 0000000..c656670 --- /dev/null +++ b/modules/custom-css/module.php @@ -0,0 +1,189 @@ +add_actions(); + } + + public function get_name() { + return 'custom-css'; + } + + /** + * @param $element Controls_Stack + * @param $section_id string + */ + public function register_controls( Controls_Stack $element, $section_id ) { + // Remove Custom CSS Banner (From free version) + if ( 'section_custom_css_pro' !== $section_id ) { + return; + } + + if ( ! API::is_licence_has_feature( static::LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $template = Tiers::get_promotion_template( [ + 'title' => esc_html__( 'Meet Our Custom CSS', 'elementor-pro' ), + 'messages' => [ + esc_html__( 'Apply CSS to any widget and elevate any element with Custom CSS.', 'elementor-pro' ), + ], + 'link' => 'https://go.elementor.com/go-pro-advanced-custom-css/', + ] ); + + $this->replace_controls_with_upgrade_promotion( $element, Controls_Manager::TAB_ADVANCED, $template ); + return; + } + + $this->replace_go_pro_custom_css_controls( $element ); + } + + /** + * @param $post_css Post + * @param $element Element_Base + */ + public function add_post_css( $post_css, $element ) { + if ( $post_css instanceof Dynamic_CSS ) { + return; + } + + $element_settings = $element->get_settings(); + + if ( empty( $element_settings['custom_css'] ) ) { + return; + } + + $css = trim( $element_settings['custom_css'] ); + + if ( empty( $css ) ) { + return; + } + $css = str_replace( 'selector', $post_css->get_element_unique_selector( $element ), $css ); + + // Add a css comment + $css = sprintf( '/* Start custom CSS for %s, class: %s */', $element->get_name(), $element->get_unique_selector() ) . $css . '/* End custom CSS */'; + + $post_css->get_stylesheet()->add_raw_css( $css ); + } + + /** + * @param $post_css Post + */ + public function add_page_settings_css( $post_css ) { + $document = Plugin::elementor()->documents->get( $post_css->get_post_id() ); + $custom_css = $document->get_settings( 'custom_css' ) ?? ''; + + $custom_css = trim( $custom_css ); + + if ( empty( $custom_css ) ) { + return; + } + + $custom_css = str_replace( 'selector', $document->get_css_wrapper_selector(), $custom_css ); + + // Add a css comment + $custom_css = '/* Start custom CSS */' . $custom_css . '/* End custom CSS */'; + + $post_css->get_stylesheet()->add_raw_css( $custom_css ); + } + + /** + * @param Controls_Stack $controls_stack + */ + public function replace_go_pro_custom_css_controls( $controls_stack ) { + $old_section = Plugin::elementor()->controls_manager->get_control_from_stack( $controls_stack->get_unique_name(), 'section_custom_css_pro' ); + + Plugin::elementor()->controls_manager->remove_control_from_stack( $controls_stack->get_unique_name(), [ 'section_custom_css_pro', 'custom_css_pro' ] ); + + $controls_stack->start_controls_section( + 'section_custom_css', + [ + 'label' => esc_html__( 'Custom CSS', 'elementor-pro' ), + 'tab' => $old_section['tab'], + ] + ); + + $controls_stack->add_control( + 'custom_css', + [ + 'label' => esc_html__( 'Add your own custom CSS', 'elementor-pro' ), + 'type' => Controls_Manager::CODE, + 'description' => sprintf( + /* translators: 1: Link opening tag, 2: Link opening tag, 3: Link closing tag. */ + esc_html__( 'Use %1$scustom CSS%3$s to style your content or add %2$sthe "selector" prefix%3$s to target specific elements.', 'elementor-pro' ), + '', + '', + '' + ), + 'language' => 'css', + 'render_type' => 'ui', + ] + ); + + $controls_stack->end_controls_section(); + } + + /** + * @deprecated 3.1.0 + */ + public function localize_settings() { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); + + return []; + } + + protected function add_actions() { + add_action( 'elementor/element/after_section_end', [ $this, 'register_controls' ], 10, 2 ); + add_action( 'elementor/element/parse_css', [ $this, 'add_post_css' ], 10, 2 ); + add_action( 'elementor/css-file/post/parse', [ $this, 'add_page_settings_css' ] ); + + // Check license for site settings tabs + if ( ! API::is_licence_has_feature( static::LICENSE_FEATURE_NAME_GLOBAL, API::BC_VALIDATION_CALLBACK ) ) { + add_action( 'elementor/kit/register_tabs', function ( Kit $kit ) { + $kit->register_tab( 'settings-custom-css', Settings_Custom_CSS_Pro::class ); + }, 100 ); + } + } + + public function replace_controls_with_upgrade_promotion( Controls_Stack $controls_stack, $tab, $template ) { + Plugin::elementor()->controls_manager->remove_control_from_stack( $controls_stack->get_unique_name(), [ 'section_custom_css_pro', 'custom_css_pro' ] ); + + $controls_stack->start_controls_section( + 'section_custom_css_promotion', + [ + 'label' => esc_html__( 'Custom CSS', 'elementor-pro' ), + 'tab' => $tab, + ] + ); + + $controls_stack->add_control( + 'custom_css_promotion', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => $template, + ] + ); + + $controls_stack->end_controls_section(); + } +} diff --git a/modules/display-conditions/classes/and-condition.php b/modules/display-conditions/classes/and-condition.php new file mode 100644 index 0000000..ea6f6f8 --- /dev/null +++ b/modules/display-conditions/classes/and-condition.php @@ -0,0 +1,49 @@ +conditions_manager = $conditions_manager; + $this->conditions = $conditions; + } + + public function check() { + foreach ( $this->conditions as $condition_options ) { + $condition_result = $this->is_condition_passing_check( $condition_options ); + + if ( ! $condition_result ) { + return false; + } + } + + return true; + } + + private function is_condition_passing_check( $condition_options ) { + $condition_instance = $this->get_condition_instance( $condition_options ); + + return $condition_instance + ? $condition_instance->check( $condition_options ) + : true; + } + + private function get_condition_instance( $condition ) { + if ( ! isset( $condition['condition'] ) ) { + return false; + } + + return $this->conditions_manager->get_condition( $condition['condition'] ); + } +} diff --git a/modules/display-conditions/classes/cache-notice.php b/modules/display-conditions/classes/cache-notice.php new file mode 100644 index 0000000..82dbdc5 --- /dev/null +++ b/modules/display-conditions/classes/cache-notice.php @@ -0,0 +1,22 @@ + $value_to_check; + case Comparator_Provider::COMPARATOR_IS_AFTER_INCLUSIVE: + return $set_value <= $value_to_check; + case Comparator_Provider::COMPARATOR_IS_BEFORE_INCLUSIVE: + return $set_value >= $value_to_check; + default: + return false; + } + } + + public static function check_array_contains( string $comparator, array $expected_values, array $array_of_values ): bool { + $is_contained = ! empty( array_intersect( $expected_values, $array_of_values ) ); + switch ( $comparator ) { + case Comparator_Provider::COMPARATOR_IS: + case Comparator_Provider::COMPARATOR_IS_ONE_OF: + return $is_contained; + + case Comparator_Provider::COMPARATOR_IS_NOT: + case Comparator_Provider::COMPARATOR_IS_NONE_OF: + return ! $is_contained; + + default: + return false; + } + } + + public static function check_string_contains( string $comparator, string $expected_value, string $actual_value ): bool { + $expected_value = strtolower( $expected_value ); + $actual_value = strtolower( $actual_value ); + + switch ( $comparator ) { + case Comparator_Provider::COMPARATOR_IS: + return $expected_value === $actual_value; + + case Comparator_Provider::COMPARATOR_IS_NOT: + return $expected_value !== $actual_value; + + case Comparator_Provider::COMPARATOR_CONTAINS: + return str_contains( $actual_value, $expected_value ); + + case Comparator_Provider::COMPARATOR_NOT_CONTAIN: + return ! str_contains( $actual_value, $expected_value ); + + default: + return false; + } + } + + public static function check_string_contains_and_empty( string $comparator, string $expected_value, string $actual_value ): bool { + if ( self::check_string_contains( $comparator, $expected_value, $actual_value ) ) { + return true; + } + + switch ( $comparator ) { + case Comparator_Provider::COMPARATOR_IS_EMPTY: + return empty( $actual_value ); + + case Comparator_Provider::COMPARATOR_IS_NOT_EMPTY: + return ! empty( $actual_value ); + + default: + return false; + } + } + + public static function check_equality( string $comparator, string $value, string $compare_to ): bool { + switch ( $comparator ) { + case Comparator_Provider::COMPARATOR_IS: + return $value === $compare_to; + + case Comparator_Provider::COMPARATOR_IS_NOT: + return $value !== $compare_to; + + default: + return false; + } + } + + /** + * @param string $comparator + * @param int $value + * @param int $compare_to + * + * @return bool + */ + public static function check_numeric_constraints( string $comparator, int $value, int $compare_to ): bool { + switch ( $comparator ) { + case Comparator_Provider::COMPARATOR_IS: + return $compare_to === $value; + case Comparator_Provider::COMPARATOR_IS_NOT: + return $compare_to !== $value; + case Comparator_Provider::COMPARATOR_IS_LESS_THAN_INCLUSIVE: + return $compare_to <= $value; + case Comparator_Provider::COMPARATOR_IS_GREATER_THAN_INCLUSIVE: + return $compare_to >= $value; + default: + return false; + } + } +} diff --git a/modules/display-conditions/classes/conditions-manager.php b/modules/display-conditions/classes/conditions-manager.php new file mode 100644 index 0000000..da0dba8 --- /dev/null +++ b/modules/display-conditions/classes/conditions-manager.php @@ -0,0 +1,203 @@ +display_conditions_module = $display_conditions_module; + $this->register_conditions(); + } + + private function init_groups() { + $this->groups = [ + 'page' => [ + 'label' => esc_html__( 'Page', 'elementor-pro' ), + ], + 'post' => [ + 'label' => esc_html__( 'Post', 'elementor-pro' ), + ], + 'user' => [ + 'label' => esc_html__( 'User', 'elementor-pro' ), + ], + 'date' => [ + 'label' => esc_html__( 'Date and Time', 'elementor-pro' ), + ], + 'archive' => [ + 'label' => esc_html__( 'Archive', 'elementor-pro' ), + ], + ]; + + /** + * Registration of a group for the display conditions. + * + * Fires when a new display condition groups is registered. This hook allows developers + * to register new display conditions groups using add_group(). + * + * @param Conditions_Manager $this An instance of conditions manager. + */ + do_action( 'elementor/display_conditions/register_groups', $this ); + + $this->groups['other'] = [ 'label' => esc_html__( 'Other', 'elementor-pro' ) ]; + } + + private function register_condition( $id, $args = [] ) { + if ( isset( $this->conditions[ $id ] ) ) { + return; + } + + $args[] = new Wordpress_Adapter(); + $class_name = '\\ElementorPro\\Modules\\DisplayConditions\\Conditions\\' . $id; + + /** @var Condition_Base $condition */ + $condition = new $class_name( $args ); + + $this->register_condition_instance( $condition ); + } + + /** + * @param Condition_Base $instance + * @return false|void + */ + public function register_condition_instance( Condition_Base $instance ) { + $id = $instance->get_name(); + $is_exist = $this->get_condition( $id ); + + if ( false !== $is_exist ) { + return false; // Already registered. + } + + $this->conditions[ $id ] = $instance; + } + + /** + * Add condition group. + * + * Register new group for the condition. + * + * @access public + * + * @param string $group_name Group name. + * @param array $group_properties Group properties. + */ + public function add_group( $group_name, $group_properties ) { + if ( null === $this->groups ) { + $this->get_groups(); + } + + if ( ! isset( $this->groups[ $group_name ] ) ) { + $this->groups[ $group_name ] = $group_properties; + } + } + + /** + * Get condition groups. + * + * Retrieve the list of groups for the conditions. + * + * @access private + * + * @return array Condition groups. + */ + private function get_groups() { + if ( null === $this->groups ) { + $this->init_groups(); + } + + return $this->groups; + } + + /** + * @param $id + * + * @return Condition_Base|bool + */ + public function get_condition( $id ) { + return isset( $this->conditions[ $id ] ) ? $this->conditions[ $id ] : false; + } + + public function get_conditions_config() { + $config = []; + + $config['conditions'] = []; + + foreach ( $this->conditions as $condition ) { + $config['conditions'][ $condition->get_name() ] = $condition->get_config(); + } + + $config['groups'] = $this->get_groups(); + $config['show_cache_notice'] = $this->display_conditions_module->get_component( 'cache_notice' )->should_show_notice(); + + return $config; + } + + public function register_conditions() { + $conditions = self::CONDITIONS; + + foreach ( $conditions as $condition ) { + $this->register_condition( $condition ); + } + + /** + * Elementor display conditions registration. + * + * Fires when a new display condition is registered. This hook allows developers + * to register new display conditions using register_condition_instance(). + * + * @param Conditions_Manager $this An instance of conditions manager. + */ + do_action( 'elementor/display_conditions/register', $this ); + } +} diff --git a/modules/display-conditions/classes/dynamic-tags/custom-fields-data-provider.php b/modules/display-conditions/classes/dynamic-tags/custom-fields-data-provider.php new file mode 100644 index 0000000..c149f7a --- /dev/null +++ b/modules/display-conditions/classes/dynamic-tags/custom-fields-data-provider.php @@ -0,0 +1,58 @@ +get_col( + $wpdb->prepare( + "SELECT DISTINCT meta_key + FROM $wpdb->postmeta + WHERE meta_key NOT BETWEEN '_' AND '_z' + HAVING meta_key NOT LIKE %s + ORDER BY meta_key + LIMIT %d", + $wpdb->esc_like( '_' ) . '%', + apply_filters( 'elementor_pro/display_conditions/dynamic_tags/custom_fields_meta_limit', static::CUSTOM_FIELDS_META_LIMIT ) + ) + ); + + if ( empty( $keys ) ) { + return []; + } + + return $this->get_control_groups() + array_combine( $keys, $keys ); + } + + /** + * @param array $args + * + * @return string | bool + */ + public function get_value( array $args ) { + if ( ! metadata_exists( 'post', get_the_ID(), $args['dynamic_tag'] ) ) { + return false; + } + + return get_post_meta( get_the_ID(), $args['dynamic_tag'], true ); + } + + private function get_control_groups(): array { + return [ + 'custom_field' => [ + 'label' => esc_html__( 'Custom Field', 'elementor-pro' ), + 'type' => 'group', + ], + ]; + } +} diff --git a/modules/display-conditions/classes/dynamic-tags/data-provider.php b/modules/display-conditions/classes/dynamic-tags/data-provider.php new file mode 100644 index 0000000..db6feba --- /dev/null +++ b/modules/display-conditions/classes/dynamic-tags/data-provider.php @@ -0,0 +1,14 @@ +get_dynamic_tags_options(); + + foreach ( $this->get_control_groups() as $group_key => $group_name ) { + $result[ $group_key ] = [ + 'label' => $group_name, + 'type' => 'group', + ]; + + $group_items = array_filter( $dynamic_tags_options, function( $item ) use ( $group_key ) { + return $group_key === $item['group']; + } ); + + $group_items = array_map( function( $item ) { + return $item['label']; + }, $group_items ); + + $result = $result + $group_items; + } + + return $result; + } + + public function get_default_control_option(): string { + return array_key_first( $this->get_dynamic_tags_options() ); + } + + /** + * @param string $key + * @return array + */ + public function get_dynamic_tag_options( string $key ): array { + $dt_config = $this->get_dynamic_tags_options(); + + return ! empty( $dt_config[ $key ] ) ? $dt_config[ $key ] : []; + } + + /** + * @return array + */ + public function get_dynamic_tags_options(): array { + if ( ! empty( $this->dynamic_tags_options ) ) { + return $this->dynamic_tags_options; + } + + $dynamic_tags_config = Plugin::elementor()->dynamic_tags->get_config(); + + foreach ( $dynamic_tags_config['tags'] as $dynamic_tag_config ) { + if ( empty( $dynamic_tag_config['display_conditions'] ) ) { + continue; + } + + $new_options = array_map( function( $item ) use ( $dynamic_tag_config ) { + return $item + [ 'dynamic_tag_name' => $dynamic_tag_config['name'] ]; + }, $dynamic_tag_config['display_conditions'] ); + + $this->dynamic_tags_options = $this->dynamic_tags_options + $new_options; + } + + return $this->dynamic_tags_options; + } + + /** + * @param array $args + * @return string | bool + */ + public function get_value( array $args ) { + $dt_options = $this->get_dynamic_tag_options( $args['dynamic_tag'] ); + + if ( empty( $dt_options ) ) { + return false; + } + + return Plugin::elementor()->dynamic_tags->get_tag_data_content( null, $dt_options['dynamic_tag_name'], $dt_options['settings'] ) ?? false; + } + + private function get_control_groups(): array { + return [ + 'archive' => esc_html__( 'Archive', 'elementor-pro' ), + 'featured_image' => esc_html__( 'Featured Image', 'elementor-pro' ), + 'author' => esc_html__( 'Author', 'elementor-pro' ), + ]; + } +} diff --git a/modules/display-conditions/classes/experiments.php b/modules/display-conditions/classes/experiments.php new file mode 100644 index 0000000..0d02b9f --- /dev/null +++ b/modules/display-conditions/classes/experiments.php @@ -0,0 +1,30 @@ +experiments->add_feature( [ + 'name' => Display_Conditions_Module::LICENSE_FEATURE_NAME, + 'title' => esc_html__( 'Display Conditions', 'elementor-pro' ), + 'description' => sprintf( + /* translators: 1: opening link tag, 2: closing link tag, 3: line break, 4: opening span tag, 5: closing span tag. */ + esc_html__( 'Define one or multiple conditions per widget, controlling when they\'re visible. Widgets will only appear on the front end if these conditions are met. It\'s ideal for showing content to specific audiences based on time, date, user role, and more. %1$sLearn More%2$s%3$s%4$sRequires: Elementor version 3.19%5$s', 'elementor-pro' ), + '', + '', + '
    ', + '', + '', + ), + 'release_status' => Manager::RELEASE_STATUS_BETA, + 'default' => Manager::STATE_INACTIVE, + 'new_site' => [ + 'default_active' => true, + 'minimum_installation_version' => '3.20', + ], + ] ); + } +} diff --git a/modules/display-conditions/classes/or-condition.php b/modules/display-conditions/classes/or-condition.php new file mode 100644 index 0000000..0f0072a --- /dev/null +++ b/modules/display-conditions/classes/or-condition.php @@ -0,0 +1,40 @@ +conditions_manager = $conditions_manager; + $this->set_and_conditions( $sets ); + } + + public function check() { + if ( empty( $this->and_conditions ) ) { + return true; + } + + foreach ( $this->and_conditions as $condition ) { + if ( $condition->check() ) { + return true; + } + } + + return false; + } + + private function set_and_conditions( $groups ) { + $this->and_conditions = array_map( function ( $condition ) { + return new And_Condition( $this->conditions_manager, $condition ); + }, $groups ); + } +} diff --git a/modules/display-conditions/conditions/archive-of-author-condition.php b/modules/display-conditions/conditions/archive-of-author-condition.php new file mode 100644 index 0000000..4364b61 --- /dev/null +++ b/modules/display-conditions/conditions/archive-of-author-condition.php @@ -0,0 +1,77 @@ +wordpress_adapter->is_author( $author_ids ); + + case Comparator_Provider::COMPARATOR_IS_NOT: + return ! $this->wordpress_adapter->is_author( $author_ids ); + + default: + return false; + } + + } + + public function get_options() { + $comparators = Comparator_Provider::get_comparators( [ + Comparator_Provider::COMPARATOR_IS, + Comparator_Provider::COMPARATOR_IS_NOT, + ] ); + + $this->add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'authors', + [ + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_AUTHOR, + ], + 'multiple' => true, + 'required' => true, + 'placeholder' => esc_html__( 'Type to search', 'elementor-pro' ), + ] + ); + } +} diff --git a/modules/display-conditions/conditions/archive-of-category-condition.php b/modules/display-conditions/conditions/archive-of-category-condition.php new file mode 100644 index 0000000..4c46b7a --- /dev/null +++ b/modules/display-conditions/conditions/archive-of-category-condition.php @@ -0,0 +1,36 @@ +condition_key = $condition_key; + + parent::__construct(); + } + + abstract public function get_name(); + + abstract public function get_label(); + + abstract protected function get_taxonomy(); + + public function get_group(): string { + return 'archive'; + } + + abstract protected function is_of_taxonomy( $args ): bool; + + protected function check_is_of_taxonomy( $args ) { + switch ( $args['comparator'] ) { + case Comparator_Provider::COMPARATOR_IS: + return $this->is_of_taxonomy( $args ); + case Comparator_Provider::COMPARATOR_IS_NOT: + return ! $this->is_of_taxonomy( $args ); + default: + return false; + } + } + + public function get_options() { + $comparators = Comparator_Provider::get_comparators( [ + Comparator_Provider::COMPARATOR_IS, + Comparator_Provider::COMPARATOR_IS_NOT, + ] ); + $taxonomy = $this->get_taxonomy(); + + $this->add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + $this->condition_key, + [ + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_TAX, + 'query' => [ + 'taxonomy' => $taxonomy, + ], + ], + 'multiple' => true, + 'required' => true, + 'placeholder' => esc_html__( 'Type to search', 'elementor-pro' ), + ] + ); + } +} diff --git a/modules/display-conditions/conditions/base/condition-base.php b/modules/display-conditions/conditions/base/condition-base.php new file mode 100644 index 0000000..a4b11d0 --- /dev/null +++ b/modules/display-conditions/conditions/base/condition-base.php @@ -0,0 +1,53 @@ +wordpress_adapter = array_pop( $data ); + parent::__construct( $data ); + } + + abstract public function get_label(); + + abstract public function get_options(); + + public function get_group() { + return 'other'; + } + + public function check( $args ) : bool { + return true; + } + + protected function register_controls() { + $this->start_controls_section( + '__settings' + ); + + $this->get_options(); + + $this->end_controls_section(); + } + + protected function get_initial_config() { + $config = parent::get_initial_config(); + + $config['label'] = $this->get_label(); + $config['group'] = $this->get_group(); + + return $config; + } +} diff --git a/modules/display-conditions/conditions/base/date-condition-base.php b/modules/display-conditions/conditions/base/date-condition-base.php new file mode 100644 index 0000000..1e19838 --- /dev/null +++ b/modules/display-conditions/conditions/base/date-condition-base.php @@ -0,0 +1,94 @@ +condition_key = $condition_key; + $this->group_key = $group_key; + } + + public function get_group() { + return $this->group_key; + } + + /** + * @return array + */ + public static function get_time_options(): array { + return [ + self::OPTION_SERVER => esc_html__( 'Server Time', 'elementor-pro' ), + self::OPTION_CLIENT => esc_html__( 'Visitor Time', 'elementor-pro' ), + ]; + } + + protected function check_date( $args, $date_to_check ): bool { + $comparator = $args[ self::COMPARATOR_KEY ]; + $date_string = $args[ $this->condition_key ]; + $set_date = date_create_from_format( self::DATE_FORMAT, $date_string ); + + if ( ! $set_date || ! $date_to_check || ! $comparator || $set_date->format( self::DATE_FORMAT ) !== $date_string ) { + return false; + } + + return Comparators_Checker::check_date_time( $comparator, $date_to_check, $set_date ); + } + + public function get_options() { + $this->add_control( + self::COMPARATOR_KEY, + [ + 'type' => Controls_Manager::SELECT, + 'options' => Comparator_Provider::get_comparators( + [ + Comparator_Provider::COMPARATOR_IS, + Comparator_Provider::COMPARATOR_IS_NOT, + Comparator_Provider::COMPARATOR_IS_BEFORE, + Comparator_Provider::COMPARATOR_IS_AFTER, + Comparator_Provider::COMPARATOR_IS_BEFORE_INCLUSIVE, + Comparator_Provider::COMPARATOR_IS_AFTER_INCLUSIVE, + ] + ), + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + $this->condition_key, + [ + 'type' => Controls_Manager::DATE_TIME, + 'label' => $this::get_label(), + 'variant' => 'date', + 'required' => true, + ] + ); + + $this->add_control( + self::OPTION_KEY, + [ + 'type' => Controls_Manager::SELECT, + 'options' => self::get_time_options(), + 'default' => self::OPTION_SERVER, + 'disabled_options' => [ self::OPTION_CLIENT ], + ] + ); + } +} diff --git a/modules/display-conditions/conditions/base/title-condition-base.php b/modules/display-conditions/conditions/base/title-condition-base.php new file mode 100644 index 0000000..4351afe --- /dev/null +++ b/modules/display-conditions/conditions/base/title-condition-base.php @@ -0,0 +1,52 @@ +add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'titles', + [ + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_POST, + 'query' => $this->get_query(), + ], + 'multiple' => true, + 'placeholder' => esc_html__( 'Type to search', 'elementor-pro' ), + 'required' => true, + ] + ); + } +} diff --git a/modules/display-conditions/conditions/current-date-condition.php b/modules/display-conditions/conditions/current-date-condition.php new file mode 100644 index 0000000..a8a202a --- /dev/null +++ b/modules/display-conditions/conditions/current-date-condition.php @@ -0,0 +1,37 @@ +get_current_date() ); + } + + /** + * @return \DateTime|false + */ + private function get_current_date() { + return date_create_from_format( self::DATE_FORMAT, gmdate( self::DATE_FORMAT ) ); + } +} diff --git a/modules/display-conditions/conditions/date-of-modification-condition.php b/modules/display-conditions/conditions/date-of-modification-condition.php new file mode 100644 index 0000000..6ee9176 --- /dev/null +++ b/modules/display-conditions/conditions/date-of-modification-condition.php @@ -0,0 +1,41 @@ +get_modification_date() ); + } + + private function get_modification_date() { + return date_create_from_format( + self::DATE_FORMAT, + get_the_modified_date( self::DATE_FORMAT, get_the_ID() ) + ); + } +} diff --git a/modules/display-conditions/conditions/date-of-publish-condition.php b/modules/display-conditions/conditions/date-of-publish-condition.php new file mode 100644 index 0000000..04d540c --- /dev/null +++ b/modules/display-conditions/conditions/date-of-publish-condition.php @@ -0,0 +1,40 @@ +get_post_date() ); + } + + private function get_post_date() { + return date_create_from_format( + self::DATE_FORMAT, + get_the_date( self::DATE_FORMAT, get_the_ID() ) + ); + } +} diff --git a/modules/display-conditions/conditions/day-of-the-week-condition.php b/modules/display-conditions/conditions/day-of-the-week-condition.php new file mode 100644 index 0000000..4a0813a --- /dev/null +++ b/modules/display-conditions/conditions/day-of-the-week-condition.php @@ -0,0 +1,78 @@ +add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS_ONE_OF, + ] + ); + + $this->add_control( + self::CONDITION_KEY, + [ + 'type' => Controls_Manager::SELECT2, + 'options' => [ + 'monday' => esc_html__( 'Monday', 'elementor-pro' ), + 'tuesday' => esc_html__( 'Tuesday', 'elementor-pro' ), + 'wednesday' => esc_html__( 'Wednesday', 'elementor-pro' ), + 'thursday' => esc_html__( 'Thursday', 'elementor-pro' ), + 'friday' => esc_html__( 'Friday', 'elementor-pro' ), + 'saturday' => esc_html__( 'Saturday', 'elementor-pro' ), + 'sunday' => esc_html__( 'Sunday', 'elementor-pro' ), + ], + 'multiple' => true, + 'required' => true, + ] + ); + + $this->add_control( + 'time_type', + [ + 'type' => Controls_Manager::SELECT, + 'options' => Date_Condition_Base::get_time_options(), + 'default' => Date_Condition_Base::OPTION_SERVER, + 'disabled_options' => [ Date_Condition_Base::OPTION_CLIENT ], + ] + ); + } +} diff --git a/modules/display-conditions/conditions/dynamic-tags-condition.php b/modules/display-conditions/conditions/dynamic-tags-condition.php new file mode 100644 index 0000000..22991c9 --- /dev/null +++ b/modules/display-conditions/conditions/dynamic-tags-condition.php @@ -0,0 +1,110 @@ +dynamic_tags_data_provider = new Dynamic_Tags_Data_Provider(); + $this->custom_fields_data_provider = new Custom_Fields_Data_Provider(); + } + + public function get_name() { + return 'dynamic_tags'; + } + + public function get_label() { + return esc_html__( 'Dynamic Tags', 'elementor-pro' ); + } + + public function get_group() { + return 'other'; + } + + public function check( $args ) : bool { + $value = $this->get_condition_value( $args ); + + if ( false === $value ) { + return false; + } + + return Comparators_Checker::check_string_contains_and_empty( $args['comparator'], $args['dynamic_tag_value'], $value ); + } + + public function get_options() { + $this->add_control( + 'dynamic_tag', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $this->dynamic_tags_data_provider->get_control_options() + $this->custom_fields_data_provider->get_control_options(), + 'default' => $this->dynamic_tags_data_provider->get_default_control_option(), + 'disabled_options' => ! current_user_can( 'manage_options' ) ? [ 'author_info_email' ] : [], + 'disabled_type' => 'hidden', + ] + ); + + $this->add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => Comparator_Provider::get_comparators( + [ + Comparator_Provider::COMPARATOR_IS, + Comparator_Provider::COMPARATOR_IS_NOT, + Comparator_Provider::COMPARATOR_CONTAINS, + Comparator_Provider::COMPARATOR_NOT_CONTAIN, + Comparator_Provider::COMPARATOR_IS_EMPTY, + Comparator_Provider::COMPARATOR_IS_NOT_EMPTY, + ] + ), + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'dynamic_tag_value', + [ + 'placeholder' => esc_html__( 'Type a value', 'elementor-pro' ), + 'required' => true, + ] + ); + } + + /** + * Conditionally retrieve the value of a dynamic tag or custom field. + * + * @return string | bool + */ + private function get_condition_value( array $args ) { + $dt_value = $this->dynamic_tags_data_provider->get_value( $args ); + + if ( $dt_value ) { + return $dt_value; + } + + return $this->custom_fields_data_provider->get_value( $args ); + } +} diff --git a/modules/display-conditions/conditions/featured-image-condition.php b/modules/display-conditions/conditions/featured-image-condition.php new file mode 100644 index 0000000..3ef2838 --- /dev/null +++ b/modules/display-conditions/conditions/featured-image-condition.php @@ -0,0 +1,63 @@ +wordpress_adapter->has_post_thumbnail() + ); + } + + public function get_options() { + $comparators = Comparator_Provider::get_comparators( + [ + Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'status', + [ + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'set' => esc_html__( 'Set', 'elementor-pro' ), + 'not_set' => esc_html__( 'Not Set', 'elementor-pro' ), + ], + 'default' => 'set', + ] + ); + } +} diff --git a/modules/display-conditions/conditions/from-url-condition.php b/modules/display-conditions/conditions/from-url-condition.php new file mode 100644 index 0000000..b08215e --- /dev/null +++ b/modules/display-conditions/conditions/from-url-condition.php @@ -0,0 +1,62 @@ +add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'from_url', + [ + 'label' => esc_html__( 'URL', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'URL', 'elementor-pro' ), + 'required' => true, + ] + ); + } + + public function get_name() { + return 'from_url'; + } + + public function wp_get_referer() { + return wp_get_raw_referer(); + } + + public function check( $args ) : bool { + $referrer = $this->wp_get_referer(); + + return Comparators_Checker::check_string_contains( $args['comparator'], $args['from_url'], $referrer ); + } +} diff --git a/modules/display-conditions/conditions/in-categories-condition.php b/modules/display-conditions/conditions/in-categories-condition.php new file mode 100644 index 0000000..815fb61 --- /dev/null +++ b/modules/display-conditions/conditions/in-categories-condition.php @@ -0,0 +1,77 @@ + 'ids' ] ) ?? []; + + if ( empty( $post_categories ) ) { + return ! ( Comparator_Provider::COMPARATOR_IS === $args['comparator'] ); + } + + $category_ids = array_column( $args['categories'], 'id' ); + + return Comparators_Checker::check_array_contains( $args['comparator'], $category_ids, $post_categories ); + } + + public function get_options() { + $comparators = Comparator_Provider::get_comparators( + [ + Comparator_Provider::COMPARATOR_IS, + Comparator_Provider::COMPARATOR_IS_NOT, + ] + ); + + $this->add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'categories', + [ + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_TAX, + 'query' => [ + 'taxonomy' => 'category', + ], + ], + 'multiple' => true, + 'placeholder' => esc_html__( 'Type to search', 'elementor-pro' ), + 'required' => true, + ] + ); + } +} diff --git a/modules/display-conditions/conditions/in-tags-condition.php b/modules/display-conditions/conditions/in-tags-condition.php new file mode 100644 index 0000000..c898fae --- /dev/null +++ b/modules/display-conditions/conditions/in-tags-condition.php @@ -0,0 +1,73 @@ + 'ids' ] ) ?? []; + + $tag_ids = array_column( $args['tags'], 'id' ); + + return Comparators_Checker::check_array_contains( $args['comparator'], $tag_ids, $post_tags ); + } + + public function get_options() { + $comparators = Comparator_Provider::get_comparators( + [ + Comparator_Provider::COMPARATOR_IS, + Comparator_Provider::COMPARATOR_IS_NOT, + ] + ); + + $this->add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'tags', + [ + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_TAX, + 'query' => [ + 'taxonomy' => 'post_tag', + ], + ], + 'multiple' => true, + 'placeholder' => esc_html__( 'Type to search', 'elementor-pro' ), + 'required' => true, + ] + ); + } +} diff --git a/modules/display-conditions/conditions/login-status-condition.php b/modules/display-conditions/conditions/login-status-condition.php new file mode 100644 index 0000000..b76ff1e --- /dev/null +++ b/modules/display-conditions/conditions/login-status-condition.php @@ -0,0 +1,59 @@ +add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'status', + [ + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'logged_in' => esc_html__( 'Logged In', 'elementor-pro' ), + 'logged_out' => esc_html__( 'Logged Out', 'elementor-pro' ), + ], + 'default' => 'logged_in', + ] + ); + } +} diff --git a/modules/display-conditions/conditions/page-author-condition.php b/modules/display-conditions/conditions/page-author-condition.php new file mode 100644 index 0000000..724be88 --- /dev/null +++ b/modules/display-conditions/conditions/page-author-condition.php @@ -0,0 +1,76 @@ +post_author ], $author_ids ); + + } + + public function get_options() { + $comparators = Comparator_Provider::get_comparators( + [ + Comparator_Provider::COMPARATOR_IS, + Comparator_Provider::COMPARATOR_IS_NOT, + ] + ); + + $this->add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'authors', + [ + 'label' => esc_html__( 'Author', 'elementor-pro' ), + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_AUTHOR, + ], + 'multiple' => true, + 'placeholder' => esc_html__( 'Type to search', 'elementor-pro' ), + 'required' => true, + ] + ); + } +} diff --git a/modules/display-conditions/conditions/page-parent-condition.php b/modules/display-conditions/conditions/page-parent-condition.php new file mode 100644 index 0000000..f71da32 --- /dev/null +++ b/modules/display-conditions/conditions/page-parent-condition.php @@ -0,0 +1,74 @@ +post_parent ], $parent_page_ids ); + } + + public function get_options() { + $comparators = Comparator_Provider::get_comparators( + [ + Comparator_Provider::COMPARATOR_IS_ONE_OF, + Comparator_Provider::COMPARATOR_IS_NONE_OF, + ] + ); + + $this->add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS_ONE_OF, + ] + ); + + $this->add_control( + 'pages', + [ + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_POST, + 'query' => [ + 'post_status' => 'publish', + 'post_type' => 'page', + ], + ], + 'multiple' => true, + 'placeholder' => esc_html__( 'Type to search', 'elementor-pro' ), + 'required' => true, + ] + ); + } +} diff --git a/modules/display-conditions/conditions/page-title-condition.php b/modules/display-conditions/conditions/page-title-condition.php new file mode 100644 index 0000000..ebc632a --- /dev/null +++ b/modules/display-conditions/conditions/page-title-condition.php @@ -0,0 +1,27 @@ + 'publish', + 'post_type' => 'page', + ]; + } +} diff --git a/modules/display-conditions/conditions/post-author-condition.php b/modules/display-conditions/conditions/post-author-condition.php new file mode 100644 index 0000000..c9f04f6 --- /dev/null +++ b/modules/display-conditions/conditions/post-author-condition.php @@ -0,0 +1,13 @@ +wordpress_adapter->get_comments_number(); + + return Comparators_Checker::check_numeric_constraints( $args['comparator'], (int) $args['number_of_comments'], (int) $actual_number_of_comments ); + } + + public function get_options() { + $comparators = Comparator_Provider::get_comparators( + [ + Comparator_Provider::COMPARATOR_IS_GREATER_THAN_INCLUSIVE, + Comparator_Provider::COMPARATOR_IS_LESS_THAN_INCLUSIVE, + Comparator_Provider::COMPARATOR_IS, + Comparator_Provider::COMPARATOR_IS_NOT, + ] + ); + + $this->add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'number_of_comments', + [ + 'type' => Controls_Manager::TEXT, + 'input_type' => 'number', + 'variant' => 'number', + 'placeholder' => 'Type a number...', + 'step' => 1, + 'min' => 0, + 'default' => 1, + ] + ); + } +} diff --git a/modules/display-conditions/conditions/post-title-condition.php b/modules/display-conditions/conditions/post-title-condition.php new file mode 100644 index 0000000..9839cfb --- /dev/null +++ b/modules/display-conditions/conditions/post-title-condition.php @@ -0,0 +1,26 @@ + 'publish', + 'post_type' => 'post', + ]; + } +} diff --git a/modules/display-conditions/conditions/time-of-the-day-condition.php b/modules/display-conditions/conditions/time-of-the-day-condition.php new file mode 100644 index 0000000..557a0e2 --- /dev/null +++ b/modules/display-conditions/conditions/time-of-the-day-condition.php @@ -0,0 +1,111 @@ +get_gm_date(); + $expected_time = $this->convert_date_time_to_24_hour_format( $args['time'] ); + + return Comparators_Checker::check_date_time( $args['comparator'], $time_now, $expected_time ); + + } + + /** + * @param $date_time_string + * @return string + * @throws \Exception + */ + public function convert_date_time_to_24_hour_format( $date_time_string ): string { + + if ( ! $this->is_valid_date_time_string( $date_time_string ) ) { + return ''; + } + + $date_time = DateTime::createFromFormat( 'm-d-Y H:i', $date_time_string ); + + return $date_time->format( 'H:i' ); + } + + private function is_valid_date_time_string( $date_time_string ): bool { + $date_time = DateTime::createFromFormat( 'm-d-Y H:i', $date_time_string ); + + if ( ! $date_time || $date_time->format( 'm-d-Y H:i' ) !== $date_time_string ) { + return false; + } + + return true; + } + + public function get_options() { + $comparators = Comparator_Provider::get_comparators( + [ + Comparator_Provider::COMPARATOR_IS, + Comparator_Provider::COMPARATOR_IS_NOT, + Comparator_Provider::COMPARATOR_IS_BEFORE, + Comparator_Provider::COMPARATOR_IS_AFTER, + Comparator_Provider::COMPARATOR_IS_BEFORE_INCLUSIVE, + Comparator_Provider::COMPARATOR_IS_AFTER_INCLUSIVE, + ] + ); + + $this->add_control( + 'comparator', + [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS, + ] + ); + + $this->add_control( + 'time', + [ + 'type' => Controls_Manager::DATE_TIME, + 'variant' => 'time', + 'required' => true, + ] + ); + + $this->add_control( + 'time_type', + [ + 'type' => Controls_Manager::SELECT, + 'options' => Date_Condition_Base::get_time_options(), + 'default' => Date_Condition_Base::OPTION_SERVER, + 'disabled_options' => Date_Condition_Base::OPTION_CLIENT, + ] + ); + } +} diff --git a/modules/display-conditions/conditions/user-registration-date-condition.php b/modules/display-conditions/conditions/user-registration-date-condition.php new file mode 100644 index 0000000..24d5870 --- /dev/null +++ b/modules/display-conditions/conditions/user-registration-date-condition.php @@ -0,0 +1,40 @@ +get_user_registration_date() ); + } + + private function get_user_registration_date() { + $registration_date = date_create( wp_get_current_user()->user_registered )->format( self::DATE_FORMAT ); + $registration_date = date_create_from_format( self::DATE_FORMAT, $registration_date ); + + return $registration_date; + } +} diff --git a/modules/display-conditions/conditions/user-role-condition.php b/modules/display-conditions/conditions/user-role-condition.php new file mode 100644 index 0000000..e76b07d --- /dev/null +++ b/modules/display-conditions/conditions/user-role-condition.php @@ -0,0 +1,70 @@ +roles : []; + + return Comparators_Checker::check_array_contains( $args['comparator'], $current_user_roles, $args['roles'] ); + } + + public function get_options() { + $user_roles = $this->get_available_roles(); + + $comparators = Comparator_Provider::get_comparators( + [ + Comparator_Provider::COMPARATOR_IS_ONE_OF, + Comparator_Provider::COMPARATOR_IS_NONE_OF, + ] + ); + + $this->add_control( 'comparator', [ + 'type' => Controls_Manager::SELECT, + 'options' => $comparators, + 'default' => Comparator_Provider::COMPARATOR_IS_ONE_OF, + ] ); + + $this->add_control( self::CONDITION_KEY, [ + 'type' => Controls_Manager::SELECT2, + 'options' => $user_roles, + 'multiple' => true, + 'required' => true, + 'default' => [], + ] ); + } + + private function get_available_roles(): array { + $user_roles = []; + + foreach ( get_editable_roles() as $role_slug => $role_data ) { + $role = $role_data['name']; + $user_roles[ $role_slug ] = esc_html( $role ); + } + + return $user_roles; + } +} diff --git a/modules/display-conditions/module.php b/modules/display-conditions/module.php new file mode 100644 index 0000000..16baa6c --- /dev/null +++ b/modules/display-conditions/module.php @@ -0,0 +1,264 @@ +add_common_actions(); + return; + } + + $this->register_display_conditions_experiments(); + $this->maybe_add_actions_and_components(); + } + + public static function is_experiment_active(): bool { + return Plugin::elementor()::$instance->experiments->is_feature_active( self::LICENSE_FEATURE_NAME ); + } + + public static function should_show_promo(): bool { + return ! self::can_use_display_conditions(); + } + + private function add_actions() { + $this->add_render_actions(); + + add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); + } + + private function add_components() { + $this->add_component( 'conditions', new Classes\Conditions_Manager( $this ) ); + $this->add_component( 'cache_notice', new Classes\Cache_Notice() ); + } + + private function add_common_actions() { + $this->add_advanced_tab_actions(); + + add_action( 'elementor/editor/before_enqueue_scripts', function() { + $this->enqueue_main_script(); + } ); + } + + private function enqueue_main_script() { + $min_suffix = Utils::is_script_debug() ? '' : '.min'; + + wp_enqueue_script( + 'e-display-conditions', + ELEMENTOR_PRO_ASSETS_URL . 'js/display-conditions' . $min_suffix . '.js', + [ + 'react', + 'react-dom', + 'backbone-marionette', + 'elementor-web-cli', + 'wp-date', + 'elementor-common', + 'elementor-editor-modules', + 'elementor-editor-document', + 'elementor-v2-ui', + 'elementor-v2-icons', + ], + ELEMENTOR_PRO_VERSION, + true + ); + + wp_set_script_translations( 'e-display-conditions', 'elementor-pro' ); + } + + private function add_advanced_tab_actions() { + $hooks = array( + 'elementor/element/section/section_advanced/after_section_end' => 'css_classes', // Sections + 'elementor/element/column/section_advanced/after_section_end' => 'css_classes', // Columns + 'elementor/element/common/_section_style/after_section_end' => '_css_classes', // Widgets + 'elementor/element/container/section_layout/after_section_end' => 'css_classes', // Containers + ); + + foreach ( $hooks as $hook => $injection_position ) { + add_action( + $hook, + function( $element, $args ) use ( $injection_position ) { + $this->add_control_to_advanced_tab( $element, $args, $injection_position ); + }, + 10, + 2 + ); + } + } + + protected function add_render_actions() { + $element_types = array( + 'section', + 'column', + 'widget', + 'container', + ); + + foreach ( $element_types as $el ) { + add_action( 'elementor/frontend/' . $el . '/before_render', array( $this, 'before_element_render' ) ); + add_action( 'elementor/frontend/' . $el . '/after_render', array( $this, 'after_element_render' ) ); + } + } + + private function add_control_to_advanced_tab( $element, $args, $injection_position ) { + $element->start_injection( + array( + 'of' => $injection_position, + ) + ); + + $element->add_control( + 'e_display_conditions_trigger', + array( + 'type' => Controls_Manager::RAW_HTML, + 'separator' => 'before', + 'raw' => $this->get_display_conditions_control_template(), + ) + ); + + $element->add_control( + 'e_display_conditions', + array( + 'type' => Controls_Manager::HIDDEN, + ) + ); + + $element->end_injection(); + } + + private function get_display_conditions_control_template() { + $icon_class = 'e-control-display-conditions'; + $show_promo = self::should_show_promo(); + + if ( $show_promo ) { + $icon_class .= '-promo'; + } + + ob_start(); + ?> +
    + + + + + + + +
    + get_settings_for_display(); + $is_visible = true; + $saved_conditions = $this->get_saved_conditions( $settings ); + + if ( empty( $settings['e_display_conditions'] ) || Plugin::elementor()->editor->is_edit_mode() || empty( $saved_conditions ) ) { + return $is_visible; + } + + $saved_conditions = $this->get_converted_conditions( $saved_conditions ); + $saved_conditions = new Or_Condition( $this->get_conditions_manager(), $saved_conditions ); + $is_visible = $saved_conditions->check(); + + if ( ! $is_visible ) { + add_filter( 'elementor/element/get_child_type', '__return_false' ); // Prevent getting content of inner elements. + add_filter( 'elementor/frontend/' . $element->get_type() . '/should_render', '__return_false' ); + + $this->hidden_elements_ids[] = $element->get_id(); + } + } + + public function after_element_render( $element ) { + if ( ! in_array( $element->get_id(), $this->hidden_elements_ids, true ) ) { + return; + } + + remove_filter( 'elementor/element/get_child_type', '__return_false' ); + remove_filter( 'elementor/frontend/' . $element->get_type() . '/should_render', '__return_false' ); + } + + public function register_display_conditions_experiments() { + if ( ! self::can_use_display_conditions() ) { + return; + } + + Experiments::register_dc_experiment(); + } + + /** + * @return string + */ + public function get_name() { + return static::LICENSE_FEATURE_NAME; + } + + /** + * @return Classes\Conditions_Manager + */ + public function get_conditions_manager() { + return $this->get_component( 'conditions' ); + } + + /** + * @param Ajax $ajax_manager + */ + public function register_ajax_actions( $ajax_manager ) { + $ajax_manager->register_ajax_action( 'display_conditions_set_cache_notice_status', [ $this->get_component( 'cache_notice' ), 'set_notice_status' ] ); + } + + /** + * @return bool + */ + public static function can_use_display_conditions(): bool { + return API::is_license_active() && API::is_licence_has_feature( self::LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ); + } + + /** + * @return void + */ + private function maybe_add_actions_and_components(): void { + if ( self::is_experiment_active() ) { + $this->add_common_actions(); + $this->add_actions(); + $this->add_components(); + } + } + + private function get_converted_conditions( $conditions ) { + foreach ( $conditions as $condition ) { + if ( ! isset( $condition['condition'] ) ) { + return $conditions; + } + } + return count( $conditions ) + ? [ $conditions ] + : []; + } +} diff --git a/modules/dynamic-tags/acf/dynamic-value-provider.php b/modules/dynamic-tags/acf/dynamic-value-provider.php new file mode 100644 index 0000000..456d626 --- /dev/null +++ b/modules/dynamic-tags/acf/dynamic-value-provider.php @@ -0,0 +1,63 @@ +get_taxonomy_field_data( $field_key, $meta_key ); + } + + $document = Plugin::elementor()->documents->get_current(); + + if ( 'options' === $field_key ) { + $field = $this->get_field_object( $meta_key, $field_key ); + } elseif ( ! empty( $document ) && LoopBuilderModule::TEMPLATE_LIBRARY_TYPE_SLUG === $document::get_type() ) { + $field = $this->get_field_object( $field_key, get_the_ID() ); + } else { + $field = $this->get_field_object( $field_key, get_queried_object() ); + } + + return [ $field, $meta_key ]; + } + + /** + * Retrieve the custom field value from `ACF` plugin. + * Used for testing. + * + * @param $selector + * @param $post_id + * + * @return array|false + */ + protected function get_field_object( $selector, $post_id ) { + return get_field_object( $selector, $post_id ); + } + + /** + * Get the field data needed when rendering a dynamic tag for a taxonomy object. + * @param $field_key + * @param $meta_key + * + * @return array + */ + private function get_taxonomy_field_data( $field_key, $meta_key ) { + global $wp_query; + $field = $this->get_field_object( $field_key, $wp_query->loop_term ); + return [ $field, $meta_key ]; + } +} diff --git a/modules/dynamic-tags/acf/module.php b/modules/dynamic-tags/acf/module.php new file mode 100644 index 0000000..39ec367 --- /dev/null +++ b/modules/dynamic-tags/acf/module.php @@ -0,0 +1,157 @@ += 5.0.0 + if ( function_exists( 'acf_get_field_groups' ) ) { + $acf_groups = acf_get_field_groups(); + } else { + $acf_groups = apply_filters( 'acf/get_field_groups', [] ); + } + + $groups = []; + + $options_page_groups_ids = []; + + if ( function_exists( 'acf_options_page' ) ) { + $pages = acf_options_page()->get_pages(); + foreach ( $pages as $slug => $page ) { + $options_page_groups = acf_get_field_groups( [ + 'options_page' => $slug, + ] ); + + foreach ( $options_page_groups as $options_page_group ) { + $options_page_groups_ids[] = $options_page_group['ID']; + } + } + } + + foreach ( $acf_groups as $acf_group ) { + // ACF >= 5.0.0 + if ( function_exists( 'acf_get_fields' ) ) { + if ( isset( $acf_group['ID'] ) && ! empty( $acf_group['ID'] ) ) { + $fields = acf_get_fields( $acf_group['ID'] ); + } else { + $fields = acf_get_fields( $acf_group ); + } + } else { + $fields = apply_filters( 'acf/field_group/get_fields', [], $acf_group['id'] ); + } + + $options = []; + + if ( ! is_array( $fields ) ) { + continue; + } + + $has_option_page_location = in_array( $acf_group['ID'], $options_page_groups_ids, true ); + $is_only_options_page = $has_option_page_location && 1 === count( $acf_group['location'] ); + + foreach ( $fields as $field ) { + if ( ! in_array( $field['type'], $types, true ) ) { + continue; + } + + // Use group ID for unique keys + if ( $has_option_page_location ) { + $key = 'options:' . $field['name']; + $options[ $key ] = esc_html__( 'Options', 'elementor-pro' ) . ':' . $field['label']; + if ( $is_only_options_page ) { + continue; + } + } + + $key = $field['key'] . ':' . $field['name']; + $options[ $key ] = $field['label']; + } + + if ( empty( $options ) ) { + continue; + } + + if ( 1 === count( $options ) ) { + $options = [ -1 => ' -- ' ] + $options; + } + + $groups[] = [ + 'label' => $acf_group['title'], + 'options' => $options, + ]; + } // End foreach(). + + return $groups; + } + + public static function add_key_control( Base_Tag $tag ) { + $tag->add_control( + 'key', + [ + 'label' => esc_html__( 'Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => self::get_control_options( $tag->get_supported_fields() ), + ] + ); + } + + public function get_tag_classes_names() { + return [ + 'ACF_Text', + 'ACF_Image', + 'ACF_URL', + 'ACF_Gallery', + 'ACF_File', + 'ACF_Number', + 'ACF_Color', + 'ACF_Date_Time', + ]; + } + + // For use by ACF tags + public static function get_tag_value_field( Base_Tag $tag ) { + $key = $tag->get_settings( 'key' ); + + // TODO: The tags should use the `Dynamic_Value_Provider::get_value()` method, but it involves + // heavily refactoring them, so currently this method is just a proxy and also kept for BC. + + if ( ! static::$dynamic_value_provider ) { + static::$dynamic_value_provider = new Dynamic_Value_Provider(); + } + + return static::$dynamic_value_provider->get_value( $key ); + } + + public function get_groups() { + return [ + self::ACF_GROUP => [ + 'title' => esc_html__( 'ACF', 'elementor-pro' ), + ], + ]; + } +} diff --git a/modules/dynamic-tags/acf/tags/acf-color.php b/modules/dynamic-tags/acf/tags/acf-color.php new file mode 100644 index 0000000..b25fd66 --- /dev/null +++ b/modules/dynamic-tags/acf/tags/acf-color.php @@ -0,0 +1,59 @@ +get_settings( 'fallback' ) ) { + $value = $this->get_settings( 'fallback' ); + } + + return $value; + } + + protected function register_controls() { + Module::add_key_control( $this ); + } + + public function get_supported_fields() { + return [ + 'color_picker', + ]; + } +} diff --git a/modules/dynamic-tags/acf/tags/acf-date-time.php b/modules/dynamic-tags/acf/tags/acf-date-time.php new file mode 100644 index 0000000..8a138eb --- /dev/null +++ b/modules/dynamic-tags/acf/tags/acf-date-time.php @@ -0,0 +1,97 @@ +dynamic_value_provider->get_value( + $this->get_settings( 'key' ) + ); + + if ( empty( $field_settings ) ) { + return ''; + } + + $field = $field_settings[0]; + $value = ''; + + if ( $field ) { + $date_time = \DateTime::createFromFormat( $field['return_format'], $field['value'] ); + + $value = $date_time instanceof \DateTime + ? $date_time->format( 'Y-m-d H:i:s' ) + : ''; + } + + if ( empty( $value ) && $this->get_settings( 'fallback' ) ) { + $value = $this->get_settings( 'fallback' ); + } + + return wp_kses_post( $value ); + } + + public function get_panel_template_setting_key() { + return 'key'; + } + + protected function register_controls() { + Module::add_key_control( $this ); + + $this->add_control( + 'fallback', + [ + 'type' => Controls_Manager::DATE_TIME, + 'label' => esc_html__( 'Fallback', 'elementor-pro' ), + ] + ); + } + + public function get_supported_fields() { + return [ + 'date_time_picker', + ]; + } + + public function __construct( array $data = [], $dynamic_value_provider = null ) { + parent::__construct( $data ); + + $this->dynamic_value_provider = $dynamic_value_provider ?? new Dynamic_Value_Provider(); + } +} diff --git a/modules/dynamic-tags/acf/tags/acf-file.php b/modules/dynamic-tags/acf/tags/acf-file.php new file mode 100644 index 0000000..fcba6ac --- /dev/null +++ b/modules/dynamic-tags/acf/tags/acf-file.php @@ -0,0 +1,31 @@ + $image['ID'], + ]; + } + } + + return $images; + } + + protected function register_controls() { + Module::add_key_control( $this ); + } + + public function get_supported_fields() { + return [ + 'gallery', + ]; + } +} diff --git a/modules/dynamic-tags/acf/tags/acf-image.php b/modules/dynamic-tags/acf/tags/acf-image.php new file mode 100644 index 0000000..1cba3ec --- /dev/null +++ b/modules/dynamic-tags/acf/tags/acf-image.php @@ -0,0 +1,99 @@ + null, + 'url' => '', + ]; + + list( $field, $meta_key ) = Module::get_tag_value_field( $this ); + + if ( $field && is_array( $field ) ) { + $field['return_format'] = isset( $field['save_format'] ) ? $field['save_format'] : $field['return_format']; + switch ( $field['return_format'] ) { + case 'object': + case 'array': + $value = $field['value']; + break; + case 'url': + $value = [ + 'id' => 0, + 'url' => $field['value'], + ]; + break; + case 'id': + $src = wp_get_attachment_image_src( $field['value'], $field['preview_size'] ); + $value = [ + 'id' => $field['value'], + 'url' => $src[0], + ]; + break; + } + } + + if ( ! isset( $value ) ) { + // Field settings has been deleted or not available. + $value = get_field( $meta_key ); + } + + if ( empty( $value ) && $this->get_settings( 'fallback' ) ) { + $value = $this->get_settings( 'fallback' ); + } + + if ( ! empty( $value ) && is_array( $value ) ) { + $image_data['id'] = $value['id']; + $image_data['url'] = $value['url']; + } + + return $image_data; + } + + protected function register_controls() { + Module::add_key_control( $this ); + + $this->add_control( + 'fallback', + [ + 'label' => esc_html__( 'Fallback', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + ] + ); + } + + public function get_supported_fields() { + return [ + 'image', + ]; + } +} diff --git a/modules/dynamic-tags/acf/tags/acf-number.php b/modules/dynamic-tags/acf/tags/acf-number.php new file mode 100644 index 0000000..e03bda2 --- /dev/null +++ b/modules/dynamic-tags/acf/tags/acf-number.php @@ -0,0 +1,58 @@ + $item ) { + if ( isset( $field['choices'][ $item ] ) ) { + $values[ $key ] = $field['choices'][ $item ]; + } + } + + $value = implode( ', ', $values ); + + break; + case 'checkbox': + $value = (array) $value; + $values = []; + foreach ( $value as $item ) { + if ( isset( $field['choices'][ $item ] ) ) { + $values[] = $field['choices'][ $item ]; + } else { + $values[] = $item; + } + } + + $value = implode( ', ', $values ); + + break; + case 'oembed': + // Get from db without formatting. + $value = $this->get_queried_object_meta( $meta_key ); + break; + case 'google_map': + $meta = $this->get_queried_object_meta( $meta_key ); + $value = isset( $meta['address'] ) ? $meta['address'] : ''; + break; + } // End switch(). + } else { + // Field settings has been deleted or not available. + $value = get_field( $meta_key ); + } // End if(). + + echo wp_kses_post( $value ); + } + + public function get_panel_template_setting_key() { + return 'key'; + } + + protected function register_controls() { + Module::add_key_control( $this ); + } + + public function get_supported_fields() { + return [ + 'text', + 'textarea', + 'number', + 'email', + 'password', + 'wysiwyg', + 'select', + 'checkbox', + 'radio', + 'true_false', + + // Pro + 'oembed', + 'google_map', + 'date_picker', + 'time_picker', + 'date_time_picker', + 'color_picker', + ]; + } + + private function get_queried_object_meta( $meta_key ) { + $value = ''; + if ( is_singular() ) { + $value = get_post_meta( get_the_ID(), $meta_key, true ); + } elseif ( is_tax() || is_category() || is_tag() ) { + $value = get_term_meta( get_queried_object_id(), $meta_key, true ); + } + + return $value; + } +} diff --git a/modules/dynamic-tags/acf/tags/acf-url.php b/modules/dynamic-tags/acf/tags/acf-url.php new file mode 100644 index 0000000..f11aa7a --- /dev/null +++ b/modules/dynamic-tags/acf/tags/acf-url.php @@ -0,0 +1,116 @@ +get_settings( 'fallback' ) ) { + $value = $this->get_settings( 'fallback' ); + } + + return wp_kses_post( $value ); + } + + protected function register_controls() { + Module::add_key_control( $this ); + + $this->add_control( + 'fallback', + [ + 'label' => esc_html__( 'Fallback', 'elementor-pro' ), + ] + ); + } + + public function get_supported_fields() { + return [ + 'text', + 'email', + 'image', + 'file', + 'page_link', + 'post_object', + 'relationship', + 'taxonomy', + 'url', + ]; + } +} diff --git a/modules/dynamic-tags/components/author-meta-filter.php b/modules/dynamic-tags/components/author-meta-filter.php new file mode 100644 index 0000000..1d13cd1 --- /dev/null +++ b/modules/dynamic-tags/components/author-meta-filter.php @@ -0,0 +1,73 @@ +get_dynamic_tag( $data['elements'] ); + + if ( ! $dynamic_tag ) { + return $data; + } + + if ( $this->is_dynamic_tag_authored_by_admin( $document, $dynamic_tag ) ) { + return $data; + } + + return Plugin::elementor()->db->iterate_data( $data, function ( $element ) { + if ( $this->is_dynamic_tag_to_escape( $element ) ) { + $element['settings']['__dynamic__'] = []; + } + + return $element; + }); + } + + private function is_dynamic_tag_authored_by_admin( $document, string $needle_tag ): bool { + global $post; + + $post_author_id = $post->post_author ?? 0; + $is_post_authored_by_admin = user_can( $post_author_id, 'manage_options' ); + + if ( ! $is_post_authored_by_admin ) { + return false; + } + + $json = wp_json_encode( $document->get_elements_data() ); + + return false !== strpos( $json, $needle_tag ); + } + + private function is_dynamic_tag_to_escape( array $element ): bool { + if ( 'widget' !== $element['elType'] ) { + return false; + } + + return ! empty( $element['settings']['__dynamic__'] ) && ! empty( preg_match( self::DYNAMIC_TAG_SHORTCODE_PATTERN, $element['settings']['__dynamic__']['title'] ) ); + } + + private function get_dynamic_tag( array $elements ): ?string { + $json = wp_json_encode( $elements ); + + preg_match( self::DYNAMIC_TAG_SHORTCODE_PATTERN, $json, $matches ); + + if ( empty( $matches ) ) { + return false; + } + + return $matches[0]; + } +} diff --git a/modules/dynamic-tags/module.php b/modules/dynamic-tags/module.php new file mode 100644 index 0000000..570a787 --- /dev/null +++ b/modules/dynamic-tags/module.php @@ -0,0 +1,164 @@ +add_component( 'author-meta-filter', new Components\Author_Meta_Filter() ); + + // ACF 5 and up + if ( class_exists( '\acf' ) && function_exists( 'acf_get_field_groups' ) && API::is_licence_has_feature( self::LICENSE_FEATURE_ACF_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->add_component( 'acf', new ACF\Module() ); + } + + if ( function_exists( 'wpcf_admin_fields_get_groups' ) && API::is_licence_has_feature( self::LICENSE_FEATURE_TOOLSET_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->add_component( 'toolset', new Toolset\Module() ); + } + + if ( function_exists( 'pods' ) && API::is_licence_has_feature( self::LICENSE_FEATURE_PODS_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->add_component( 'pods', new Pods\Module() ); + } + + /* + * WooCommerce Add To Cart Dynamic Tag. + * + * The WC ATC Dynamic Tag returns a URL that adds items to a users cart + * via the URL parameters `?add-to-cart=' . $product_id . '&quantity=' . $quantity`. + * Normally this URL method redirects to the website's Home page after adding the items to + * the cart. + * + * Since the behavior of the Tag should be identical to the "Add to Cart" widget, clicking an + * element that is using the tag needs to redirect to the Single Product page for the added + * product or the Cart page after this process if the user selected that setting in WooCommerce. + * + * To accomplish that, an extra parameter in the URL ('&e-redirect=') is used. When this + * paramater is found, the WooCommerce Add to Cart Dynamic Tag will redirect to the + * appropriate page. + */ + + //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The nonce is verified in the WC class. + $add_to_cart = Utils::_unstable_get_super_global_value( $_REQUEST, 'add-to-cart' ); + //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The nonce is verified in the WC class. + $redirect = Utils::_unstable_get_super_global_value( $_REQUEST, 'e-redirect' ); + + if ( $add_to_cart && $redirect ) { + add_filter( 'woocommerce_add_to_cart_redirect', [ $this, 'filter_woocommerce_add_to_cart_redirect' ], 10, 1 ); + } + + add_filter( 'elementor/document/save/data', [ $this->get_component( 'author-meta-filter' ), 'filter' ], 10, 2 ); + } + + public function filter_woocommerce_add_to_cart_redirect( $wc_get_cart_url ) { + //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here. + return esc_url( Utils::_unstable_get_super_global_value( $_REQUEST, 'e-redirect' ) ); + } + + public function get_name() { + return 'tags'; + } + + public function get_tag_classes_names() { + return [ + 'Archive_Description', + 'Archive_Meta', + 'Archive_Title', + 'Archive_URL', + 'Author_Info', + 'Author_Meta', + 'Author_Name', + 'Author_Profile_Picture', + 'Author_URL', + 'Comments_Number', + 'Comments_URL', + 'Page_Title', + 'Post_Custom_Field', + 'Post_Date', + 'Post_Excerpt', + 'Post_Featured_Image', + 'Post_Gallery', + 'Post_ID', + 'Post_Terms', + 'Post_Time', + 'Post_Title', + 'Post_URL', + 'Site_Logo', + 'Site_Tagline', + 'Site_Title', + 'Site_URL', + 'Internal_URL', + 'Current_Date_Time', + 'Request_Parameter', + 'Lightbox', + 'Featured_Image_Data', + 'Shortcode', + 'Contact_URL', + 'User_Info', + 'User_Profile_Picture', + ]; + } + + public function get_groups() { + return [ + self::POST_GROUP => [ + 'title' => esc_html__( 'Post', 'elementor-pro' ), + ], + self::ARCHIVE_GROUP => [ + 'title' => esc_html__( 'Archive', 'elementor-pro' ), + ], + self::SITE_GROUP => [ + 'title' => esc_html__( 'Site', 'elementor-pro' ), + ], + self::MEDIA_GROUP => [ + 'title' => esc_html__( 'Media', 'elementor-pro' ), + ], + self::ACTION_GROUP => [ + 'title' => esc_html__( 'Actions', 'elementor-pro' ), + ], + self::AUTHOR_GROUP => [ + 'title' => esc_html__( 'Author', 'elementor-pro' ), + ], + self::COMMENTS_GROUP => [ + 'title' => esc_html__( 'Comments', 'elementor-pro' ), + ], + self::WOOCOMMERCE_GROUP => [ + 'title' => esc_html__( 'WooCommerce', 'elementor-pro' ), + ], + ]; + } +} diff --git a/modules/dynamic-tags/pods/dynamic-value-provider.php b/modules/dynamic-tags/pods/dynamic-value-provider.php new file mode 100644 index 0000000..28de1e4 --- /dev/null +++ b/modules/dynamic-tags/pods/dynamic-value-provider.php @@ -0,0 +1,59 @@ +is_valid_field_key( $key ) ) { + return []; + } + + list( $pod_name, , $meta_key ) = explode( ':', $key ); + + $pod = $this->get_pods_value( $pod_name, get_the_ID() ); + + if ( false === $pod ) { + return []; + } + + return [ + 'field' => $pod->fields[ $meta_key ], + 'value' => $pod->field( $meta_key ), + 'display' => $pod->display( $meta_key ), + 'pod' => $pod, + 'key' => $meta_key, + ]; + } + + /** + * Retrieve the Pod value from `Pods` plugin. + * Used for testing. + * + * @param $type + * @param $id + * + * @return bool|\Pods + */ + protected function get_pods_value( $type, $id ) { + return pods( $type, $id ); + } + + private function is_valid_field_key( $key ) { + $key = trim( $key ); + + if ( empty( $key ) ) { + return false; + } + + $colon_count = substr_count( $key, ':' ); + + // Key structure looks like: `page:699:pods_date_time`. + return ( 2 === $colon_count ); + } +} diff --git a/modules/dynamic-tags/pods/module.php b/modules/dynamic-tags/pods/module.php new file mode 100644 index 0000000..b9837bc --- /dev/null +++ b/modules/dynamic-tags/pods/module.php @@ -0,0 +1,135 @@ +load_pods( [ + 'table_info' => true, + 'fields' => true, + ] ); + + $groups = []; + + foreach ( $all_pods as $group ) { + $options = []; + + foreach ( $group['fields'] as $field ) { + if ( ! self::valid_field_type( $types, $field ) ) { + continue; + } + + // Use pods ID for unique keys + $key = $group['name'] . ':' . $field['pod_id'] . ':' . $field['name']; + $options[ $key ] = $field['label']; + } + + if ( empty( $options ) ) { + continue; + } + + if ( 1 === count( $options ) ) { + $options = [ -1 => ' -- ' ] + $options; + } + + $groups[] = [ + 'label' => $group['name'], + 'options' => $options, + ]; + } + + return $groups; + } + + public static function valid_field_type( $types, $field ) { + // Only file field with single image value + if ( in_array( 'pods_image', $types, true ) && self::pods_image_mapping( $field ) ) { + return true; + } + if ( in_array( 'pods_url', $types, true ) && in_array( $field['type'], [ 'email', 'file', 'website', 'phone' ] ) ) { + // Only file with single value allowed + if ( 'file' === $field['type'] && ! self::pods_file_mapping( $field ) ) { + return false; + } + + return true; + } + // Only file with multiple images allowed + if ( in_array( 'pods_gallery', $types, true ) && self::pods_image_mapping( $field, false ) ) { + return true; + } + // Any other type + if ( in_array( $field['type'], $types, true ) ) { + return true; + } + + return false; + } + + public static function pods_file_mapping( $field, $single = true ) { + if ( 'file' !== $field['type'] ) { + return false; + } + + $limit = $single ? 'single' : 'multi'; + if ( $limit !== $field['options']['file_format_type'] ) { + return false; + } + + return true; + } + + public static function pods_image_mapping( $field, $single = true ) { + if ( ! isset( $field['options'] ) || ! isset( $field['options']['file_type'] ) ) { + return false; + } + if ( 'images' !== $field['options']['file_type'] ) { + return false; + } + if ( ! self::pods_file_mapping( $field, $single ) ) { + return false; + } + + return true; + } + + public function get_tag_classes_names() { + return [ + 'Pods_Text', + 'Pods_Date', + 'Pods_Date_Time', + 'Pods_Image', + 'Pods_Gallery', + 'Pods_URL', + 'Pods_Numeric', + ]; + } + + public function get_groups() { + return [ + self::PODS_GROUP => [ + 'title' => esc_html__( 'Pods', 'elementor-pro' ), + ], + ]; + } +} diff --git a/modules/dynamic-tags/pods/tags/pods-base.php b/modules/dynamic-tags/pods/tags/pods-base.php new file mode 100644 index 0000000..3f9865a --- /dev/null +++ b/modules/dynamic-tags/pods/tags/pods-base.php @@ -0,0 +1,64 @@ +get_settings( 'key' ); + if ( empty( $key ) ) { + return false; + } + + list( $pod_name, $pod_id, $meta_key ) = explode( ':', $key ); + /** + * @var \Pods + */ + $pod = pods( $pod_name, get_the_ID() ); + + if ( false === $pod ) { + return []; + } + + return [ + 'field' => $pod->fields[ $meta_key ], + 'value' => $pod->field( $meta_key ), + 'display' => $pod->display( $meta_key ), + 'pod' => $pod, + 'key' => $meta_key, + ]; + } + + public function get_categories() { + return [ + Module::TEXT_CATEGORY, + Module::POST_META_CATEGORY, + ]; + } + + protected function register_controls() { + $this->add_control( + 'key', + [ + 'label' => esc_html__( 'Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => Module::get_control_options( $this->get_supported_fields() ), + ] + ); + } + + protected function get_supported_fields() { + return []; + } +} diff --git a/modules/dynamic-tags/pods/tags/pods-date-time.php b/modules/dynamic-tags/pods/tags/pods-date-time.php new file mode 100644 index 0000000..d70efdc --- /dev/null +++ b/modules/dynamic-tags/pods/tags/pods-date-time.php @@ -0,0 +1,95 @@ +dynamic_value_provider->get_value( + $this->get_settings( 'key' ) + ); + + $value = $field['value'] ?? ''; + + if ( ! empty( $value ) ) { + $value = gmdate( 'Y-m-d H:i:s', strtotime( $value ) ); + } + + if ( empty( $value ) && $this->get_settings( 'fallback' ) ) { + $value = $this->get_settings( 'fallback' ); + } + + return wp_kses_post( $value ); + } + + public function get_panel_template_setting_key() { + return 'key'; + } + + protected function register_controls() { + $this->add_control( + 'key', + [ + 'label' => esc_html__( 'Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => Module::get_control_options( $this->get_supported_fields() ), + ] + ); + + $this->add_control( + 'fallback', + [ + 'type' => Controls_Manager::DATE_TIME, + 'label' => esc_html__( 'Fallback', 'elementor-pro' ), + ] + ); + } + + protected function get_supported_fields() { + return [ + 'datetime', + ]; + } + + public function __construct( array $data = [], $dynamic_value_provider = null ) { + parent::__construct( $data ); + + $this->dynamic_value_provider = $dynamic_value_provider ?? new Dynamic_Value_Provider(); + } +} diff --git a/modules/dynamic-tags/pods/tags/pods-date.php b/modules/dynamic-tags/pods/tags/pods-date.php new file mode 100644 index 0000000..6b1e42a --- /dev/null +++ b/modules/dynamic-tags/pods/tags/pods-date.php @@ -0,0 +1,96 @@ +get_field(); + $field = $field_data['field']; + $value = empty( $field_data['value'] ) ? '' : $field_data['value']; + + if ( $field && ! empty( $field['type'] ) && in_array( $field['type'], [ 'date', 'datetime' ] ) ) { + + $format = $this->get_settings( 'format' ); + + $timestamp = strtotime( $value ); + + if ( 'human' === $format ) { + $value = human_time_diff( $timestamp ); + } else { + switch ( $format ) { + case 'default': + $date_format = get_option( 'date_format' ); + break; + case 'custom': + $date_format = $this->get_settings( 'custom_format' ); + break; + default: + $date_format = $format; + break; + } + + $value = gmdate( $date_format, $timestamp ); + } + } + echo wp_kses_post( $value ); + } + + public function get_panel_template_setting_key() { + return 'key'; + } + + protected function register_controls() { + parent::register_controls(); + + $this->add_control( + 'format', + [ + 'label' => esc_html__( 'Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'F j, Y' => gmdate( 'F j, Y' ), + 'Y-m-d' => gmdate( 'Y-m-d' ), + 'm/d/Y' => gmdate( 'm/d/Y' ), + 'd/m/Y' => gmdate( 'd/m/Y' ), + 'human' => esc_html__( 'Human Readable', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + ] + ); + + $this->add_control( + 'custom_format', + [ + 'label' => esc_html__( 'Custom Format', 'elementor-pro' ), + 'default' => '', + 'description' => sprintf( '%s', esc_html__( 'Documentation on date and time formatting', 'elementor-pro' ) ), + 'condition' => [ + 'format' => 'custom', + ], + ] + ); + } + + protected function get_supported_fields() { + return [ + 'datetime', + 'date', + ]; + } +} diff --git a/modules/dynamic-tags/pods/tags/pods-gallery.php b/modules/dynamic-tags/pods/tags/pods-gallery.php new file mode 100644 index 0000000..a05ee5e --- /dev/null +++ b/modules/dynamic-tags/pods/tags/pods-gallery.php @@ -0,0 +1,83 @@ +get_settings( 'key' ); + if ( empty( $key ) ) { + return false; + } + + $images = []; + + list( $pod_name, $pod_id, $meta_key ) = explode( ':', $key ); + /** + * @var \Pods + */ + $pod = pods( $pod_name, get_the_ID() ); + + if ( false === $pod ) { + return []; + } + + $galley_images = $pod->field( $meta_key ); + + if ( empty( $galley_images ) || ! is_array( $galley_images ) ) { + return $images; + } + + foreach ( $galley_images as $image ) { + $images[] = [ + 'id' => $image['ID'], + ]; + } + + return $images; + } + + protected function register_controls() { + $this->add_control( + 'key', + [ + 'label' => esc_html__( 'Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => Module::get_control_options( $this->get_supported_fields() ), + ] + ); + } + + protected function get_supported_fields() { + return [ + 'pods_gallery', + ]; + } +} diff --git a/modules/dynamic-tags/pods/tags/pods-image.php b/modules/dynamic-tags/pods/tags/pods-image.php new file mode 100644 index 0000000..0c5444c --- /dev/null +++ b/modules/dynamic-tags/pods/tags/pods-image.php @@ -0,0 +1,87 @@ +get_settings( 'key' ); + + $image_data = $this->get_settings( 'fallback' ); + + if ( empty( $key ) ) { + return $image_data; + } + + list( $pod_name, $pod_id, $meta_key ) = explode( ':', $key ); + /** + * @var \Pods + */ + $pod = pods( $pod_name, get_the_ID() ); + + if ( false === $pod ) { + return []; + } + + $image = $pod->field( $meta_key ); + + $image_data = [ + 'id' => empty( $image['ID'] ) ? $image_data['id'] : $image['ID'], + 'url' => empty( $image['guid'] ) ? $image_data['url'] : $image['guid'], + ]; + + return $image_data; + } + + protected function register_controls() { + $this->add_control( + 'key', + [ + 'label' => esc_html__( 'Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => Module::get_control_options( $this->get_supported_fields() ), + ] + ); + + $this->add_control( + 'fallback', + [ + 'label' => esc_html__( 'Fallback', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + ] + ); + } + + protected function get_supported_fields() { + return [ + 'pods_image', + ]; + } +} diff --git a/modules/dynamic-tags/pods/tags/pods-numeric.php b/modules/dynamic-tags/pods/tags/pods-numeric.php new file mode 100644 index 0000000..78a596d --- /dev/null +++ b/modules/dynamic-tags/pods/tags/pods-numeric.php @@ -0,0 +1,39 @@ +get_field(); + $value = ! empty( $field_data['value'] ) && is_numeric( $field_data['value'] ) ? $field_data['value'] : ''; + + echo wp_kses_post( $value ); + } + + protected function get_supported_fields() { + return [ + 'numeric', + ]; + } +} diff --git a/modules/dynamic-tags/pods/tags/pods-text.php b/modules/dynamic-tags/pods/tags/pods-text.php new file mode 100644 index 0000000..1373326 --- /dev/null +++ b/modules/dynamic-tags/pods/tags/pods-text.php @@ -0,0 +1,55 @@ +get_field(); + $field = $field_data['field']; + $value = empty( $field_data['value'] ) ? '' : $field_data['value']; + + if ( ! empty( $field['type'] ) ) { + switch ( $field['type'] ) { + case 'paragraph': + $value = $field_data['display']; + break; + case 'pick': + $value = $field_data['display']; + if ( is_array( $value ) ) { + $value = implode( ', ', $value ); + } + break; + } + } + + echo wp_kses_post( $value ); + } + + protected function get_supported_fields() { + return [ + 'text', + 'phone', + 'paragraph', + 'relationship', + 'pick', + 'numeric', + 'email', + 'oembed', + 'google_address', + 'wysiwyg', + 'time', + ]; + } +} diff --git a/modules/dynamic-tags/pods/tags/pods-url.php b/modules/dynamic-tags/pods/tags/pods-url.php new file mode 100644 index 0000000..fbe8ef5 --- /dev/null +++ b/modules/dynamic-tags/pods/tags/pods-url.php @@ -0,0 +1,95 @@ +get_settings( 'key' ); + if ( empty( $key ) ) { + return false; + } + + list( $pod_name, $pod_id, $meta_key ) = explode( ':', $key ); + /** + * @var \Pods + */ + $pod = pods( $pod_name, get_the_ID() ); + + if ( false === $pod ) { + return []; + } + + $field = $pod->fields[ $meta_key ]; + $value = $pod->field( $meta_key ); + if ( $field && ! empty( $field['type'] ) ) { + + switch ( $field['type'] ) { + case 'phone': + $value = 'tel:' . $value; + break; + case 'file': + $value = empty( $value['guid'] ) ? '' : $value['guid']; + break; + case 'email': + $value = 'mailto:' . $value; + break; + } // End switch(). + } + + if ( empty( $value ) && $this->get_settings( 'fallback' ) ) { + $value = $this->get_settings( 'fallback' ); + } + + return wp_kses_post( $value ); + } + + protected function register_controls() { + $this->add_control( + 'key', + [ + 'label' => esc_html__( 'Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => Module::get_control_options( $this->get_supported_fields() ), + ] + ); + + $this->add_control( + 'fallback', + [ + 'label' => esc_html__( 'Fallback', 'elementor-pro' ), + ] + ); + } + + protected function get_supported_fields() { + return [ 'pods_url' ]; + } +} diff --git a/modules/dynamic-tags/tags/archive-description.php b/modules/dynamic-tags/tags/archive-description.php new file mode 100644 index 0000000..b60cb16 --- /dev/null +++ b/modules/dynamic-tags/tags/archive-description.php @@ -0,0 +1,46 @@ +render_loop_taxonomy(); + return; + } + + $this->render_post(); + } + + private function render_post() { + echo wp_kses_post( get_the_archive_description() ); + } + + private function render_loop_taxonomy() { + $this->render_taxonomy_content_by_key( 'description' ); + } +} diff --git a/modules/dynamic-tags/tags/archive-meta.php b/modules/dynamic-tags/tags/archive-meta.php new file mode 100644 index 0000000..88490e0 --- /dev/null +++ b/modules/dynamic-tags/tags/archive-meta.php @@ -0,0 +1,66 @@ +get_settings( 'key' ); + + if ( empty( $key ) ) { + return; + } + + $value = ''; + + if ( is_category() || is_tax() ) { + $value = get_term_meta( get_queried_object_id(), $key, true ); + } elseif ( is_author() ) { + $value = get_user_meta( get_queried_object_id(), $key, true ); + } + + echo wp_kses_post( $value ); + } + + public function get_panel_template_setting_key() { + return 'key'; + } + + protected function register_controls() { + $this->add_control( + 'key', + [ + 'label' => esc_html__( 'Meta Key', 'elementor-pro' ), + 'ai' => [ + 'active' => false, + ], + ] + ); + } +} diff --git a/modules/dynamic-tags/tags/archive-title.php b/modules/dynamic-tags/tags/archive-title.php new file mode 100644 index 0000000..35370d8 --- /dev/null +++ b/modules/dynamic-tags/tags/archive-title.php @@ -0,0 +1,76 @@ + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'settings' => [ 'include_context' => 'no' ], + 'group' => 'archive', + ], + ]; + + return $config; + } + + public function render() { + if ( Taxonomy_Loop_Provider::is_loop_taxonomy() ) { + $this->render_loop_taxonomy(); + return; + } + + $this->render_post(); + } + + private function render_post() { + $include_context = 'yes' === $this->get_settings( 'include_context' ); + + $title = Utils::get_page_title( $include_context ); + + echo wp_kses_post( $title ); + } + + private function render_loop_taxonomy() { + $this->render_taxonomy_content_by_key( 'name' ); + } + + protected function register_controls() { + $this->add_control( + 'include_context', + [ + 'label' => esc_html__( 'Include Context', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + ] + ); + } +} diff --git a/modules/dynamic-tags/tags/archive-url.php b/modules/dynamic-tags/tags/archive-url.php new file mode 100644 index 0000000..bb78df8 --- /dev/null +++ b/modules/dynamic-tags/tags/archive-url.php @@ -0,0 +1,38 @@ + [ + 'label' => esc_html__( 'Author Bio', 'elementor-pro' ), + 'settings' => [ 'key' => 'description' ], + 'group' => 'author', + ], + 'author_info_email' => [ + 'label' => esc_html__( 'Author Email', 'elementor-pro' ), + 'settings' => [ 'key' => 'email' ], + 'group' => 'author', + ], + 'author_info_website' => [ + 'label' => esc_html__( 'Author Website', 'elementor-pro' ), + 'settings' => [ 'key' => 'url' ], + 'group' => 'author', + ], + ]; + + return $config; + } + + protected function register_controls() { + $this->add_control( + 'key', + [ + 'label' => esc_html__( 'Field', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'description', + 'options' => $this->get_key_options(), + ] + ); + } + + private function get_key_options() { + $options = []; + $options['description'] = esc_html__( 'Bio', 'elementor-pro' ); + + if ( current_user_can( 'manage_options' ) ) { + $options['email'] = esc_html__( 'Email', 'elementor-pro' ); + } + + $options['url'] = esc_html__( 'Website', 'elementor-pro' ); + + return $options; + } +} diff --git a/modules/dynamic-tags/tags/author-meta.php b/modules/dynamic-tags/tags/author-meta.php new file mode 100644 index 0000000..bc1c326 --- /dev/null +++ b/modules/dynamic-tags/tags/author-meta.php @@ -0,0 +1,31 @@ +add_control( + 'key', + [ + 'label' => esc_html__( 'Meta Key', 'elementor-pro' ), + 'ai' => [ + 'active' => false, + ], + ] + ); + } +} diff --git a/modules/dynamic-tags/tags/author-name.php b/modules/dynamic-tags/tags/author-name.php new file mode 100644 index 0000000..d7413dc --- /dev/null +++ b/modules/dynamic-tags/tags/author-name.php @@ -0,0 +1,32 @@ + '', + 'url' => get_avatar_url( (int) get_the_author_meta( 'ID' ) ), + ]; + } +} diff --git a/modules/dynamic-tags/tags/author-url.php b/modules/dynamic-tags/tags/author-url.php new file mode 100644 index 0000000..7f86c89 --- /dev/null +++ b/modules/dynamic-tags/tags/author-url.php @@ -0,0 +1,64 @@ +get_settings( 'url' ) ) { + global $authordata; + + if ( $authordata ) { + $value = get_author_posts_url( $authordata->ID, $authordata->user_nicename ); + } + } else { + $value = get_the_author_meta( 'url' ); + } + + return $value; + } + + protected function register_controls() { + $this->add_control( + 'url', + [ + 'label' => esc_html__( 'URL', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'archive', + 'options' => [ + 'archive' => esc_html__( 'Author Archive', 'elementor-pro' ), + 'website' => esc_html__( 'Author Website', 'elementor-pro' ), + ], + ] + ); + } +} diff --git a/modules/dynamic-tags/tags/base/author-tag.php b/modules/dynamic-tags/tags/base/author-tag.php new file mode 100644 index 0000000..d000c2e --- /dev/null +++ b/modules/dynamic-tags/tags/base/author-tag.php @@ -0,0 +1,77 @@ +get_not_allowed_fields(), true ); + } + + private function get_not_allowed_fields() { + $not_allowed_fields = [ + 'user_login', + 'user_pass', + 'user_registered', + 'user_activation_key', + 'user_status', + 'user_email', + // WordPress allows these aliases. + 'login', + 'pass', + 'registered', + 'activation_key', + 'status', + 'email', + ]; + + if ( $this->allow_email_key_for_admins_only() ) { + unset( $not_allowed_fields[ array_search( 'user_email', $not_allowed_fields ) ] ); + unset( $not_allowed_fields[ array_search( 'email', $not_allowed_fields ) ] ); + } + + return $not_allowed_fields; + } + + private function allow_email_key_for_admins_only() { + return current_user_can( 'manage_options' ) || $this->is_post_authored_by_admin(); + } + + private function is_post_authored_by_admin() { + global $post; + + $post_author_id = $post->post_author ?? 0; + + return user_can( $post_author_id, 'manage_options' ); + } + + public function render() { + $key = $this->get_settings( 'key' ); + + if ( empty( $key ) || ! $this->is_field_allowed( $key ) ) { + return; + } + + $value = get_the_author_meta( $key ); + + echo wp_kses_post( $value ); + } +} diff --git a/modules/dynamic-tags/tags/base/data-tag.php b/modules/dynamic-tags/tags/base/data-tag.php new file mode 100644 index 0000000..90afcac --- /dev/null +++ b/modules/dynamic-tags/tags/base/data-tag.php @@ -0,0 +1,14 @@ +loop_term ) || ! is_object( $wp_query->loop_term ) ) { + return; + } + + $content = ''; + + if ( isset( $wp_query->loop_term->$key ) ) { + $content = $wp_query->loop_term->$key; + } + + echo wp_kses_post( $content ); + } + + protected function get_data_id_from_taxonomy_loop_query() { + global $wp_query; + + if ( isset( $wp_query->loop_term ) && isset( $wp_query->loop_term->term_id ) ) { + return $wp_query->loop_term->term_id; + } + + return 0; + } +} diff --git a/modules/dynamic-tags/tags/base/tag.php b/modules/dynamic-tags/tags/base/tag.php new file mode 100644 index 0000000..4a67fd2 --- /dev/null +++ b/modules/dynamic-tags/tags/base/tag.php @@ -0,0 +1,16 @@ +add_control( + 'format_no_comments', + [ + 'label' => esc_html__( 'No Comments Format', 'elementor-pro' ), + 'default' => esc_html__( 'No Responses', 'elementor-pro' ), + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'format_one_comments', + [ + 'label' => esc_html__( 'One Comment Format', 'elementor-pro' ), + 'default' => esc_html__( 'One Response', 'elementor-pro' ), + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'format_many_comments', + [ + 'label' => esc_html__( 'Many Comment Format', 'elementor-pro' ), + 'default' => esc_html__( '{number} Responses', 'elementor-pro' ), + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'link_to', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'comments_link' => esc_html__( 'Comments Link', 'elementor-pro' ), + ], + ] + ); + } + + public function render() { + $settings = $this->get_settings(); + + $comments_number = get_comments_number(); + + if ( ! $comments_number ) { + $count = $settings['format_no_comments']; + } elseif ( 1 === $comments_number ) { + $count = $settings['format_one_comments']; + } else { + $count = strtr( $settings['format_many_comments'], [ + '{number}' => number_format_i18n( $comments_number ), + ] ); + } + + if ( 'comments_link' === $this->get_settings( 'link_to' ) ) { + $count = sprintf( '%s', get_comments_link(), $count ); + } + + echo wp_kses_post( $count ); + } +} diff --git a/modules/dynamic-tags/tags/comments-url.php b/modules/dynamic-tags/tags/comments-url.php new file mode 100644 index 0000000..2fd1c46 --- /dev/null +++ b/modules/dynamic-tags/tags/comments-url.php @@ -0,0 +1,32 @@ +add_control( + 'link_type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => '— ' . esc_html__( 'Select', 'elementor-pro' ) . ' —', + 'email' => esc_html__( 'Email', 'elementor-pro' ), + 'tel' => esc_html__( 'Tel', 'elementor-pro' ), + 'sms' => esc_html__( 'SMS', 'elementor-pro' ), + 'whatsapp' => esc_html__( 'WhatsApp', 'elementor-pro' ), + 'skype' => esc_html__( 'Skype', 'elementor-pro' ), + 'messenger' => esc_html__( 'Messenger', 'elementor-pro' ), + 'viber' => esc_html__( 'Viber', 'elementor-pro' ), + 'waze' => esc_html__( 'Waze', 'elementor-pro' ), + 'google_calendar' => esc_html__( 'Google Calendar', 'elementor-pro' ), + 'outlook_calendar' => esc_html__( 'Outlook Calendar', 'elementor-pro' ), + 'yahoo_calendar' => esc_html__( 'Yahoo Calendar', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'mail_to', + [ + 'label' => esc_html__( 'Email', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'condition' => [ + 'link_type' => 'email', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'mail_subject', + [ + 'label' => esc_html__( 'Subject', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'label_block' => 'true', + 'condition' => [ + 'link_type' => 'email', + ], + ] + ); + + $this->add_control( + 'mail_body', + [ + 'label' => esc_html__( 'Message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'condition' => [ + 'link_type' => 'email', + ], + ] + ); + + $this->add_control( + 'tel_number', + [ + 'label' => esc_html__( 'Number', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'condition' => [ + 'link_type' => [ + 'tel', + 'sms', + 'whatsapp', + 'viber', + ], + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'username', + [ + 'label' => esc_html__( 'Username', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'condition' => [ + 'link_type' => [ 'skype', 'messenger' ], + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'viber_action', + [ + 'label' => esc_html__( 'Action', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'contact' => esc_html__( 'Contact', 'elementor-pro' ), + 'add' => esc_html__( 'Add', 'elementor-pro' ), + ], + 'default' => 'contact', + 'condition' => [ + 'link_type' => 'viber', + ], + ] + ); + + $this->add_control( + 'skype_action', + [ + 'label' => esc_html__( 'Action', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'call' => esc_html__( 'Call', 'elementor-pro' ), + 'chat' => esc_html__( 'Chat', 'elementor-pro' ), + 'userinfo' => esc_html__( 'Show Profile', 'elementor-pro' ), + 'add' => esc_html__( 'Add to Contacts', 'elementor-pro' ), + 'voicemail' => esc_html__( 'Send Voice Mail', 'elementor-pro' ), + ], + 'default' => 'call', + 'condition' => [ + 'link_type' => 'skype', + ], + ] + ); + + $this->add_control( + 'waze_address', + [ + 'label' => esc_html__( 'Location', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'label_block' => 'true', + 'condition' => [ + 'link_type' => 'waze', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'event_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'label_block' => 'true', + 'condition' => [ + 'link_type' => [ + 'google_calendar', + 'outlook_calendar', + 'yahoo_calendar', + ], + ], + ] + ); + + $this->add_control( + 'event_description', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'condition' => [ + 'link_type' => [ + 'google_calendar', + 'outlook_calendar', + 'yahoo_calendar', + ], + ], + ] + ); + + $this->add_control( + 'event_location', + [ + 'label' => esc_html__( 'Location', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'label_block' => 'true', + 'condition' => [ + 'link_type' => [ + 'google_calendar', + 'outlook_calendar', + 'yahoo_calendar', + ], + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'event_start_date', + [ + 'label' => esc_html__( 'Start', 'elementor-pro' ), + 'type' => Controls_Manager::DATE_TIME, + 'condition' => [ + 'link_type' => [ + 'google_calendar', + 'outlook_calendar', + 'yahoo_calendar', + ], + ], + ] + ); + + $this->add_control( + 'event_end_date', + [ + 'label' => esc_html__( 'End', 'elementor-pro' ), + 'type' => Controls_Manager::DATE_TIME, + 'condition' => [ + 'link_type' => [ + 'google_calendar', + 'outlook_calendar', + 'yahoo_calendar', + ], + ], + ] + ); + } + + protected function register_advanced_section() {} + + private function build_mail_to_link( $settings ) { + if ( empty( $settings['mail_to'] ) ) { + return ''; + } + + $link = 'mailto:' . $settings['mail_to'] . '?'; + + $build_parts = []; + + if ( ! empty( $settings['mail_subject'] ) ) { + $build_parts['subject'] = $this->escape_space_in_url( $settings['mail_subject'] ); + } + + if ( ! empty( $settings['mail_body'] ) ) { + $build_parts['body'] = $this->escape_space_in_url( $settings['mail_body'] ); + } + + return add_query_arg( $build_parts, $link ); + } + + private function build_sms_link( $settings ) { + if ( empty( $settings['tel_number'] ) ) { + return ''; + } + + $value = 'sms:' . $settings['tel_number']; + return $value; + } + + private function build_whatsapp_link( $settings ) { + if ( empty( $settings['tel_number'] ) ) { + return ''; + } + + return 'https://api.whatsapp.com/send?phone=' . $settings['tel_number']; + } + + private function build_skype_link( $settings ) { + if ( empty( $settings['username'] ) ) { + return ''; + } + + $action = 'call'; + if ( ! empty( $settings['skype_action'] ) ) { + $action = $settings['skype_action']; + } + $link = 'skype:' . $settings['username'] . '?' . $action; + return $link; + } + + private function build_waze_link( $settings ) { + $link = 'https://waze.com/ul?'; + + $build_parts = [ + 'q' => $settings['waze_address'], + 'z' => 10, + 'navigate' => 'yes', + ]; + + return add_query_arg( $build_parts, $link ); + } + + private function date_to_iso( $date, $all_day = false ) { + $time = strtotime( $date ); + + if ( $all_day ) { + return gmdate( 'Ymd\/Ymd', $time ); + } + + return gmdate( 'Ymd\THis', $time ); + } + + private function date_to_ics( $date ) { + $time = strtotime( $date ); + return gmdate( 'Y-m-d\Th:i:s', $time ); + } + + private function escape_space_in_url( $url ) { + return str_replace( ' ', '%20', $url ); + } + + private function build_google_calendar_link( $settings ) { + $dates = ''; + if ( ! empty( $settings['event_start_date'] ) ) { + if ( empty( $settings['event_end_date'] ) ) { + $dates = $this->date_to_iso( $settings['event_start_date'], true ); + } else { + $dates = $this->date_to_iso( $settings['event_start_date'] ) . '/' . $this->date_to_iso( $settings['event_end_date'] ); + } + } + $link = 'https://www.google.com/calendar/render?action=TEMPLATE&'; + $build_parts = [ + 'text' => empty( $settings['event_title'] ) ? '' : $this->escape_space_in_url( $settings['event_title'] ), + 'details' => empty( $settings['event_description'] ) ? '' : $this->escape_space_in_url( $settings['event_description'] ), + 'dates' => $dates, + 'location' => empty( $settings['event_location'] ) ? '' : $this->escape_space_in_url( $settings['event_location'] ), + ]; + + return add_query_arg( $build_parts, $link ); + } + + private function build_outlook_calendar_link( $settings ) { + $link = 'https://outlook.office.com/owa/?path=/calendar/action/compose&'; + $build_parts = [ + 'subject' => empty( $settings['event_title'] ) ? '' : urlencode( $settings['event_title'] ), + 'body' => empty( $settings['event_description'] ) ? '' : urlencode( $settings['event_description'] ), + 'location' => empty( $settings['event_location'] ) ? '' : urlencode( $settings['event_location'] ), + ]; + + if ( ! empty( $settings['event_start_date'] ) ) { + $build_parts['startdt'] = urlencode( $this->date_to_ics( $settings['event_start_date'] ) ); + } + + if ( ! empty( $settings['event_end_date'] ) ) { + $build_parts['enddt'] = urlencode( $this->date_to_ics( $settings['event_end_date'] ) ); + } + + return add_query_arg( $build_parts, $link ); + } + + private function build_messenger_link( $settings ) { + if ( empty( $settings['username'] ) ) { + return ''; + } + return 'https://m.me/' . $settings['username']; + } + + private function build_yahoo_calendar_link( $settings ) { + $link = 'https://calendar.yahoo.com/?v=60&view=d&type=20'; + $build_parts = [ + 'title' => empty( $settings['event_title'] ) ? '' : urlencode( $settings['event_title'] ), + 'desc' => empty( $settings['event_description'] ) ? '' : urlencode( $settings['event_description'] ), + 'in_loc' => empty( $settings['event_location'] ) ? '' : urlencode( $settings['event_location'] ), + ]; + + if ( ! empty( $settings['event_start_date'] ) ) { + $build_parts['st'] = urlencode( gmdate( 'Ymd\This', strtotime( $settings['event_start_date'] ) ) ); + } + + if ( ! empty( $settings['event_end_date'] ) ) { + $build_parts['et'] = urlencode( gmdate( 'Ymd\This', strtotime( $settings['event_end_date'] ) ) ); + } + + return add_query_arg( $build_parts, $link ); + } + + public function build_viber_link( $settings ) { + if ( empty( $settings['tel_number'] ) ) { + return ''; + } + $action = 'contact'; + if ( ! empty( $settings['viber_action'] ) ) { + $action = $settings['viber_action']; + } + return add_query_arg( [ + 'number' => urlencode( $settings['tel_number'] ), + ], 'viber://' . $action ); + } + + public function render() { + $settings = $this->get_settings(); + + if ( empty( $settings['link_type'] ) ) { + return ''; + } + + $value = ''; + switch ( $settings['link_type'] ) { + case 'email': + $value = $this->build_mail_to_link( $settings ); + break; + case 'tel': + $value = ( empty( $settings['tel_number'] ) ? '' : 'tel:' . $settings['tel_number'] ); + break; + case 'sms': + $value = $this->build_sms_link( $settings ); + break; + case 'messenger': + $value = $this->build_messenger_link( $settings ); + break; + case 'whatsapp': + $value = $this->build_whatsapp_link( $settings ); + break; + case 'skype': + $value = $this->build_skype_link( $settings ); + break; + case 'waze': + $value = $this->build_waze_link( $settings ); + break; + case 'google_calendar': + $value = $this->build_google_calendar_link( $settings ); + break; + case 'outlook_calendar': + $value = $this->build_outlook_calendar_link( $settings ); + break; + case 'yahoo_calendar': + $value = $this->build_yahoo_calendar_link( $settings ); + break; + case 'viber': + $value = $this->build_viber_link( $settings ); + break; + } + echo esc_html( $value ); + } +} + diff --git a/modules/dynamic-tags/tags/current-date-time.php b/modules/dynamic-tags/tags/current-date-time.php new file mode 100644 index 0000000..73b6cce --- /dev/null +++ b/modules/dynamic-tags/tags/current-date-time.php @@ -0,0 +1,120 @@ +add_control( + 'date_format', + [ + 'label' => esc_html__( 'Date Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + '' => esc_html__( 'None', 'elementor-pro' ), + 'F j, Y' => gmdate( 'F j, Y' ), + 'Y-m-d' => gmdate( 'Y-m-d' ), + 'm/d/Y' => gmdate( 'm/d/Y' ), + 'd/m/Y' => gmdate( 'd/m/Y' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + ] + ); + + $this->add_control( + 'time_format', + [ + 'label' => esc_html__( 'Time Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + '' => esc_html__( 'None', 'elementor-pro' ), + 'g:i a' => gmdate( 'g:i a' ), + 'g:i A' => gmdate( 'g:i A' ), + 'H:i' => gmdate( 'H:i' ), + ], + 'default' => 'default', + 'condition' => [ + 'date_format!' => 'custom', + ], + ] + ); + + $this->add_control( + 'custom_format', + [ + 'label' => esc_html__( 'Custom Format', 'elementor-pro' ), + 'default' => get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), + 'description' => sprintf( '%s', esc_html__( 'Documentation on date and time formatting', 'elementor-pro' ) ), + 'condition' => [ + 'date_format' => 'custom', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + } + + public function render() { + $settings = $this->get_settings(); + + if ( 'custom' === $settings['date_format'] ) { + $format = $settings['custom_format']; + } else { + $date_format = $settings['date_format']; + $time_format = $settings['time_format']; + $format = ''; + + if ( 'default' === $date_format ) { + $date_format = get_option( 'date_format' ); + } + + if ( 'default' === $time_format ) { + $time_format = get_option( 'time_format' ); + } + + if ( $date_format ) { + $format = $date_format; + $has_date = true; + } else { + $has_date = false; + } + + if ( $time_format ) { + if ( $has_date ) { + $format .= ' '; + } + $format .= $time_format; + } + } + + $value = date_i18n( $format ); + + echo wp_kses_post( $value ); + } +} diff --git a/modules/dynamic-tags/tags/featured-image-data.php b/modules/dynamic-tags/tags/featured-image-data.php new file mode 100644 index 0000000..f2af14a --- /dev/null +++ b/modules/dynamic-tags/tags/featured-image-data.php @@ -0,0 +1,121 @@ + [ + 'label' => esc_html__( 'Image Title', 'elementor-pro' ), + 'settings' => [ 'attachment_data' => 'title' ], + 'group' => 'featured_image', + ], + 'featured_image_data_alt' => [ + 'label' => esc_html__( 'Image Alt', 'elementor-pro' ), + 'settings' => [ 'attachment_data' => 'alt' ], + 'group' => 'featured_image', + ], + 'featured_image_data_caption' => [ + 'label' => esc_html__( 'Image Caption', 'elementor-pro' ), + 'settings' => [ 'attachment_data' => 'caption' ], + 'group' => 'featured_image', + ], + ]; + + return $config; + } + + private function get_attacment() { + $id = get_post_thumbnail_id(); + + if ( ! $id ) { + return false; + } + + return get_post( $id ); + } + + public function render() { + $settings = $this->get_settings(); + $attachment = $this->get_attacment(); + + if ( ! $attachment ) { + return; + } + + $value = ''; + + switch ( $settings['attachment_data'] ) { + case 'alt': + $value = get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true ); + break; + case 'caption': + $value = $attachment->post_excerpt; + break; + case 'description': + $value = $attachment->post_content; + break; + case 'href': + $value = get_permalink( $attachment->ID ); + break; + case 'src': + $value = $attachment->guid; + break; + case 'title': + $value = $attachment->post_title; + break; + } + + echo wp_kses_post( $value ); + } + + protected function register_controls() { + + $this->add_control( + 'attachment_data', + [ + 'label' => esc_html__( 'Data', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'title', + 'options' => [ + 'title' => esc_html__( 'Title', 'elementor-pro' ), + 'alt' => esc_html__( 'Alt', 'elementor-pro' ), + 'caption' => esc_html__( 'Caption', 'elementor-pro' ), + 'description' => esc_html__( 'Description', 'elementor-pro' ), + 'src' => esc_html__( 'File URL', 'elementor-pro' ), + 'href' => esc_html__( 'Attachment URL', 'elementor-pro' ), + ], + ] + ); + } +} diff --git a/modules/dynamic-tags/tags/internal-url.php b/modules/dynamic-tags/tags/internal-url.php new file mode 100644 index 0000000..c801f5e --- /dev/null +++ b/modules/dynamic-tags/tags/internal-url.php @@ -0,0 +1,143 @@ +get_settings(); + + $type = $settings['type']; + $url = ''; + + if ( 'post' === $type && ! empty( $settings['post_id'] ) ) { + $url = get_permalink( (int) $settings['post_id'] ); + } elseif ( 'taxonomy' === $type && ! empty( $settings['taxonomy_id'] ) ) { + $url = get_term_link( (int) $settings['taxonomy_id'] ); + } elseif ( 'attachment' === $type && ! empty( $settings['attachment_id'] ) ) { + $url = get_attachment_link( (int) $settings['attachment_id'] ); + } elseif ( 'author' === $type && ! empty( $settings['author_id'] ) ) { + $url = get_author_posts_url( (int) $settings['author_id'] ); + } + + if ( ! is_wp_error( $url ) ) { + return $url; + } + + return ''; + } + + protected function register_controls() { + $this->add_control( 'type', [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'post' => esc_html__( 'Content', 'elementor-pro' ), + 'taxonomy' => esc_html__( 'Taxonomy', 'elementor-pro' ), + 'attachment' => esc_html__( 'Media', 'elementor-pro' ), + 'author' => esc_html__( 'Author', 'elementor-pro' ), + ], + ] ); + + $this->add_control( 'post_id', [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_POST, + 'display' => 'detailed', + 'query' => [ + 'post_type' => 'any', + ], + ], + 'condition' => [ + 'type' => 'post', + ], + ] ); + + $this->add_control( 'taxonomy_id', [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_TAX, + 'display' => 'detailed', + ], + 'condition' => [ + 'type' => 'taxonomy', + ], + ] ); + + $this->add_control( 'attachment_id', [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_ATTACHMENT, + 'display' => 'detailed', + ], + 'condition' => [ + 'type' => 'attachment', + ], + ] ); + + $this->add_control( 'author_id', [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_AUTHOR, + 'display' => 'detailed', + ], + 'condition' => [ + 'type' => 'author', + ], + ] ); + } +} diff --git a/modules/dynamic-tags/tags/lightbox.php b/modules/dynamic-tags/tags/lightbox.php new file mode 100644 index 0000000..208491f --- /dev/null +++ b/modules/dynamic-tags/tags/lightbox.php @@ -0,0 +1,142 @@ +add_control( + 'type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'video' => [ + 'title' => esc_html__( 'Video', 'elementor-pro' ), + 'icon' => 'eicon-video-camera', + ], + 'image' => [ + 'title' => esc_html__( 'Image', 'elementor-pro' ), + 'icon' => 'eicon-image-bold', + ], + ], + ] + ); + + $this->add_control( + 'image', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'condition' => [ + 'type' => 'image', + ], + ] + ); + + $this->add_control( + 'video_url', + [ + 'label' => esc_html__( 'Video URL', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'label_block' => true, + 'condition' => [ + 'type' => 'video', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + } + + private function get_image_settings( $settings ) { + $image_settings = [ + 'url' => $settings['image']['url'], + 'type' => 'image', + ]; + + $image_id = $settings['image']['id']; + + if ( $image_id ) { + $lightbox_image_attributes = Plugin::elementor()->images_manager->get_lightbox_image_attributes( $image_id ); + $image_settings = array_merge( $image_settings, $lightbox_image_attributes ); + } + + return $image_settings; + } + + private function get_video_settings( $settings ) { + $video_properties = Embed::get_video_properties( $settings['video_url'] ); + $video_url = null; + if ( ! $video_properties ) { + $video_type = 'hosted'; + $video_url = $settings['video_url']; + } else { + $video_type = $video_properties['provider']; + $video_url = Embed::get_embed_url( $settings['video_url'] ); + } + + if ( null === $video_url ) { + return ''; + } + + return [ + 'type' => 'video', + 'videoType' => $video_type, + 'url' => $video_url, + ]; + } + + public function render() { + $settings = $this->get_settings(); + + $value = []; + + if ( ! $settings['type'] ) { + return; + } + + if ( 'image' === $settings['type'] && $settings['image'] ) { + $value = $this->get_image_settings( $settings ); + } elseif ( 'video' === $settings['type'] && $settings['video_url'] ) { + $value = $this->get_video_settings( $settings ); + } + + if ( ! $value ) { + return; + } + + // PHPCS - the method Plugin::elementor()->frontend->create_action_hash is safe. + echo Plugin::elementor()->frontend->create_action_hash( 'lightbox', $value ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } +} diff --git a/modules/dynamic-tags/tags/page-title.php b/modules/dynamic-tags/tags/page-title.php new file mode 100644 index 0000000..9c50e42 --- /dev/null +++ b/modules/dynamic-tags/tags/page-title.php @@ -0,0 +1,74 @@ +get_settings( 'show_home_title' ) ) { + return; + } + + if ( Plugin::elementor()->common ) { + $current_action_data = Plugin::elementor()->common->get_component( 'ajax' )->get_current_action_data(); + + if ( $current_action_data && 'render_tags' === $current_action_data['action'] ) { + // Override the global $post for the render. + query_posts( + [ + 'p' => get_the_ID(), + 'post_type' => 'any', + ] + ); + } + } + + $include_context = 'yes' === $this->get_settings( 'include_context' ); + + $title = Utils::get_page_title( $include_context ); + + echo wp_kses_post( $title ); + } + + protected function register_controls() { + $this->add_control( + 'include_context', + [ + 'label' => esc_html__( 'Include Context', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + ] + ); + + $this->add_control( + 'show_home_title', + [ + 'label' => esc_html__( 'Show Home Title', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + ] + ); + } +} diff --git a/modules/dynamic-tags/tags/post-custom-field.php b/modules/dynamic-tags/tags/post-custom-field.php new file mode 100644 index 0000000..2702e77 --- /dev/null +++ b/modules/dynamic-tags/tags/post-custom-field.php @@ -0,0 +1,104 @@ +add_control( + 'key', + [ + 'label' => esc_html__( 'Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_keys_array(), + ] + ); + + $this->add_control( + 'custom_key', + [ + 'label' => esc_html__( 'Custom Key', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => 'key', + 'condition' => [ + 'key' => '', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + } + + public function render() { + $key = $this->get_settings( 'key' ); + + if ( empty( $key ) ) { + $key = $this->get_settings( 'custom_key' ); + } + + if ( empty( $key ) ) { + return; + } + + $value = get_post_meta( get_the_ID(), $key, true ); + + echo wp_kses_post( $value ); + } + + private function get_custom_keys_array() { + $custom_keys = get_post_custom_keys(); + $options = [ + '' => esc_html__( 'Select...', 'elementor-pro' ), + ]; + + if ( ! empty( $custom_keys ) ) { + foreach ( $custom_keys as $custom_key ) { + if ( '_' !== substr( $custom_key, 0, 1 ) ) { + $options[ $custom_key ] = $custom_key; + } + } + } + + return $options; + } +} diff --git a/modules/dynamic-tags/tags/post-date.php b/modules/dynamic-tags/tags/post-date.php new file mode 100644 index 0000000..e44efba --- /dev/null +++ b/modules/dynamic-tags/tags/post-date.php @@ -0,0 +1,102 @@ +add_control( + 'type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'post_date_gmt' => esc_html__( 'Post Published', 'elementor-pro' ), + 'post_modified_gmt' => esc_html__( 'Post Modified', 'elementor-pro' ), + ], + 'default' => 'post_date_gmt', + ] + ); + + $this->add_control( + 'format', + [ + 'label' => esc_html__( 'Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'F j, Y' => gmdate( 'F j, Y' ), + 'Y-m-d' => gmdate( 'Y-m-d' ), + 'm/d/Y' => gmdate( 'm/d/Y' ), + 'd/m/Y' => gmdate( 'd/m/Y' ), + 'human' => esc_html__( 'Human Readable', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + ] + ); + + $this->add_control( + 'custom_format', + [ + 'label' => esc_html__( 'Custom Format', 'elementor-pro' ), + 'default' => '', + 'description' => sprintf( '%s', esc_html__( 'Documentation on date and time formatting', 'elementor-pro' ) ), + 'condition' => [ + 'format' => 'custom', + ], + ] + ); + } + + public function render() { + $date_type = $this->get_settings( 'type' ); + $format = $this->get_settings( 'format' ); + + if ( 'human' === $format ) { + /* translators: %s: Human readable date/time. */ + $value = sprintf( esc_html__( '%s ago', 'elementor-pro' ), human_time_diff( strtotime( get_post()->{$date_type} ) ) ); + } else { + switch ( $format ) { + case 'default': + $date_format = ''; + break; + case 'custom': + $date_format = $this->get_settings( 'custom_format' ); + break; + default: + $date_format = $format; + break; + } + + if ( 'post_date_gmt' === $date_type ) { + $value = get_the_date( $date_format ); + } else { + $value = get_the_modified_date( $date_format ); + } + } + echo wp_kses_post( $value ); + } +} diff --git a/modules/dynamic-tags/tags/post-excerpt.php b/modules/dynamic-tags/tags/post-excerpt.php new file mode 100644 index 0000000..320489e --- /dev/null +++ b/modules/dynamic-tags/tags/post-excerpt.php @@ -0,0 +1,102 @@ +add_control( + 'max_length', + [ + 'label' => esc_html__( 'Excerpt Length', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + ] + ); + + $this->add_control( + 'apply_to_post_content', + [ + 'label' => esc_html__( 'Apply to post content', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'default' => 'no', + ] + ); + } + + public function get_categories() { + return [ Module::TEXT_CATEGORY ]; + } + + public function should_get_excerpt_from_post_content( $settings ) { + return 'yes' === $settings['apply_to_post_content']; + } + + public function is_post_excerpt_valid( $settings, $post ) { + if ( ! $post ) { + return false; + } + + if ( empty( $post->post_excerpt ) && ! $this->should_get_excerpt_from_post_content( $settings ) ) { + return false; + } + + if ( empty( $post->post_excerpt ) && empty( $post->post_content ) && $this->should_get_excerpt_from_post_content( $settings ) ) { + return false; + } + + if ( empty( $post->post_excerpt ) && empty( $post->post_content ) ) { + return false; + } + + return true; + } + + public function get_post_excerpt( $settings, $post ) { + $post_excerpt = $post->post_excerpt ?? ''; + + if ( empty( $post_excerpt ) && ! empty( $post->post_content ) && $this->should_get_excerpt_from_post_content( $settings ) ) { + $post_excerpt = apply_filters( 'the_excerpt', get_the_excerpt( $post ) ); + } + + return $post_excerpt; + } + + public function render() { + // Allow only a real `post_excerpt` and not the trimmed `post_content` from the `get_the_excerpt` filter + $post = get_post(); + $settings = $this->get_settings_for_display(); + + if ( ! $this->is_post_excerpt_valid( $settings, $post ) ) { + return; + } + + $max_length = (int) $settings['max_length']; + $excerpt = $this->get_post_excerpt( $settings, $post ); + + $excerpt = Utils::trim_words( $excerpt, $max_length ); + + echo wp_kses_post( $excerpt ); + } +} diff --git a/modules/dynamic-tags/tags/post-featured-image.php b/modules/dynamic-tags/tags/post-featured-image.php new file mode 100644 index 0000000..32863d5 --- /dev/null +++ b/modules/dynamic-tags/tags/post-featured-image.php @@ -0,0 +1,57 @@ + $thumbnail_id, + 'url' => wp_get_attachment_image_src( $thumbnail_id, 'full' )[0], + ]; + } else { + $image_data = $this->get_settings( 'fallback' ); + } + + return $image_data; + } + + protected function register_controls() { + $this->add_control( + 'fallback', + [ + 'label' => esc_html__( 'Fallback', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + ] + ); + } +} diff --git a/modules/dynamic-tags/tags/post-gallery.php b/modules/dynamic-tags/tags/post-gallery.php new file mode 100644 index 0000000..b7ecdb5 --- /dev/null +++ b/modules/dynamic-tags/tags/post-gallery.php @@ -0,0 +1,59 @@ +ID, array_column( $value, 'id' ), true ) ) { + $value[] = [ + 'id' => $image->ID, + ]; + } + } + + return $value; + } +} diff --git a/modules/dynamic-tags/tags/post-id.php b/modules/dynamic-tags/tags/post-id.php new file mode 100644 index 0000000..ef61714 --- /dev/null +++ b/modules/dynamic-tags/tags/post-id.php @@ -0,0 +1,34 @@ + true, + 'object_type' => [ get_post_type() ], + ]; + + /** + * Dynamic tags taxonomy args. + * + * Filters the taxonomy arguments used to retrieve the registered taxonomies + * displayed in the taxonomy dynamic tag. + * + * @since 2.0.0 + * + * @param array $taxonomy_filter_args An array of `key => value` arguments to + * match against the taxonomy objects inside + * the `get_taxonomies()` function. + */ + $taxonomy_filter_args = apply_filters( 'elementor_pro/dynamic_tags/post_terms/taxonomy_args', $taxonomy_filter_args ); + + $taxonomies = Utils::get_taxonomies( $taxonomy_filter_args, 'objects' ); + + $options = []; + + foreach ( $taxonomies as $taxonomy => $object ) { + $options[ $taxonomy ] = $object->label; + } + + $this->add_control( + 'taxonomy', + [ + 'label' => esc_html__( 'Taxonomy', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $options, + 'default' => 'post_tag', + ] + ); + + $this->add_control( + 'separator', + [ + 'label' => esc_html__( 'Separator', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => ', ', + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + ] + ); + } + + public function render() { + $settings = $this->get_settings(); + + if ( 'yes' === $settings['link'] ) { + $value = get_the_term_list( get_the_ID(), $settings['taxonomy'], '', $settings['separator'] ); + } else { + $terms = get_the_terms( get_the_ID(), $settings['taxonomy'] ); + + if ( is_wp_error( $terms ) || empty( $terms ) ) { + return ''; + } + + $term_names = []; + + foreach ( $terms as $term ) { + $term_names[] = '' . $term->name . ''; + } + + $value = implode( $settings['separator'], $term_names ); + } + + echo wp_kses_post( $value ); + } +} diff --git a/modules/dynamic-tags/tags/post-time.php b/modules/dynamic-tags/tags/post-time.php new file mode 100644 index 0000000..bc4f6e3 --- /dev/null +++ b/modules/dynamic-tags/tags/post-time.php @@ -0,0 +1,96 @@ +add_control( + 'type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'post_date_gmt' => esc_html__( 'Post Published', 'elementor-pro' ), + 'post_modified_gmt' => esc_html__( 'Post Modified', 'elementor-pro' ), + ], + 'default' => 'post_date_gmt', + ] + ); + + $this->add_control( + 'format', + [ + 'label' => esc_html__( 'Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'g:i a' => gmdate( 'g:i a' ), + 'g:i A' => gmdate( 'g:i A' ), + 'H:i' => gmdate( 'H:i' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + ] + ); + + $this->add_control( + 'custom_format', + [ + 'label' => esc_html__( 'Custom Format', 'elementor-pro' ), + 'default' => '', + 'description' => sprintf( '%s', esc_html__( 'Documentation on date and time formatting', 'elementor-pro' ) ), + 'condition' => [ + 'format' => 'custom', + ], + ] + ); + } + + public function render() { + $time_type = $this->get_settings( 'type' ); + $format = $this->get_settings( 'format' ); + + switch ( $format ) { + case 'default': + $date_format = ''; + break; + case 'custom': + $date_format = $this->get_settings( 'custom_format' ); + break; + default: + $date_format = $format; + break; + } + + if ( 'post_date_gmt' === $time_type ) { + $value = get_the_time( $date_format ); + } else { + $value = get_the_modified_time( $date_format ); + } + + echo wp_kses_post( $value ); + } +} diff --git a/modules/dynamic-tags/tags/post-title.php b/modules/dynamic-tags/tags/post-title.php new file mode 100644 index 0000000..5cd67bb --- /dev/null +++ b/modules/dynamic-tags/tags/post-title.php @@ -0,0 +1,31 @@ +get_settings(); + $request_type = isset( $settings['request_type'] ) ? strtoupper( $settings['request_type'] ) : false; + $param_name = isset( $settings['param_name'] ) ? $settings['param_name'] : false; + $value = ''; + + if ( ! $param_name || ! $request_type ) { + return ''; + } + + switch ( $request_type ) { + case 'POST': + $value = Utils::_unstable_get_super_global_value( $_POST, $param_name ) ?? ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing + break; + case 'GET': + $value = Utils::_unstable_get_super_global_value( $_GET, $param_name ) ?? ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + break; + case 'QUERY_VAR': + $value = get_query_var( $param_name ); + break; + } + echo htmlentities( wp_kses_post( $value ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + protected function register_controls() { + $this->add_control( + 'request_type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'get', + 'options' => [ + 'get' => 'Get', + 'post' => 'Post', + 'query_var' => 'Query Var', + ], + ] + ); + $this->add_control( + 'param_name', + [ + 'label' => esc_html__( 'Parameter Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + ] + ); + } +} diff --git a/modules/dynamic-tags/tags/shortcode.php b/modules/dynamic-tags/tags/shortcode.php new file mode 100644 index 0000000..6d918f2 --- /dev/null +++ b/modules/dynamic-tags/tags/shortcode.php @@ -0,0 +1,79 @@ +add_control( + 'shortcode', + [ + 'label' => esc_html__( 'Shortcode', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'ai' => [ + 'active' => false, + ], + ], + ); + } + + public function render() { + $settings = $this->get_settings(); + + if ( empty( $settings['shortcode'] ) ) { + return; + } + + $shortcode_string = $settings['shortcode']; + + $value = do_shortcode( $shortcode_string ); + + $should_escape = true; + + /** + * Should escape shortcodes. + * + * By default shortcodes in dynamic tags are escaped. This hook allows developers + * to avoid shortcodes from beeing escaped. Defaults to true. + * + * @since 2.2.1 + * + * @param bool $should_escape Whether to escape shortcodes in dynamic tags. + */ + $should_escape = apply_filters( 'elementor_pro/dynamic_tags/shortcode/should_escape', $should_escape ); + + if ( $should_escape ) { + $value = wp_kses_post( $value ); + } + // PHPCS - the variable $value is safe. + echo $value; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } +} diff --git a/modules/dynamic-tags/tags/site-logo.php b/modules/dynamic-tags/tags/site-logo.php new file mode 100644 index 0000000..487e28a --- /dev/null +++ b/modules/dynamic-tags/tags/site-logo.php @@ -0,0 +1,42 @@ + $custom_logo_id, + 'url' => $url, + ]; + } +} diff --git a/modules/dynamic-tags/tags/site-tagline.php b/modules/dynamic-tags/tags/site-tagline.php new file mode 100644 index 0000000..ec23605 --- /dev/null +++ b/modules/dynamic-tags/tags/site-tagline.php @@ -0,0 +1,31 @@ +get_settings( 'type' ); + $user = wp_get_current_user(); + if ( empty( $type ) || 0 === $user->ID ) { + return; + } + + $value = ''; + switch ( $type ) { + case 'login': + case 'email': + case 'url': + case 'nicename': + $field = 'user_' . $type; + $value = isset( $user->$field ) ? $user->$field : ''; + break; + case 'id': + $value = $user->ID; + break; + case 'description': + case 'first_name': + case 'last_name': + case 'display_name': + $value = isset( $user->$type ) ? $user->$type : ''; + break; + case 'meta': + $key = $this->get_settings( 'meta_key' ); + if ( ! empty( $key ) ) { + $value = get_user_meta( $user->ID, $key, true ); + } + break; + } + + echo wp_kses_post( $value ); + } + + public function get_panel_template_setting_key() { + return 'type'; + } + + protected function register_controls() { + $this->add_control( + 'type', + [ + 'label' => esc_html__( 'Field', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Choose', 'elementor-pro' ), + 'id' => esc_html__( 'ID', 'elementor-pro' ), + 'display_name' => esc_html__( 'Display Name', 'elementor-pro' ), + 'login' => esc_html__( 'Username', 'elementor-pro' ), + 'first_name' => esc_html__( 'First Name', 'elementor-pro' ), + 'last_name' => esc_html__( 'Last Name', 'elementor-pro' ), + 'description' => esc_html__( 'Bio', 'elementor-pro' ), + 'email' => esc_html__( 'Email', 'elementor-pro' ), + 'url' => esc_html__( 'Website', 'elementor-pro' ), + 'meta' => esc_html__( 'User Meta', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'meta_key', + [ + 'label' => esc_html__( 'Meta Key', 'elementor-pro' ), + 'condition' => [ + 'type' => 'meta', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + } +} diff --git a/modules/dynamic-tags/tags/user-profile-picture.php b/modules/dynamic-tags/tags/user-profile-picture.php new file mode 100644 index 0000000..0bb7d5d --- /dev/null +++ b/modules/dynamic-tags/tags/user-profile-picture.php @@ -0,0 +1,21 @@ + '', + 'url' => get_avatar_url( get_current_user_id() ), + ]; + } +} diff --git a/modules/dynamic-tags/toolset/module.php b/modules/dynamic-tags/toolset/module.php new file mode 100644 index 0000000..55a3a16 --- /dev/null +++ b/modules/dynamic-tags/toolset/module.php @@ -0,0 +1,114 @@ + $field ) { + if ( ! is_array( $field ) || empty( $field['type'] ) ) { + continue; + } + + if ( ! self::valid_field_type( $types, $field ) ) { + continue; + } + + // Use group ID for unique keys + $key = $group['slug'] . ':' . $field_key; + $options[ $key ] = $field['name']; + } + + if ( empty( $options ) ) { + continue; + } + + if ( 1 === count( $options ) ) { + $options = [ -1 => ' -- ' ] + $options; + } + + $groups[] = [ + 'label' => $group['name'], + 'options' => $options, + ]; + } + + return $groups; + } + + public static function toolset_image_mapping( $field, $single = true ) { + if ( 'image' !== $field['type'] ) { + return false; + } + + $limit = $single ? '0' : '1'; + if ( empty( $field['data'] ) || $limit !== $field['data']['repetitive'] ) { + return false; + } + + return true; + } + + public static function valid_field_type( $types, $field ) { + // Only file field with single image value + if ( in_array( 'toolset_image', $types, true ) && self::toolset_image_mapping( $field ) ) { + return true; + } + + // Only file with multiple images allowed + if ( in_array( 'toolset_gallery', $types, true ) && self::toolset_image_mapping( $field, false ) ) { + return true; + } + + // Any other type + if ( in_array( $field['type'], $types, true ) ) { + return true; + } + + return false; + } + + public function get_tag_classes_names() { + return [ + 'Toolset_Text', + 'Toolset_Date', + 'Toolset_Image', + 'Toolset_URL', + 'Toolset_Gallery', + ]; + } + + public function get_groups() { + return [ + self::TOOLSET_GROUP => [ + 'title' => esc_html__( 'Toolset', 'elementor-pro' ), + ], + ]; + } +} diff --git a/modules/dynamic-tags/toolset/tags/toolset-base.php b/modules/dynamic-tags/toolset/tags/toolset-base.php new file mode 100644 index 0000000..1c99e75 --- /dev/null +++ b/modules/dynamic-tags/toolset/tags/toolset-base.php @@ -0,0 +1,39 @@ +add_control( + 'key', + [ + 'label' => esc_html__( 'Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => Module::get_control_options( $this->get_supported_fields() ), + ] + ); + } + + protected function get_supported_fields() { + return []; + } +} diff --git a/modules/dynamic-tags/toolset/tags/toolset-date.php b/modules/dynamic-tags/toolset/tags/toolset-date.php new file mode 100644 index 0000000..866d1f7 --- /dev/null +++ b/modules/dynamic-tags/toolset/tags/toolset-date.php @@ -0,0 +1,112 @@ +get_settings( 'key' ); + if ( empty( $key ) ) { + return; + } + + list( $field_group, $field_key ) = explode( ':', $key ); + + $field = wpcf_admin_fields_get_field( $field_key ); + $value = ''; + + if ( $field && ! empty( $field['type'] ) && 'date' === $field['type'] ) { + + $format = $this->get_settings( 'format' ); + + $timestamp = types_render_field( $field_key, [ + 'output' => 'raw', + 'style' => 'text', + ] ); + + if ( empty( $timestamp ) ) { + return $value; + } + + if ( 'human' === $format ) { + /* translators: %s: Human readable date/time. */ + $value = human_time_diff( $timestamp ); + } else { + switch ( $format ) { + case 'default': + $date_format = get_option( 'date_format' ); + break; + case 'custom': + $date_format = $this->get_settings( 'custom_format' ); + break; + default: + $date_format = $format; + break; + } + + $value = date_i18n( $date_format, $timestamp ); + } + } + echo wp_kses_post( $value ); + } + + public function get_panel_template_setting_key() { + return 'key'; + } + + protected function register_controls() { + parent::register_controls(); + + $this->add_control( + 'format', + [ + 'label' => esc_html__( 'Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'F j, Y' => gmdate( 'F j, Y' ), + 'Y-m-d' => gmdate( 'Y-m-d' ), + 'm/d/Y' => gmdate( 'm/d/Y' ), + 'd/m/Y' => gmdate( 'd/m/Y' ), + 'human' => esc_html__( 'Human Readable', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + ] + ); + + $this->add_control( + 'custom_format', + [ + 'label' => esc_html__( 'Custom Format', 'elementor-pro' ), + 'default' => '', + 'description' => sprintf( '%s', esc_html__( 'Documentation on date and time formatting', 'elementor-pro' ) ), + 'condition' => [ + 'format' => 'custom', + ], + ] + ); + } + + protected function get_supported_fields() { + return [ 'date' ]; + } +} diff --git a/modules/dynamic-tags/toolset/tags/toolset-gallery.php b/modules/dynamic-tags/toolset/tags/toolset-gallery.php new file mode 100644 index 0000000..93c2ae3 --- /dev/null +++ b/modules/dynamic-tags/toolset/tags/toolset-gallery.php @@ -0,0 +1,113 @@ +get_settings( 'key' ); + if ( empty( $key ) ) { + return []; + } + + $images = []; + + list( $field_group, $field_key ) = explode( ':', $key ); + + $field = wpcf_admin_fields_get_field( $field_key ); + + if ( $field && ! empty( $field['type'] ) ) { + + $galley_images = types_render_field( $field_key, [ + 'separator' => '|', + 'url' => true, + ] ); + $galley_images = explode( '|', $galley_images ); + foreach ( $galley_images as $image_url ) { + $images[] = [ + 'id' => $this->get_cached_attachment_url_to_post_id( $image_url ), + 'url' => $image_url, + ]; + } + } + + return $images; + } + + protected function register_controls() { + $this->add_control( + 'key', + [ + 'label' => esc_html__( 'Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => Module::get_control_options( $this->get_supported_fields() ), + ] + ); + } + + protected function get_supported_fields() { + return [ + 'toolset_gallery', + ]; + } + + /** + * @param $attachment_url + * + * @return false|int|mixed + */ + private function get_cached_attachment_url_to_post_id( $attachment_url ) { + $id = wp_cache_get( $attachment_url, __CLASS__ ); + + if ( false === $id ) { + $id = attachment_url_to_postid( $attachment_url ); + + wp_cache_set( $attachment_url, $id, __CLASS__ ); + } + + return $id; + } + + /** + * Toolset_Gallery constructor. + * + * @param array $data + */ + public function __construct( array $data = [] ) { + parent::__construct( $data ); + + wp_cache_add_non_persistent_groups( __CLASS__ ); + } +} diff --git a/modules/dynamic-tags/toolset/tags/toolset-image.php b/modules/dynamic-tags/toolset/tags/toolset-image.php new file mode 100644 index 0000000..0e70e72 --- /dev/null +++ b/modules/dynamic-tags/toolset/tags/toolset-image.php @@ -0,0 +1,91 @@ +get_settings( 'key' ); + $image_data = $this->get_settings( 'fallback' ); + if ( empty( $key ) ) { + return $image_data; + } + + list( $field_group, $field_key ) = explode( ':', $key ); + + $field = wpcf_admin_fields_get_field( $field_key ); + + if ( $field && ! empty( $field['type'] ) ) { + + $url = types_render_field( $field_key, [ 'url' => true ] ); + + if ( empty( $url ) ) { + return $image_data; + } + + $image_data = [ + 'id' => attachment_url_to_postid( $url ), + 'url' => $url, + ]; + } + + return $image_data; + } + + protected function register_controls() { + $this->add_control( + 'key', + [ + 'label' => esc_html__( 'Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => Module::get_control_options( $this->get_supported_fields() ), + ] + ); + + $this->add_control( + 'fallback', + [ + 'label' => esc_html__( 'Fallback', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + ] + ); + } + + protected function get_supported_fields() { + return [ + 'toolset_image', + ]; + } +} diff --git a/modules/dynamic-tags/toolset/tags/toolset-text.php b/modules/dynamic-tags/toolset/tags/toolset-text.php new file mode 100644 index 0000000..6773f0f --- /dev/null +++ b/modules/dynamic-tags/toolset/tags/toolset-text.php @@ -0,0 +1,70 @@ +get_settings( 'key' ); + if ( empty( $key ) ) { + return; + } + + list( $field_group, $field_key ) = explode( ':', $key ); + + $field = wpcf_admin_fields_get_field( $field_key ); + + if ( $field && ! empty( $field['type'] ) ) { + $value = ''; + switch ( $field['type'] ) { + case 'google_address': + $value = types_render_field( $field_key, [ 'format' => 'FIELD_ADDRESS' ] ); + break; + case 'email': + case 'embed': + $value = types_render_field( $field_key, [ 'output' => 'raw' ] ); + break; + default: + $value = types_render_field( $field_key ); + break; + } // End switch(). + } else { + // Field settings has been deleted or not available. + $value = types_render_field( $field_key ); + } // End if(). + + echo wp_kses_post( $value ); + } + + protected function get_supported_fields() { + return [ + 'textfield', + 'phone', + 'textarea', + 'checkbox', + 'select', + 'numeric', + 'email', + 'embed', + 'google_address', + 'wysiwyg', + 'radio', + ]; + } +} diff --git a/modules/dynamic-tags/toolset/tags/toolset-url.php b/modules/dynamic-tags/toolset/tags/toolset-url.php new file mode 100644 index 0000000..ffb4efe --- /dev/null +++ b/modules/dynamic-tags/toolset/tags/toolset-url.php @@ -0,0 +1,97 @@ +get_settings( 'key' ); + if ( empty( $key ) ) { + return; + } + + list( $field_group, $field_key ) = explode( ':', $key ); + + $field = wpcf_admin_fields_get_field( $field_key ); + + if ( $field && ! empty( $field['type'] ) ) { + $value = ''; + switch ( $field['type'] ) { + case 'email': + $value = 'mailto:' . types_render_field( $field_key, [ 'output' => 'raw' ] ); + break; + case 'image': + $value = types_render_field( $field_key, [ 'url' => true ] ); + break; + default: + $value = types_render_field( $field_key, [ 'output' => 'raw' ] ); + } // End switch(). + } + + if ( empty( $value ) && $this->get_settings( 'fallback' ) ) { + $value = $this->get_settings( 'fallback' ); + } + + return wp_kses_post( $value ); + } + + protected function register_controls() { + $this->add_control( + 'key', + [ + 'label' => esc_html__( 'Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => Module::get_control_options( $this->get_supported_fields() ), + ] + ); + + $this->add_control( + 'fallback', + [ + 'label' => esc_html__( 'Fallback', 'elementor-pro' ), + ] + ); + } + + protected function get_supported_fields() { + return [ + 'email', + 'image', + 'file', + 'audio', + 'url', + ]; + } +} diff --git a/modules/element-manager/module.php b/modules/element-manager/module.php new file mode 100644 index 0000000..e62db88 --- /dev/null +++ b/modules/element-manager/module.php @@ -0,0 +1,83 @@ + $restricted_roles ) { + $compare_roles = array_intersect( $user->roles, $restricted_roles ); + + if ( ! empty( $compare_roles ) ) { + $additional_config['widgets'][ $element_name ]['show_in_panel'] = false; + } + } + + return $additional_config; + }, 100, 2 ); + + add_action( 'elementor/element_manager/save_disabled_elements', function() { + $role_restrictions = Utils::get_super_global_value( $_POST, 'elements_restriction' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + + if ( empty( $role_restrictions ) ) { + return; + } + + $role_restrictions = json_decode( $role_restrictions, true ); + + if ( is_array( $role_restrictions ) ) { + Options::update_role_restrictions( $role_restrictions ); + } + } ); + + add_filter( 'elementor/element_manager/admin_app_data/additional_data', function( $additional_data ) { + $additional_data['roles'] = $this->get_roles(); + $additional_data['role_restrictions'] = Options::get_role_restrictions(); + + return $additional_data; + } ); + } + + private function get_roles() : array { + $roles = []; + + foreach ( get_editable_roles() as $role => $details ) { + if ( 'administrator' === $role ) { + continue; + } + + $name = translate_user_role( $details['name'] ); + + $roles[] = [ + 'id' => $role, + 'name' => $name, + ]; + } + + return $roles; + } +} diff --git a/modules/element-manager/options.php b/modules/element-manager/options.php new file mode 100644 index 0000000..ed7ea63 --- /dev/null +++ b/modules/element-manager/options.php @@ -0,0 +1,17 @@ +start_controls_section( + 'section_side_a_content', + [ + 'label' => esc_html__( 'Front', 'elementor-pro' ), + ] + ); + + $this->start_controls_tabs( 'side_a_content_tabs' ); + + $this->start_controls_tab( 'side_a_content_tab', [ 'label' => esc_html__( 'Content', 'elementor-pro' ) ] ); + + $this->add_control( + 'graphic_element', + [ + 'label' => esc_html__( 'Graphic Element', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'none' => [ + 'title' => esc_html__( 'None', 'elementor-pro' ), + 'icon' => 'eicon-ban', + ], + 'image' => [ + 'title' => esc_html__( 'Image', 'elementor-pro' ), + 'icon' => 'eicon-image-bold', + ], + 'icon' => [ + 'title' => esc_html__( 'Icon', 'elementor-pro' ), + 'icon' => 'eicon-star', + ], + ], + 'default' => 'icon', + ] + ); + + $this->add_control( + 'image', + [ + 'label' => esc_html__( 'Choose Image', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'default' => [ + 'url' => Utils::get_placeholder_image_src(), + ], + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'graphic_element' => 'image', + ], + ] + ); + + $this->add_group_control( + Group_Control_Image_Size::get_type(), + [ + 'name' => 'image', // Actually its `image_size` + 'default' => 'thumbnail', + 'condition' => [ + 'graphic_element' => 'image', + ], + ] + ); + + $this->add_control( + 'selected_icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'default' => [ + 'value' => 'fas fa-star', + 'library' => 'fa-solid', + ], + 'condition' => [ + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'icon_view', + [ + 'label' => esc_html__( 'View', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'stacked' => esc_html__( 'Stacked', 'elementor-pro' ), + 'framed' => esc_html__( 'Framed', 'elementor-pro' ), + ], + 'default' => 'default', + 'condition' => [ + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'icon_shape', + [ + 'label' => esc_html__( 'Shape', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'circle' => esc_html__( 'Circle', 'elementor-pro' ), + 'square' => esc_html__( 'Square', 'elementor-pro' ), + ], + 'default' => 'circle', + 'condition' => [ + 'icon_view!' => 'default', + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'title_text_a', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'This is the heading', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Enter your title', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'label_block' => true, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'description_text_a', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Enter your description', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'rows' => 10, + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'side_a_background_tab', [ 'label' => esc_html__( 'Background', 'elementor-pro' ) ] ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'background_a', + 'types' => [ 'classic', 'gradient' ], + 'selector' => '{{WRAPPER}} .elementor-flip-box__front', + ] + ); + + $this->add_control( + 'background_overlay_a', + [ + 'label' => esc_html__( 'Background Overlay', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__overlay' => 'background-color: {{VALUE}};', + ], + 'separator' => 'before', + 'condition' => [ + 'background_a_image[id]!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Css_Filter::get_type(), + [ + 'name' => 'background_overlay_a_filters', + 'selector' => '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__overlay', + 'condition' => [ + 'background_overlay_a!' => '', + ], + ] + ); + + $this->add_control( + 'background_overlay_a_blend_mode', + [ + 'label' => esc_html__( 'Blend Mode', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Normal', 'elementor-pro' ), + 'multiply' => 'Multiply', + 'screen' => 'Screen', + 'overlay' => 'Overlay', + 'darken' => 'Darken', + 'lighten' => 'Lighten', + 'color-dodge' => 'Color Dodge', + 'color-burn' => 'Color Burn', + 'hue' => 'Hue', + 'saturation' => 'Saturation', + 'color' => 'Color', + 'exclusion' => 'Exclusion', + 'luminosity' => 'Luminosity', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__overlay' => 'mix-blend-mode: {{VALUE}}', + ], + 'condition' => [ + 'background_overlay_a!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_side_b_content', + [ + 'label' => esc_html__( 'Back', 'elementor-pro' ), + ] + ); + + $this->start_controls_tabs( 'side_b_content_tabs' ); + + $this->start_controls_tab( 'side_b_content_tab', [ 'label' => esc_html__( 'Content', 'elementor-pro' ) ] ); + + $this->add_control( + 'title_text_b', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'This is the heading', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Enter your title', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'label_block' => true, + ] + ); + + $this->add_control( + 'description_text_b', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Enter your description', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'rows' => 10, + ] + ); + + $this->add_control( + 'button_text', + [ + 'label' => esc_html__( 'Button Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Click Here', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'link_click', + [ + 'label' => esc_html__( 'Apply Link On', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'box' => esc_html__( 'Whole Box', 'elementor-pro' ), + 'button' => esc_html__( 'Button Only', 'elementor-pro' ), + ], + 'default' => 'button', + 'condition' => [ + 'link[url]!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'side_b_background_tab', [ 'label' => esc_html__( 'Background', 'elementor-pro' ) ] ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'background_b', + 'types' => [ 'classic', 'gradient' ], + 'selector' => '{{WRAPPER}} .elementor-flip-box__back', + ] + ); + + $this->add_control( + 'background_overlay_b', + [ + 'label' => esc_html__( 'Background Overlay', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__overlay' => 'background-color: {{VALUE}};', + ], + 'separator' => 'before', + 'condition' => [ + 'background_b_image[id]!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Css_Filter::get_type(), + [ + 'name' => 'background_overlay_b_filters', + 'selector' => '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__overlay', + 'condition' => [ + 'background_overlay_b!' => '', + ], + ] + ); + + $this->add_control( + 'background_overlay_b_blend_mode', + [ + 'label' => esc_html__( 'Blend Mode', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Normal', 'elementor-pro' ), + 'multiply' => 'Multiply', + 'screen' => 'Screen', + 'overlay' => 'Overlay', + 'darken' => 'Darken', + 'lighten' => 'Lighten', + 'color-dodge' => 'Color Dodge', + 'color-burn' => 'Color Burn', + 'hue' => 'Hue', + 'saturation' => 'Saturation', + 'color' => 'Color', + 'exclusion' => 'Exclusion', + 'luminosity' => 'Luminosity', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__overlay' => 'mix-blend-mode: {{VALUE}}', + ], + 'condition' => [ + 'background_overlay_b!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_box_settings', + [ + 'label' => esc_html__( 'Settings', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'title_tag', + [ + 'label' => esc_html__( 'Title HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + 'p' => 'p', + ], + 'default' => 'h3', + ] + ); + + $this->add_control( + 'description_tag', + [ + 'label' => esc_html__( 'Description HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + 'p' => 'p', + ], + 'default' => 'div', + ] + ); + + $this->add_responsive_control( + 'height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 100, + 'max' => 1000, + ], + 'em' => [ + 'min' => 10, + 'max' => 100, + ], + 'rem' => [ + 'min' => 10, + 'max' => 100, + ], + 'vh' => [ + 'min' => 10, + 'max' => 100, + ], + ], + 'separator' => 'before', + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box' => 'height: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'separator' => 'after', + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__layer, {{WRAPPER}} .elementor-flip-box__layer__overlay' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'flip_effect', + [ + 'label' => esc_html__( 'Flip Effect', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'flip', + 'options' => [ + 'flip' => 'Flip', + 'slide' => 'Slide', + 'push' => 'Push', + 'zoom-in' => 'Zoom In', + 'zoom-out' => 'Zoom Out', + 'fade' => 'Fade', + ], + 'prefix_class' => 'elementor-flip-box--effect-', + ] + ); + + $this->add_control( + 'flip_direction', + [ + 'label' => esc_html__( 'Flip Direction', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'up', + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + 'up' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'down' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'toggle' => false, + 'condition' => [ + 'flip_effect!' => [ + 'fade', + 'zoom-in', + 'zoom-out', + ], + ], + 'prefix_class' => 'elementor-flip-box--direction-', + ] + ); + + $this->add_control( + 'flip_3d', + [ + 'label' => esc_html__( '3D Depth', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'return_value' => 'elementor-flip-box--3d', + 'default' => '', + 'prefix_class' => '', + 'condition' => [ + 'flip_effect' => 'flip', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_a', + [ + 'label' => esc_html__( 'Front', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'padding_a', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__overlay' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'alignment_a', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'default' => 'center', + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__overlay' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'vertical_position_a', + [ + 'label' => esc_html__( 'Vertical Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'middle' => [ + 'title' => esc_html__( 'Middle', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'selectors_dictionary' => [ + 'top' => 'flex-start', + 'middle' => 'center', + 'bottom' => 'flex-end', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__overlay' => 'justify-content: {{VALUE}}', + ], + 'separator' => 'after', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'border_a', + 'selector' => '{{WRAPPER}} .elementor-flip-box__front', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'heading_image_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'condition' => [ + 'graphic_element' => 'image', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'image_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__image' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'graphic_element' => 'image', + ], + ] + ); + + $this->add_control( + 'image_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'range' => [ + '%' => [ + 'min' => 5, + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__image img' => 'width: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'graphic_element' => 'image', + ], + ] + ); + + $this->add_control( + 'image_opacity', + [ + 'label' => esc_html__( 'Opacity', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 1, + ], + 'range' => [ + 'px' => [ + 'max' => 1, + 'min' => 0.10, + 'step' => 0.01, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__image' => 'opacity: {{SIZE}};', + ], + 'condition' => [ + 'graphic_element' => 'image', + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'image_border', + 'selector' => '{{WRAPPER}} .elementor-flip-box__image img', + 'condition' => [ + 'graphic_element' => 'image', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'image_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__image img' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'graphic_element' => 'image', + ], + ] + ); + + $this->add_control( + 'heading_icon_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'condition' => [ + 'graphic_element' => 'icon', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'icon_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-wrapper' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'icon_primary_color', + [ + 'label' => esc_html__( 'Primary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-view-stacked .elementor-icon' => 'background-color: {{VALUE}}', + '{{WRAPPER}} .elementor-view-stacked .elementor-icon svg' => 'stroke: {{VALUE}}', + '{{WRAPPER}} .elementor-view-framed .elementor-icon, {{WRAPPER}} .elementor-view-default .elementor-icon' => 'color: {{VALUE}}; border-color: {{VALUE}}', + '{{WRAPPER}} .elementor-view-framed .elementor-icon svg, {{WRAPPER}} .elementor-view-default .elementor-icon svg' => 'fill: {{VALUE}}; border-color: {{VALUE}}', + ], + 'condition' => [ + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'icon_secondary_color', + [ + 'label' => esc_html__( 'Secondary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'condition' => [ + 'graphic_element' => 'icon', + 'icon_view!' => 'default', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-view-framed .elementor-icon' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .elementor-view-framed .elementor-icon svg' => 'stroke: {{VALUE}};', + '{{WRAPPER}} .elementor-view-stacked .elementor-icon' => 'color: {{VALUE}};', + '{{WRAPPER}} .elementor-view-stacked .elementor-icon svg' => 'fill: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'icon_size', + [ + 'label' => esc_html__( 'Icon Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 6, + 'max' => 300, + ], + 'em' => [ + 'min' => 0.6, + 'max' => 30, + ], + 'rem' => [ + 'min' => 0.6, + 'max' => 30, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon' => 'font-size: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .elementor-icon svg' => 'width: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'icon_padding', + [ + 'label' => esc_html__( 'Icon Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'selectors' => [ + '{{WRAPPER}} .elementor-icon' => 'padding: {{SIZE}}{{UNIT}};', + ], + 'range' => [ + 'em' => [ + 'min' => 0, + 'max' => 5, + ], + ], + 'condition' => [ + 'graphic_element' => 'icon', + 'icon_view!' => 'default', + ], + ] + ); + + $this->add_control( + 'icon_rotate', + [ + 'label' => esc_html__( 'Icon Rotate', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'deg', 'grad', 'rad', 'turn' ], + 'default' => [ + 'size' => 0, + 'unit' => 'deg', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon i' => 'transform: rotate({{SIZE}}{{UNIT}});', + '{{WRAPPER}} .elementor-icon svg' => 'transform: rotate({{SIZE}}{{UNIT}});', + ], + 'condition' => [ + 'graphic_element' => 'icon', + ], + ] + ); + + $this->add_control( + 'icon_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon' => 'border-width: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'graphic_element' => 'icon', + 'icon_view' => 'framed', + ], + ] + ); + + $this->add_control( + 'icon_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'graphic_element' => 'icon', + 'icon_view!' => 'default', + ], + ] + ); + + $this->add_control( + 'heading_title_style_a', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'separator' => 'before', + 'condition' => [ + 'title_text_a!' => '', + ], + ] + ); + + $this->add_control( + 'title_spacing_a', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__title' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'description_text_a!' => '', + 'title_text_a!' => '', + ], + ] + ); + + $this->add_control( + 'title_color_a', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__title' => 'color: {{VALUE}}', + + ], + 'condition' => [ + 'title_text_a!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'title_typography_a', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__title', + 'condition' => [ + 'title_text_a!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Stroke::get_type(), + [ + 'name' => 'text_stroke', + 'selector' => '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__title', + ] + ); + + $this->add_control( + 'heading_description_style_a', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'separator' => 'before', + 'condition' => [ + 'description_text_a!' => '', + ], + ] + ); + + $this->add_control( + 'description_color_a', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__description' => 'color: {{VALUE}}', + + ], + 'condition' => [ + 'description_text_a!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'description_typography_a', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-flip-box__front .elementor-flip-box__layer__description', + 'condition' => [ + 'description_text_a!' => '', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_b', + [ + 'label' => esc_html__( 'Back', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'padding_b', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__overlay' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'alignment_b', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'default' => 'center', + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__overlay' => 'text-align: {{VALUE}}', + '{{WRAPPER}} .elementor-flip-box__button' => 'margin-{{VALUE}}: 0', + ], + ] + ); + + $this->add_control( + 'vertical_position_b', + [ + 'label' => esc_html__( 'Vertical Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'middle' => [ + 'title' => esc_html__( 'Middle', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'selectors_dictionary' => [ + 'top' => 'flex-start', + 'middle' => 'center', + 'bottom' => 'flex-end', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__overlay' => 'justify-content: {{VALUE}}', + ], + 'separator' => 'after', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'border_b', + 'selector' => '{{WRAPPER}} .elementor-flip-box__back', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'heading_title_style_b', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'separator' => 'before', + 'condition' => [ + 'title_text_b!' => '', + ], + ] + ); + + $this->add_control( + 'title_spacing_b', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__title' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'title_text_b!' => '', + ], + ] + ); + + $this->add_control( + 'title_color_b', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__title' => 'color: {{VALUE}}', + + ], + 'condition' => [ + 'title_text_b!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'title_typography_b', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__title', + 'condition' => [ + 'title_text_b!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Stroke::get_type(), + [ + 'name' => 'text_stroke_b', + 'selector' => '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__title', + ] + ); + + $this->add_control( + 'heading_description_style_b', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'separator' => 'before', + 'condition' => [ + 'description_text_b!' => '', + ], + ] + ); + + $this->add_control( + 'description_spacing_b', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__description' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'description_text_b!' => '', + 'button_text!' => '', + ], + ] + ); + + $this->add_control( + 'description_color_b', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__description' => 'color: {{VALUE}}', + + ], + 'condition' => [ + 'description_text_b!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'description_typography_b', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-flip-box__back .elementor-flip-box__layer__description', + 'condition' => [ + 'description_text_b!' => '', + ], + ] + ); + + $this->add_control( + 'heading_button', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'separator' => 'before', + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'sm', + 'options' => [ + 'xs' => esc_html__( 'Extra Small', 'elementor-pro' ), + 'sm' => esc_html__( 'Small', 'elementor-pro' ), + 'md' => esc_html__( 'Medium', 'elementor-pro' ), + 'lg' => esc_html__( 'Large', 'elementor-pro' ), + 'xl' => esc_html__( 'Extra Large', 'elementor-pro' ), + ], + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'button_typography', + 'selector' => '{{WRAPPER}} .elementor-flip-box__button', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->start_controls_tabs( 'button_tabs' ); + + $this->start_controls_tab( 'normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__button' => 'color: {{VALUE}};', + ], + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'button_background', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-flip-box__button', + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + ], + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__button' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + 'condition' => [ + 'button_text!' => '', + ], + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__button:hover' => 'color: {{VALUE}};', + ], + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'button_hover_background', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-flip-box__button:hover', + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + ], + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__button:hover' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__button' => 'transition-duration: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'button_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__button' => 'border-width: {{SIZE}}{{UNIT}};', + ], + 'separator' => 'before', + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-flip-box__button' => 'border-radius: {{SIZE}}{{UNIT}};', + ], + 'separator' => 'after', + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->end_controls_section(); + + } + + protected function render() { + $settings = $this->get_settings_for_display(); + $wrapper_tag = 'div'; + $button_tag = 'a'; + $title_tag = Utils::validate_html_tag( $settings['title_tag'] ); + $description_tag = Utils::validate_html_tag( $settings['description_tag'] ); + $migration_allowed = Icons_Manager::is_migration_allowed(); + $this->add_render_attribute( 'button', 'class', [ + 'elementor-flip-box__button', + 'elementor-button', + 'elementor-size-' . $settings['button_size'], + ] ); + + $this->add_render_attribute( 'wrapper', 'class', 'elementor-flip-box__layer elementor-flip-box__back' ); + + if ( ! empty( $settings['link']['url'] ) ) { + $link_element = 'button'; + + if ( 'box' === $settings['link_click'] ) { + $wrapper_tag = 'a'; + $button_tag = 'span'; + $link_element = 'wrapper'; + } + + $this->add_link_attributes( $link_element, $settings['link'] ); + } + + if ( 'icon' === $settings['graphic_element'] ) { + $this->add_render_attribute( 'icon-wrapper', 'class', 'elementor-icon-wrapper' ); + $this->add_render_attribute( 'icon-wrapper', 'class', 'elementor-view-' . $settings['icon_view'] ); + if ( 'default' != $settings['icon_view'] ) { + $this->add_render_attribute( 'icon-wrapper', 'class', 'elementor-shape-' . $settings['icon_shape'] ); + } + + if ( ! isset( $settings['icon'] ) && ! $migration_allowed ) { + // add old default + $settings['icon'] = 'fa fa-star'; + } + + if ( ! empty( $settings['icon'] ) ) { + $this->add_render_attribute( 'icon', 'class', $settings['icon'] ); + } + } + + $has_icon = ! empty( $settings['icon'] ) || ! empty( $settings['selected_icon'] ); + $migrated = isset( $settings['__fa4_migrated']['selected_icon'] ); + $is_new = empty( $settings['icon'] ) && $migration_allowed; + + ?> +
    +
    +
    +
    + +
    + +
    + +
    print_render_attribute_string( 'icon-wrapper' ); ?>> +
    + + print_render_attribute_string( 'icon' ); ?>> + +
    +
    + + + + < class="elementor-flip-box__layer__title"> + print_unescaped_setting( 'title_text_a' ); ?> + > + + + + < class="elementor-flip-box__layer__description"> + print_unescaped_setting( 'description_text_a' ); ?> + > + +
    +
    +
    + < print_render_attribute_string( 'wrapper' ); ?>> +
    +
    + + < class="elementor-flip-box__layer__title"> + print_unescaped_setting( 'title_text_b' ); ?> + > + + + + < class="elementor-flip-box__layer__description"> + print_unescaped_setting( 'description_text_b' ); ?> + > + + + + < print_render_attribute_string( 'button' ); ?>> + print_unescaped_setting( 'button_text' ); ?> + > + +
    +
    + > +
    + + <# + var btnClasses = 'elementor-flip-box__button elementor-button elementor-size-' + settings.button_size; + + if ( 'image' === settings.graphic_element && '' !== settings.image.url ) { + var image = { + id: settings.image.id, + url: settings.image.url, + size: settings.image_size, + dimension: settings.image_custom_dimension, + model: view.getEditModel() + }; + + var imageUrl = elementor.imagesManager.getImageUrl( image ); + } + + var wrapperTag = 'div', + buttonTag = 'a', + titleTag = elementor.helpers.validateHTMLTag( settings.title_tag ), + descriptionTag = elementor.helpers.validateHTMLTag( settings.description_tag ); + + if ( 'box' === settings.link_click ) { + wrapperTag = 'a'; + buttonTag = 'span'; + } + + if ( 'icon' === settings.graphic_element ) { + var iconWrapperClasses = 'elementor-icon-wrapper'; + iconWrapperClasses += ' elementor-view-' + settings.icon_view; + if ( 'default' !== settings.icon_view ) { + iconWrapperClasses += ' elementor-shape-' + settings.icon_shape; + } + } + var hasIcon = settings.icon || settings.selected_icon, + iconHTML = elementor.helpers.renderIcon( view, settings.selected_icon, { 'aria-hidden': true }, 'i' , 'object' ), + migrated = elementor.helpers.isIconMigrated( settings, 'selected_icon' ); + #> + +
    +
    +
    +
    + <# if ( 'image' === settings.graphic_element && '' !== settings.image.url ) { #> +
    + +
    + <# } else if ( 'icon' === settings.graphic_element && hasIcon ) { #> +
    +
    + <# if ( iconHTML && iconHTML.rendered && ( ! settings.icon || migrated ) ) { #> + {{{ iconHTML.value }}} + <# } else { #> + + <# } #> +
    +
    + <# } #> + + <# if ( settings.title_text_a ) { #> + <{{ titleTag }} class="elementor-flip-box__layer__title">{{{ settings.title_text_a }}} + <# } #> + + <# if ( settings.description_text_a ) { #> + <{{ descriptionTag }} class="elementor-flip-box__layer__description">{{{ settings.description_text_a }}} + <# } #> +
    +
    +
    + <{{ wrapperTag }} class="elementor-flip-box__layer elementor-flip-box__back"> +
    +
    + <# if ( settings.title_text_b ) { #> + <{{ titleTag }} class="elementor-flip-box__layer__title">{{{ settings.title_text_b }}} + <# } #> + + <# if ( settings.description_text_b ) { #> + <{{ descriptionTag }} class="elementor-flip-box__layer__description">{{{ settings.description_text_b }}} + <# } #> + + <# if ( settings.button_text ) { #> + <{{ buttonTag }} href="#" class="{{ btnClasses }}">{{{ settings.button_text }}} + <# } #> +
    +
    + +
    + start_controls_section( + 'section_activecampaign', + [ + 'label' => esc_html__( 'ActiveCampaign', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + self::global_api_control( + $widget, + $this->get_global_api_key(), + 'ActiveCampaign API credentials', + [ + 'activecampaign_api_credentials_source' => 'default', + ], + $this->get_name() + ); + + $widget->add_control( + 'activecampaign_api_credentials_source', + [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'label_block' => false, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + ] + ); + + $widget->add_control( + 'activecampaign_api_key', + [ + 'label' => esc_html__( 'Custom API Key', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'description' => esc_html__( 'Use this field to set a custom API Key for the current form', 'elementor-pro' ), + 'condition' => [ + 'activecampaign_api_credentials_source' => 'custom', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $widget->add_control( + 'activecampaign_api_url', + [ + 'label' => esc_html__( 'API URL', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'description' => esc_html__( 'Use this field to set a custom API URL for the current form', 'elementor-pro' ), + 'condition' => [ + 'activecampaign_api_credentials_source' => 'custom', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $widget->add_control( + 'activecampaign_list', + [ + 'label' => esc_html__( 'List', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [], + 'render_type' => 'none', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'activecampaign_api_credentials_source', + 'operator' => '=', + 'value' => 'default', + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'activecampaign_api_url', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'activecampaign_api_key', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->register_fields_map_control( $widget ); + + $widget->add_control( + 'activecampaign_tags', + [ + 'label' => esc_html__( 'Tags', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'description' => esc_html__( 'Add as many tags as you want, comma separated.', 'elementor-pro' ), + 'condition' => [ + 'activecampaign_list!' => '', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( + $element['settings']['activecampaign_api_credentials_source'], + $element['settings']['activecampaign_api_key'], + $element['settings']['activecampaign_api_url'], + $element['settings']['activecampaign_list'], + $element['settings']['activecampaign_fields_map'], + $element['settings']['activecampaign_tags'] + ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $form_settings = $record->get( 'form_settings' ); + $subscriber = $this->create_subscriber_object( $record ); + + if ( ! $subscriber ) { + throw new \Exception( 'Integration requires an email field and a selected list.' ); + } + + if ( 'default' === $form_settings['activecampaign_api_credentials_source'] ) { + $api_key = $this->get_global_api_key(); + $api_url = $this->get_global_api_url(); + } else { + $api_key = $form_settings['activecampaign_api_key']; + $api_url = $form_settings['activecampaign_api_url']; + } + + $handler = new Classes\Activecampaign_Handler( $api_key, $api_url ); + $handler->create_subscriber( $subscriber ); + } + + /** + * Create subscriber array from submitted data and form settings + * returns a subscriber array or false on error + * + * @param Form_Record $record + * + * @return array|bool + */ + private function create_subscriber_object( Form_Record $record ) { + $form_settings = $record->get( 'form_settings' ); + $subscriber = $this->map_fields( $record ); + + if ( ! isset( $subscriber['email'] ) ) { + return false; + } + + if ( ! isset( $form_settings['activecampaign_list'] ) ) { + return false; + } + + $subscriber['ip4'] = Utils::get_client_ip(); + $list_id = $form_settings['activecampaign_list']; + $subscriber[ 'p[' . $list_id . ']' ] = $list_id; + + if ( isset( $form_settings['activecampaign_tags'] ) && ! empty( $form_settings['activecampaign_tags'] ) ) { + $subscriber['tags'] = $form_settings['activecampaign_tags']; + } + + if ( isset( $form_settings['form_id'] ) && ! empty( $form_settings['form_id'] ) ) { + $subscriber['form'] = $form_settings['form_id']; + } + + return $subscriber; + } + + /** + * @param Form_Record $record + * + * @return array + */ + private function map_fields( Form_Record $record ) { + $subscriber = []; + $fields = $record->get( 'fields' ); + + // Other form has a field mapping + foreach ( $record->get_form_settings( 'activecampaign_fields_map' ) as $map_item ) { + if ( empty( $fields[ $map_item['local_id'] ]['value'] ) ) { + continue; + } + + $value = $fields[ $map_item['local_id'] ]['value']; + $subscriber[ $map_item['remote_id'] ] = $value; + } + + return $subscriber; + } + + /** + * @param array $data + * + * @return array + * @throws \Exception + */ + public function handle_panel_request( array $data ) { + if ( ! empty( $data['api_cred'] ) && 'default' === $data['api_cred'] ) { + $api_key = $this->get_global_api_key(); + $api_url = $this->get_global_api_url(); + } elseif ( ! empty( $data['api_key'] ) && ! empty( $data['api_url'] ) ) { + $api_key = $data['api_key']; + $api_url = $data['api_url']; + } + + if ( empty( $api_key ) ) { + throw new \Exception( '`api_key` is required.', 400 ); + } + + if ( empty( $api_url ) ) { + throw new \Exception( '`api_url` is required.', 400 ); + } + + $handler = new Classes\Activecampaign_Handler( $api_key, $api_url ); + + return $handler->get_lists(); + } + + public function ajax_validate_api_token() { + check_ajax_referer( self::OPTION_NAME_API_KEY, '_nonce' ); + if ( ! isset( $_POST['api_key'] ) || ! isset( $_POST['api_url'] ) ) { + wp_send_json_error(); + } + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( 'Permission denied' ); + } + + try { + new Classes\Activecampaign_Handler( + Utils::_unstable_get_super_global_value( $_POST, 'api_key' ), + Utils::_unstable_get_super_global_value( $_POST, 'api_url' ) + ); + } catch ( \Exception $exception ) { + wp_send_json_error(); + } + wp_send_json_success(); + } + + public function register_admin_fields( Settings $settings ) { + $settings->add_section( Settings::TAB_INTEGRATIONS, 'activecampign', [ + 'callback' => function() { + echo '

    ' . esc_html__( 'ActiveCampaign', 'elementor-pro' ) . '

    '; + }, + 'fields' => [ + self::OPTION_NAME_API_KEY => [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + ], + ], + self::OPTION_NAME_API_URL => [ + 'label' => esc_html__( 'API URL', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'url', + 'desc' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'To integrate with our forms you need an %1$sAPI Key%2$s.', 'elementor-pro' ), + '', + '' + ), + ], + ], + 'validate_api_data' => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => sprintf( '', self::OPTION_NAME_API_KEY . '_validate', wp_create_nonce( self::OPTION_NAME_API_KEY ), esc_html__( 'Validate API Key', 'elementor-pro' ) ), + ], + ], + ], + ] ); + } + + public function __construct() { + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 15 ); + } + add_action( 'wp_ajax_' . self::OPTION_NAME_API_KEY . '_validate', [ $this, 'ajax_validate_api_token' ] ); + } + + protected function get_fields_map_control_options() { + return [ + 'condition' => [ + 'activecampaign_list!' => '', + ], + ]; + } +} diff --git a/modules/forms/actions/activity-log.php b/modules/forms/actions/activity-log.php new file mode 100644 index 0000000..5df730c --- /dev/null +++ b/modules/forms/actions/activity-log.php @@ -0,0 +1,47 @@ + 'New Record', + 'object_type' => 'Elementor Forms', + 'object_id' => $record->get_form_settings( 'id' ), + 'object_name' => $record->get_form_settings( 'form_name' ), + ] + ); + } + + public function __construct() { + add_filter( 'aal_init_roles', [ $this, 'aal_init_roles' ] ); + } +} diff --git a/modules/forms/actions/cf7db.php b/modules/forms/actions/cf7db.php new file mode 100644 index 0000000..29d3a7b --- /dev/null +++ b/modules/forms/actions/cf7db.php @@ -0,0 +1,33 @@ + $record->get_form_settings( 'form_name' ), + 'posted_data' => $record->get_formatted_data( true ), + ]; + + // Call hook to submit data + do_action_ref_array( 'cfdb_submit', [ $data ] ); + } +} diff --git a/modules/forms/actions/convertkit.php b/modules/forms/actions/convertkit.php new file mode 100644 index 0000000..176f0db --- /dev/null +++ b/modules/forms/actions/convertkit.php @@ -0,0 +1,286 @@ +start_controls_section( + 'section_convertkit', + [ + 'label' => esc_html__( 'ConvertKit', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + self::global_api_control( + $widget, + $this->get_global_api_key(), + 'ConvertKit API key', + [ + 'convertkit_api_key_source' => 'default', + ], + $this->get_name() + ); + + $widget->add_control( + 'convertkit_api_key_source', + [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'label_block' => false, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + ] + ); + + $widget->add_control( + 'convertkit_custom_api_key', + [ + 'label' => esc_html__( 'Custom API Key', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'description' => esc_html__( 'Use this field to set a custom API Key for the current form', 'elementor-pro' ), + 'condition' => [ + 'convertkit_api_key_source' => 'custom', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $widget->add_control( + 'convertkit_form', + [ + 'label' => esc_html__( 'Form', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [], + 'render_type' => 'none', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'convertkit_custom_api_key', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'convertkit_api_key_source', + 'operator' => '=', + 'value' => 'default', + ], + ], + ], + ] + ); + + $this->register_fields_map_control( $widget ); + + $widget->add_control( + 'convertkit_tags', + [ + 'label' => esc_html__( 'Tags', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'options' => [], + 'multiple' => true, + 'render_type' => 'none', + 'label_block' => true, + 'condition' => [ + 'convertkit_form!' => '', + ], + ] + ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( + $element['settings']['convertkit_api_key_source'], + $element['settings']['convertkit_custom_api_key'], + $element['settings']['convertkit_form'], + $element['settings']['convertkit_fields_map'] + ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $form_settings = $record->get( 'form_settings' ); + $subscriber = $this->create_subscriber_object( $record ); + + if ( ! $subscriber ) { + throw new \Exception( 'Integration requires an email field.' ); + } + + if ( 'default' === $form_settings['convertkit_api_key_source'] ) { + $api_key = $this->get_global_api_key(); + } else { + $api_key = $form_settings['convertkit_custom_api_key']; + } + + if ( '' !== $form_settings['convertkit_tags'] ) { + $subscriber['tags'] = $form_settings['convertkit_tags']; + } + + $handler = new ConvertKit_Handler( $api_key ); + $handler->create_subscriber( $form_settings['convertkit_form'], $subscriber ); + } + + /** + * Create subscriber array from submitted data and form settings + * returns a subscriber array or false on error + * + * @param Form_Record $record + * + * @return array|bool + */ + private function create_subscriber_object( Form_Record $record ) { + $subscriber = $this->map_fields( $record ); + + if ( ! isset( $subscriber['email'] ) ) { + return false; + } + + $subscriber['ipAddress'] = Utils::get_client_ip(); + + return $subscriber; + } + + /** + * @param Form_Record $record + * + * @return array + */ + private function map_fields( Form_Record $record ) { + $subscriber = []; + $fields = $record->get( 'fields' ); + + // Other form has a field mapping + foreach ( $record->get_form_settings( 'convertkit_fields_map' ) as $map_item ) { + if ( empty( $fields[ $map_item['local_id'] ]['value'] ) ) { + continue; + } + + $value = $fields[ $map_item['local_id'] ]['value']; + if ( in_array( $map_item['remote_id'], [ 'first_name', 'email' ] ) ) { + $subscriber[ $map_item['remote_id'] ] = $value; + continue; + } + } + + return $subscriber; + } + + /** + * @param array $data + * + * @return array + * @throws \Exception + */ + public function handle_panel_request( array $data ) { + if ( ! empty( $data['api_key'] ) && 'default' === $data['api_key'] ) { + $api_key = $this->get_global_api_key(); + } elseif ( ! empty( $data['custom_api_key'] ) ) { + $api_key = $data['custom_api_key']; + } + + if ( empty( $api_key ) ) { + throw new \Exception( '`api_key` is required.', 400 ); + } + + $handler = new Convertkit_Handler( $api_key ); + + return $handler->get_forms_and_tags(); + } + + public function ajax_validate_api_token() { + check_ajax_referer( self::OPTION_NAME_API_KEY, '_nonce' ); + if ( ! isset( $_POST['api_key'] ) ) { + wp_send_json_error(); + } + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( 'Permission denied' ); + } + + try { + new Convertkit_Handler( $_POST['api_key'] ); // phpcs:ignore -- No need to sanitize to support special characters. + } catch ( \Exception $exception ) { + wp_send_json_error(); + } + wp_send_json_success(); + } + + public function register_admin_fields( Settings $settings ) { + $settings->add_section( Settings::TAB_INTEGRATIONS, 'convertkit', [ + 'callback' => function() { + echo '

    ' . esc_html__( 'ConvertKit', 'elementor-pro' ) . '

    '; + }, + 'fields' => [ + self::OPTION_NAME_API_KEY => [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + 'desc' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'To integrate with our forms you need an %1$sAPI Key%2$s.', 'elementor-pro' ), + '', + '' + ), + ], + ], + 'validate_api_data' => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => sprintf( '', self::OPTION_NAME_API_KEY . '_validate', wp_create_nonce( self::OPTION_NAME_API_KEY ), esc_html__( 'Validate API Key', 'elementor-pro' ) ), + ], + ], + ], + ] ); + } + + public function __construct() { + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 15 ); + } + add_action( 'wp_ajax_' . self::OPTION_NAME_API_KEY . '_validate', [ $this, 'ajax_validate_api_token' ] ); + } + + protected function get_fields_map_control_options() { + return [ + 'condition' => [ + 'convertkit_form!' => '', + ], + ]; + } +} diff --git a/modules/forms/actions/discord.php b/modules/forms/actions/discord.php new file mode 100644 index 0000000..4c909d5 --- /dev/null +++ b/modules/forms/actions/discord.php @@ -0,0 +1,222 @@ +start_controls_section( + 'section_discord', + [ + 'label' => esc_html__( 'Discord', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + $widget->add_control( + 'discord_webhook', + [ + 'label' => esc_html__( 'Webhook URL', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => 'https://discordapp.com/api/webhooks/', + 'ai' => [ + 'active' => false, + ], + 'label_block' => true, + 'separator' => 'before', + 'description' => esc_html__( 'Enter the webhook URL that will receive the form\'s submitted data.', 'elementor-pro' ) . ' ' . sprintf( '%s.', 'https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks', esc_html__( 'Click here for Instructions', 'elementor-pro' ) ), + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'discord_username', + [ + 'label' => esc_html__( 'Username', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'discord_avatar_url', + [ + 'label' => esc_html__( 'Avatar URL', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'discord_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'discord_content', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'discord_form_data', + [ + 'label' => esc_html__( 'Form Data', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + ] + ); + + $widget->add_control( + 'discord_ts', + [ + 'label' => esc_html__( 'Timestamp', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + ] + ); + + $widget->add_control( + 'discord_webhook_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'alpha' => false, + 'default' => '#D30C5C', + ] + ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( + $element['discord_avatar_url'], + $element['discord_content'], + $element['discord_webhook_color'], + $element['discord_username'], + $element['discord_form_data'], + $element['discord_ts'], + $element['discord_title'], + $element['discord_webhook'] + ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $settings = $record->get( 'form_settings' ); + + if ( empty( $settings['discord_webhook'] ) || false === strpos( $settings['discord_webhook'], 'https://discordapp.com/api/webhooks/' ) ) { + return; + } + + // PHPCS - The form is a visitor action and doesn't require a nonce. + $referrer = Utils::_unstable_get_super_global_value( $_POST, 'referrer' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + + $page_url = $referrer ? esc_url( $referrer ) : site_url(); + $color = isset( $settings['discord_webhook_color'] ) ? hexdec( ltrim( $settings['discord_webhook_color'], '#' ) ) : hexdec( '9c0244' ); + + // Build discord webhook data + $embeds = [ + 'title' => isset( $settings['discord_title'] ) ? $settings['discord_title'] : esc_html__( 'A new Submission', 'elementor-pro' ), + 'description' => isset( $settings['discord_content'] ) ? $settings['discord_content'] : esc_html__( 'A new Form Submission has been received', 'elementor-pro' ), + 'author' => [ + 'name' => isset( $settings['discord_username'] ) ? $settings['discord_username'] : esc_html__( 'Elementor Forms', 'elementor-pro' ), + 'url' => $page_url, + 'icon_url' => isset( $settings['discord_avatar_url'] ) ? $settings['discord_avatar_url'] : null, + ], + 'url' => $page_url, + 'color' => $color, + ]; + + if ( ! empty( $settings['discord_form_data'] ) && 'yes' === $settings['discord_form_data'] ) { + // prepare Form Data + $raw_fields = $record->get( 'fields' ); + $fields = []; + foreach ( $raw_fields as $id => $field ) { + $fields[] = [ + 'name' => $id, + 'value' => $field['value'], + 'inline' => false, + ]; + } + + $embeds['fields'] = array_values( $fields ); + } + + if ( ! empty( $settings['discord_ts'] ) && 'yes' === $settings['discord_ts'] ) { + $embeds['timestamp'] = gmdate( \DateTime::ISO8601 ); + $embeds['footer'] = [ + 'text' => sprintf( + /* translators: %s: Elementor. */ + esc_html__( 'Powered by %s', 'elementor-pro' ), + 'Elementor' + ), + 'icon_url' => is_ssl() ? ELEMENTOR_ASSETS_URL . 'images/logo-icon.png' : null, + ]; + } + + $webhook_data = [ + 'embeds' => array_values( [ $embeds ] ), + ]; + + $webhook_data = apply_filters( 'elementor_pro/forms/discord/webhook_args', $webhook_data ); + + $response = wp_remote_post( $settings['discord_webhook'], [ + 'body' => wp_json_encode( $webhook_data ), + 'headers' => [ 'Content-Type' => 'application/json; charset=utf-8' ], + ]); + + if ( 204 !== (int) wp_remote_retrieve_response_code( $response ) ) { + throw new \Exception( 'Webhook error.' ); + } + } +} diff --git a/modules/forms/actions/drip.php b/modules/forms/actions/drip.php new file mode 100644 index 0000000..182337d --- /dev/null +++ b/modules/forms/actions/drip.php @@ -0,0 +1,349 @@ +start_controls_section( + 'section_drip', + [ + 'label' => esc_html__( 'Drip', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + self::global_api_control( + $widget, + $this->get_global_api_key(), + 'Drip API Token', + [ + 'drip_api_token_source' => 'default', + ], + $this->get_name() + ); + + $widget->add_control( + 'drip_api_token_source', + [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'label_block' => false, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + ] + ); + + $widget->add_control( + 'drip_custom_api_token', + [ + 'label' => esc_html__( 'Custom API Key', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'condition' => [ + 'drip_api_token_source' => 'custom', + ], + 'description' => esc_html__( 'Use this field to set a custom API Key for the current form', 'elementor-pro' ), + 'ai' => [ + 'active' => false, + ], + ] + ); + + $widget->add_control( + 'drip_account', + [ + 'label' => esc_html__( 'Account', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [], + 'render_type' => 'none', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'drip_custom_api_token', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'drip_api_token_source', + 'operator' => '=', + 'value' => 'default', + ], + ], + ], + ] + ); + + $this->register_fields_map_control( $widget ); + + $widget->add_control( + 'drip_custom_field_heading', + [ + 'label' => esc_html__( 'Send Additional Data to Drip', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'condition' => [ + 'drip_account!' => '', + ], + ] + ); + + $widget->add_control( + 'drip_custom_fields', + [ + 'label' => esc_html__( 'Form Fields', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'no', + 'description' => esc_html__( 'Send all form fields to drip as custom fields', 'elementor-pro' ), + 'condition' => [ + 'drip_account!' => '', + ], + ] + ); + + $widget->add_control( + 'tags', + [ + 'label' => esc_html__( 'Tags', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'description' => esc_html__( 'Add as many tags as you want, comma separated.', 'elementor-pro' ), + 'condition' => [ + 'drip_account!' => '', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( + $element['settings']['drip_api_token_source'], + $element['settings']['drip_custom_api_token'], + $element['settings']['drip_account'], + $element['settings']['drip_fields_map'], + $element['settings']['tags'], + $element['settings']['drip_custom_fields'] + ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $form_settings = $record->get( 'form_settings' ); + $subscriber = $this->create_subscriber_object( $record ); + + if ( ! $subscriber ) { + throw new \Exception( 'Integration requires an email field.' ); + } + + if ( 'default' === $form_settings['drip_api_token_source'] ) { + $api_key = $this->get_global_api_key(); + } else { + $api_key = $form_settings['drip_custom_api_token']; + } + + $handler = new Drip_Handler( $api_key ); + $handler->create_subscriber( $form_settings['drip_account'], $subscriber ); + } + + /** + * Create subscriber array from submitted data and form settings + * returns a subscriber array or false on error + * + * @param Form_Record $record + * + * @return array|bool + */ + private function create_subscriber_object( Form_Record $record ) { + $form_settings = $record->get( 'form_settings' ); + $email = $this->map_email_field( $record ); + + if ( ! $email ) { + return false; + } + $subscriber = [ + 'ip_address' => Utils::get_client_ip(), + 'email' => $email, + ]; + + if ( isset( $form_settings['tags'] ) && ! empty( $form_settings['tags'] ) ) { + $tags = $record->replace_setting_shortcodes( $form_settings['tags'] ); + + $subscriber['tags'] = explode( ',', $tags ); + } + + $custom_fields = []; + if ( isset( $form_settings['drip_custom_fields'] ) && 'yes' === $form_settings['drip_custom_fields'] ) { + $custom_fields = $this->get_drip_custom_fields( $record ); + } + + $subscriber['custom_fields'] = $custom_fields; + + return $subscriber; + } + + /** + * @param Form_Record $record + * + * @return array + */ + private function get_drip_custom_fields( Form_Record $record ) { + $local_email_id = ''; + foreach ( $record->get_form_settings( 'drip_fields_map' ) as $map_item ) { + if ( 'email' === $map_item['remote_id'] ) { + $local_email_id = $map_item['local_id']; + } + } + $custom_fields = []; + foreach ( $record->get( 'fields' ) as $id => $field ) { + if ( $local_email_id === $id ) { + continue; + } + $custom_fields[ $id ] = $field['value']; + } + + return $custom_fields; + } + + /** + * extracts Email field from form based on mapping + * returns email address or false if missing + * + * @param Form_Record $record + * + * @return bool + */ + private function map_email_field( Form_Record $record ) { + $fields = $record->get( 'fields' ); + foreach ( $record->get_form_settings( 'drip_fields_map' ) as $map_item ) { + if ( empty( $fields[ $map_item['local_id'] ]['value'] ) ) { + continue; + } + + $value = $fields[ $map_item['local_id'] ]['value']; + if ( 'email' === $map_item['remote_id'] ) { + return $value; + } + } + + return false; + } + + /** + * @param array $data + * + * @return array + * @throws \Exception + */ + public function handle_panel_request( array $data ) { + if ( ! empty( $data['api_token'] ) && 'default' === $data['api_token'] ) { + $api_key = $this->get_global_api_key(); + } elseif ( ! empty( $data['custom_api_token'] ) ) { + $api_key = $data['custom_api_token']; + } + + if ( empty( $api_key ) ) { + throw new \Exception( '`api_token` is required.', 400 ); + } + + $handler = new Drip_Handler( $api_key ); + + return $handler->get_accounts(); + } + + public function register_admin_fields( Settings $settings ) { + $settings->add_section( Settings::TAB_INTEGRATIONS, 'drip', [ + 'callback' => function() { + echo '

    ' . esc_html__( 'Drip', 'elementor-pro' ) . '

    '; + }, + 'fields' => [ + self::OPTION_NAME_API_KEY => [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + 'desc' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'To integrate with our forms you need an %1$sAPI Key%2$s.', 'elementor-pro' ), + '', + '' + ), + ], + ], + 'validate_api_data' => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => sprintf( '', self::OPTION_NAME_API_KEY . '_validate', wp_create_nonce( self::OPTION_NAME_API_KEY ), esc_html__( 'Validate API Key', 'elementor-pro' ) ), + ], + ], + ], + ] ); + } + + /** + * + */ + public function ajax_validate_api_token() { + check_ajax_referer( self::OPTION_NAME_API_KEY, '_nonce' ); + if ( ! isset( $_POST['api_key'] ) ) { + wp_send_json_error(); + } + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( 'Permission denied' ); + } + + try { + new Drip_Handler( $_POST['api_key'] ); // phpcs:ignore -- No need to sanitize to support special characters. + } catch ( \Exception $exception ) { + wp_send_json_error(); + } + wp_send_json_success(); + } + + public function __construct() { + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 15 ); + } + add_action( 'wp_ajax_' . self::OPTION_NAME_API_KEY . '_validate', [ $this, 'ajax_validate_api_token' ] ); + } + + protected function get_fields_map_control_options() { + return [ + 'condition' => [ + 'drip_account!' => '', + ], + ]; + } +} diff --git a/modules/forms/actions/email.php b/modules/forms/actions/email.php new file mode 100644 index 0000000..92caa22 --- /dev/null +++ b/modules/forms/actions/email.php @@ -0,0 +1,463 @@ +start_controls_section( + $this->get_control_id( 'section_email' ), + [ + 'label' => $this->get_label(), + 'tab' => Controls_Manager::TAB_CONTENT, + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + $widget->add_control( + $this->get_control_id( 'email_to' ), + [ + 'label' => esc_html__( 'To', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => get_option( 'admin_email' ), + 'ai' => [ + 'active' => false, + ], + 'placeholder' => get_option( 'admin_email' ), + 'label_block' => true, + 'title' => esc_html__( 'Separate emails with commas', 'elementor-pro' ), + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + /* translators: %s: Site title. */ + $default_message = sprintf( esc_html__( 'New message from "%s"', 'elementor-pro' ), get_option( 'blogname' ) ); + + $widget->add_control( + $this->get_control_id( 'email_subject' ), + [ + 'label' => esc_html__( 'Subject', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => $default_message, + 'ai' => [ + 'active' => false, + ], + 'placeholder' => $default_message, + 'label_block' => true, + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + $this->get_control_id( 'email_content' ), + [ + 'label' => esc_html__( 'Message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => '[all-fields]', + 'ai' => [ + 'active' => false, + ], + 'placeholder' => '[all-fields]', + 'description' => sprintf( + /* translators: %s: The [all-fields] shortcode. */ + esc_html__( 'By default, all form fields are sent via %s shortcode. To customize sent fields, copy the shortcode that appears inside each field and paste it above.', 'elementor-pro' ), + '[all-fields]' + ), + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $site_domain = Utils::get_site_domain(); + + $widget->add_control( + $this->get_control_id( 'email_from' ), + [ + 'label' => esc_html__( 'From Email', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => 'email@' . $site_domain, + 'ai' => [ + 'active' => false, + ], + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + $this->get_control_id( 'email_from_name' ), + [ + 'label' => esc_html__( 'From Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => get_bloginfo( 'name' ), + 'ai' => [ + 'active' => false, + ], + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + $this->get_control_id( 'email_reply_to' ), + [ + 'label' => esc_html__( 'Reply-To', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => '', + ], + 'render_type' => 'none', + ] + ); + + $widget->add_control( + $this->get_control_id( 'email_to_cc' ), + [ + 'label' => esc_html__( 'Cc', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'ai' => [ + 'active' => false, + ], + 'title' => esc_html__( 'Separate emails with commas', 'elementor-pro' ), + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + $this->get_control_id( 'email_to_bcc' ), + [ + 'label' => esc_html__( 'Bcc', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'ai' => [ + 'active' => false, + ], + 'title' => esc_html__( 'Separate emails with commas', 'elementor-pro' ), + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + $this->get_control_id( 'form_metadata' ), + [ + 'label' => esc_html__( 'Meta Data', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'label_block' => true, + 'separator' => 'before', + 'default' => [ + 'date', + 'time', + 'page_url', + 'user_agent', + 'remote_ip', + 'credit', + ], + 'options' => [ + 'date' => esc_html__( 'Date', 'elementor-pro' ), + 'time' => esc_html__( 'Time', 'elementor-pro' ), + 'page_url' => esc_html__( 'Page URL', 'elementor-pro' ), + 'user_agent' => esc_html__( 'User Agent', 'elementor-pro' ), + 'remote_ip' => esc_html__( 'Remote IP', 'elementor-pro' ), + 'credit' => esc_html__( 'Credit', 'elementor-pro' ), + ], + 'render_type' => 'none', + ] + ); + + $widget->add_control( + $this->get_control_id( 'email_content_type' ), + [ + 'label' => esc_html__( 'Send As', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'html', + 'render_type' => 'none', + 'options' => [ + 'html' => esc_html__( 'HTML', 'elementor-pro' ), + 'plain' => esc_html__( 'Plain', 'elementor-pro' ), + ], + ] + ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + $controls_to_unset = [ + 'email_to', + 'email_from', + 'email_from_name', + 'email_subject', + 'email_reply_to', + 'email_to_cc', + 'email_to_bcc', + ]; + + foreach ( $controls_to_unset as $base_id ) { + $control_id = $this->get_control_id( $base_id ); + unset( $element['settings'][ $control_id ] ); + } + + return $element; + } + + /** + * @param \ElementorPro\Modules\Forms\Classes\Form_Record $record + * @param \ElementorPro\Modules\Forms\Classes\Ajax_Handler $ajax_handler + */ + public function run( $record, $ajax_handler ) { + $settings = $record->get( 'form_settings' ); + $send_html = 'plain' !== $settings[ $this->get_control_id( 'email_content_type' ) ]; + $line_break = $send_html ? '
    ' : "\n"; + + $fields = [ + 'email_to' => get_option( 'admin_email' ), + /* translators: %s: Site title. */ + 'email_subject' => sprintf( esc_html__( 'New message from "%s"', 'elementor-pro' ), get_bloginfo( 'name' ) ), + 'email_content' => '[all-fields]', + 'email_from_name' => get_bloginfo( 'name' ), + 'email_from' => get_bloginfo( 'admin_email' ), + 'email_reply_to' => 'noreply@' . Utils::get_site_domain(), + 'email_to_cc' => '', + 'email_to_bcc' => '', + ]; + + foreach ( $fields as $key => $default ) { + $setting = trim( $settings[ $this->get_control_id( $key ) ] ); + $setting = $record->replace_setting_shortcodes( $setting ); + if ( ! empty( $setting ) ) { + $fields[ $key ] = $setting; + } + } + + $email_reply_to = $this->get_reply_to( $record, $fields ); + + $fields['email_content'] = $this->replace_content_shortcodes( $fields['email_content'], $record, $line_break ); + + $email_meta = ''; + + $form_metadata_settings = $settings[ $this->get_control_id( 'form_metadata' ) ]; + + foreach ( $record->get( 'meta' ) as $id => $field ) { + if ( in_array( $id, $form_metadata_settings ) ) { + $email_meta .= $this->field_formatted( $field ) . $line_break; + } + } + + if ( ! empty( $email_meta ) ) { + $fields['email_content'] .= $line_break . '---' . $line_break . $line_break . $email_meta; + } + + $headers = sprintf( 'From: %s <%s>' . "\r\n", $fields['email_from_name'], $fields['email_from'] ); + $headers .= sprintf( 'Reply-To: %s' . "\r\n", $email_reply_to ); + + if ( $send_html ) { + $headers .= 'Content-Type: text/html; charset=UTF-8' . "\r\n"; + } + + $cc_header = ''; + if ( ! empty( $fields['email_to_cc'] ) ) { + $cc_header = 'Cc: ' . $fields['email_to_cc'] . "\r\n"; + } + + /** + * Email headers. + * + * Filters the headers sent when an email is sent from Elementor forms. This + * hook allows developers to alter email headers triggered by Elementor forms. + * + * @since 1.0.0 + * + * @param string|array $headers Additional headers. + */ + $headers = apply_filters( 'elementor_pro/forms/wp_mail_headers', $headers ); + + /** + * Email content. + * + * Filters the content of the email sent by Elementor forms. This hook allows + * developers to alter the content of the email sent by Elementor forms. + * + * @since 1.0.0 + * + * @param string $email_content Email content. + */ + $fields['email_content'] = apply_filters( 'elementor_pro/forms/wp_mail_message', $fields['email_content'] ); + + $attachments_mode_attach = $this->get_file_by_attachment_type( $settings['form_fields'], $record, Upload::MODE_ATTACH ); + $attachments_mode_both = $this->get_file_by_attachment_type( $settings['form_fields'], $record, Upload::MODE_BOTH ); + + $email_sent = wp_mail( + $fields['email_to'], + $fields['email_subject'], + $fields['email_content'], + $headers . $cc_header, + array_merge( $attachments_mode_attach, $attachments_mode_both ) + ); + + if ( ! empty( $fields['email_to_bcc'] ) ) { + $bcc_emails = explode( ',', $fields['email_to_bcc'] ); + foreach ( $bcc_emails as $bcc_email ) { + wp_mail( + trim( $bcc_email ), + $fields['email_subject'], + $fields['email_content'], + $headers, + array_merge( $attachments_mode_attach, $attachments_mode_both ) + ); + } + } + + foreach ( $attachments_mode_attach as $file ) { + @unlink( $file ); + } + + /** + * Elementor form mail sent. + * + * Fires when an email was sent successfully by Elementor forms. This + * hook allows developers to add functionality after mail sending. + * + * @since 1.0.0 + * + * @param array $settings Form settings. + * @param Form_Record $record An instance of the form record. + */ + do_action( 'elementor_pro/forms/mail_sent', $settings, $record ); + + if ( ! $email_sent ) { + $message = Ajax_Handler::get_default_message( Ajax_Handler::SERVER_ERROR, $settings ); + + $ajax_handler->add_error_message( $message ); + + throw new \Exception( $message ); + } + } + + private function field_formatted( $field ) { + $formatted = ''; + if ( ! empty( $field['title'] ) ) { + $formatted = sprintf( '%s: %s', $field['title'], $field['value'] ); + } elseif ( ! empty( $field['value'] ) ) { + $formatted = sprintf( '%s', $field['value'] ); + } + + return $formatted; + } + + // Allow overwrite the control_id with a prefix, @see Email2 + protected function get_control_id( $control_id ) { + return $control_id; + } + + protected function get_reply_to( $record, $fields ) { + $email_reply_to = ''; + + if ( ! empty( $fields['email_reply_to'] ) ) { + $sent_data = $record->get( 'sent_data' ); + foreach ( $record->get( 'fields' ) as $field_index => $field ) { + if ( $field_index === $fields['email_reply_to'] && ! empty( $sent_data[ $field_index ] ) && is_email( $sent_data[ $field_index ] ) ) { + $email_reply_to = $sent_data[ $field_index ]; + break; + } + } + } + + return $email_reply_to; + } + + /** + * @param string $email_content + * @param Form_Record $record + * + * @return string + */ + private function replace_content_shortcodes( $email_content, $record, $line_break ) { + $email_content = do_shortcode( $email_content ); + $all_fields_shortcode = '[all-fields]'; + + if ( false !== strpos( $email_content, $all_fields_shortcode ) ) { + $text = ''; + foreach ( $record->get( 'fields' ) as $field ) { + // Skip upload fields that only attached to the email + if ( isset( $field['attachment_type'] ) && Upload::MODE_ATTACH === $field['attachment_type'] ) { + continue; + } + + $formatted = $this->field_formatted( $field ); + if ( ( 'textarea' === $field['type'] ) && ( '
    ' === $line_break ) ) { + $formatted = str_replace( [ "\r\n", "\n", "\r" ], '
    ', $formatted ); + } + + $text .= $formatted . $line_break; + } + + $email_content = str_replace( $all_fields_shortcode, $text, $email_content ); + + } + + return $email_content; + } + + /** + * @param array $form_fields + * @param Form_Record $record + * @param string $type + * + * @return array + */ + private function get_file_by_attachment_type( $form_fields, $record, $type ) { + return Collection::make( $form_fields ) + ->filter( function ( $field ) use ( $type ) { + return $type === $field['attachment_type']; + } ) + ->map( function ( $field ) use ( $record ) { + $id = $field['custom_id']; + + return $record->get( 'files' )[ $id ]['path'] ?? null; + } ) + ->filter() + ->flatten() + ->values(); + } +} diff --git a/modules/forms/actions/email2.php b/modules/forms/actions/email2.php new file mode 100644 index 0000000..565cd2f --- /dev/null +++ b/modules/forms/actions/email2.php @@ -0,0 +1,52 @@ +update_control( + $this->get_control_id( 'email_reply_to' ), + [ + 'type' => Controls_Manager::TEXT, + 'default' => $admin_email, + 'placeholder' => $admin_email, + 'ai' => [ + 'active' => false, + ], + ] + ); + + $widget->update_control( + $this->get_control_id( 'form_metadata' ), + [ + 'default' => [], + ] + ); + } +} diff --git a/modules/forms/actions/getresponse.php b/modules/forms/actions/getresponse.php new file mode 100644 index 0000000..eee8f65 --- /dev/null +++ b/modules/forms/actions/getresponse.php @@ -0,0 +1,334 @@ +start_controls_section( + 'section_getresponse', + [ + 'label' => esc_html__( 'GetResponse', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + self::global_api_control( + $widget, + $this->get_global_api_key(), + 'GetResponse API key', + [ + 'getresponse_api_key_source' => 'default', + ], + $this->get_name() + ); + + $widget->add_control( + 'getresponse_api_key_source', + [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'label_block' => false, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + ] + ); + + $widget->add_control( + 'getresponse_custom_api_key', + [ + 'label' => esc_html__( 'Custom API Key', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'description' => esc_html__( 'Use this field to set a custom API Key for the current form', 'elementor-pro' ), + 'condition' => [ + 'getresponse_api_key_source' => 'custom', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $widget->add_control( + 'getresponse_list', + [ + 'label' => esc_html__( 'List', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [], + 'render_type' => 'none', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'getresponse_custom_api_key', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'getresponse_api_key_source', + 'operator' => '=', + 'value' => 'default', + ], + ], + ], + ] + ); + + $widget->add_control( + 'getresponse_dayofcycle', + [ + 'label' => esc_html__( 'Day Of Cycle', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'min' => 0, + 'condition' => [ + 'getresponse_list!' => '', + ], + ] + ); + + $this->register_fields_map_control( $widget ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( + $element['settings']['getresponse_api_key_source'], + $element['settings']['getresponse_custom_api_key'], + $element['settings']['getresponse_list'], + $element['settings']['getresponse_fields_map'] + ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $form_settings = $record->get( 'form_settings' ); + $subscriber = $this->create_subscriber_object( $record ); + + if ( ! $subscriber ) { + throw new \Exception( 'Integration requires an email field.' ); + } + + if ( 'default' === $form_settings['getresponse_api_key_source'] ) { + $api_key = $this->get_global_api_key(); + } else { + $api_key = $form_settings['getresponse_custom_api_key']; + } + + try { + $handler = new Getresponse_Handler( $api_key ); + $handler->create_subscriber( $subscriber ); + } catch ( \Exception $exception ) { + foreach ( (array) $handler->rest_client->request_cache as $response ) { + if ( isset( $response['parsed'] ) || ! isset( $response['raw'] ) || ! isset( $response['raw']['response'] ) || ! isset( $response['raw']['response']['code'] ) ) { + continue; + } + if ( ! in_array( $response['raw']['response']['code'], [ 200, 202, 409 ] ) ) { + throw new \Exception( $exception->getMessage() ); + } + } + } + } + + /** + * Create subscriber array from submitted data and form settings + * returns a subscriber array or false on error + * + * @param Form_Record $record + * + * @return array|bool + */ + private function create_subscriber_object( Form_Record $record ) { + $form_settings = $record->get( 'form_settings' ); + $subscriber = $this->map_fields( $record ); + + if ( ! isset( $subscriber['email'] ) ) { + return false; + } + + if ( isset( $form_settings['getresponse_dayofcycle'] ) ) { + $subscriber['dayOfCycle'] = intval( $form_settings['getresponse_dayofcycle'] ); + } + $subscriber['ipAddress'] = Utils::get_client_ip(); + $subscriber['campaign'] = [ 'campaignId' => $form_settings['getresponse_list'] ]; + + return $subscriber; + } + + + /** + * @param Form_Record $record + * + * @return array + */ + private function get_getresponse_custom_fields( Form_Record $record ) { + $local_email_id = ''; + $local_name_id = ''; + foreach ( $record->get_form_settings( 'getresponse_fields_map' ) as $map_item ) { + if ( 'email' === $map_item['remote_id'] ) { + $local_email_id = $map_item['local_id']; + } + if ( 'name' === $map_item['remote_id'] ) { + $local_name_id = $map_item['local_id']; + } + } + $custom_fields = []; + foreach ( $record->get( 'fields' ) as $id => $field ) { + if ( in_array( $id, [ $local_email_id, $local_name_id ] ) ) { + continue; + } + $custom_fields[ $id ] = $field['value']; + } + + return $custom_fields; + } + + /** + * @param Form_Record $record + * + * @return array + */ + private function map_fields( Form_Record $record ) { + $subscriber = []; + $custom_fields = []; + $fields = $record->get( 'fields' ); + + // Other form has a field mapping + foreach ( $record->get_form_settings( 'getresponse_fields_map' ) as $map_item ) { + if ( empty( $fields[ $map_item['local_id'] ]['value'] ) ) { + continue; + } + + $value = $fields[ $map_item['local_id'] ]['value']; + if ( in_array( $map_item['remote_id'], [ 'name', 'email' ] ) ) { + $subscriber[ $map_item['remote_id'] ] = $value; + continue; + } + + $custom_fields[] = [ + 'customFieldId' => $map_item['remote_id'], + 'value' => [ $value ], + ]; + } + $subscriber['customFieldValues'] = $custom_fields; + + return $subscriber; + } + + /** + * @param array $data + * + * @return array + * @throws \Exception + */ + public function handle_panel_request( array $data ) { + if ( ! empty( $data['api_key'] ) && 'default' === $data['api_key'] ) { + $api_key = $this->get_global_api_key(); + } elseif ( ! empty( $data['custom_api_key'] ) ) { + $api_key = $data['custom_api_key']; + } + + if ( empty( $api_key ) ) { + throw new \Exception( '`api_key` is required.', 400 ); + } + + $handler = new Getresponse_Handler( $api_key ); + + if ( 'lists' === $data['getresponse_action'] ) { + return $handler->get_lists(); + } + + return $handler->get_fields(); + } + + public function ajax_validate_api_token() { + check_ajax_referer( self::OPTION_NAME_API_KEY, '_nonce' ); + if ( ! isset( $_POST['api_key'] ) ) { + wp_send_json_error(); + } + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( 'Permission denied' ); + } + + try { + new Getresponse_Handler( $_POST['api_key'] ); // phpcs:ignore -- No need to sanitize to support special characters. + } catch ( \Exception $exception ) { + wp_send_json_error(); + } + wp_send_json_success(); + } + + public function register_admin_fields( Settings $settings ) { + $settings->add_section( Settings::TAB_INTEGRATIONS, 'getresponse', [ + 'callback' => function() { + echo '

    ' . esc_html__( 'GetResponse', 'elementor-pro' ) . '

    '; + }, + 'fields' => [ + self::OPTION_NAME_API_KEY => [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + 'desc' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'To integrate with our forms you need an %1$sAPI Key%2$s.', 'elementor-pro' ), + '', + '' + ), + ], + ], + 'validate_api_data' => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => sprintf( '', self::OPTION_NAME_API_KEY . '_validate', wp_create_nonce( self::OPTION_NAME_API_KEY ), esc_html__( 'Validate API Key', 'elementor-pro' ) ), + ], + ], + ], + ] ); + } + + public function __construct() { + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 15 ); + } + add_action( 'wp_ajax_' . self::OPTION_NAME_API_KEY . '_validate', [ $this, 'ajax_validate_api_token' ] ); + } + + protected function get_fields_map_control_options() { + return [ + 'condition' => [ + 'getresponse_list!' => '', + ], + ]; + } +} diff --git a/modules/forms/actions/mailchimp.php b/modules/forms/actions/mailchimp.php new file mode 100644 index 0000000..a25ec5d --- /dev/null +++ b/modules/forms/actions/mailchimp.php @@ -0,0 +1,490 @@ +start_controls_section( + 'section_mailchimp', + [ + 'label' => esc_html__( 'MailChimp', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + self::global_api_control( + $widget, + $this->get_global_api_key(), + 'MailChimp API Key', + [ + 'mailchimp_api_key_source' => 'default', + ], + $this->get_name() + ); + + $widget->add_control( + 'mailchimp_api_key_source', + [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'label_block' => false, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + ] + ); + + $widget->add_control( + 'mailchimp_api_key', + [ + 'label' => esc_html__( 'Custom API Key', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'condition' => [ + 'mailchimp_api_key_source' => 'custom', + ], + 'description' => esc_html__( 'Use this field to set a custom API Key for the current form', 'elementor-pro' ), + 'ai' => [ + 'active' => false, + ], + ] + ); + + $widget->add_control( + 'mailchimp_list', + [ + 'label' => esc_html__( 'Audience', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [], + 'render_type' => 'none', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'mailchimp_api_key', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'mailchimp_api_key_source', + 'operator' => '=', + 'value' => 'default', + ], + ], + ], + ] + ); + + $widget->add_control( + 'mailchimp_groups', + [ + 'label' => esc_html__( 'Groups', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'options' => [], + 'label_block' => true, + 'multiple' => true, + 'render_type' => 'none', + 'condition' => [ + 'mailchimp_list!' => '', + ], + ] + ); + + $widget->add_control( + 'mailchimp_tags', + [ + 'label' => esc_html__( 'Tags', 'elementor-pro' ), + 'description' => esc_html__( 'Add comma separated tags', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'render_type' => 'none', + 'condition' => [ + 'mailchimp_list!' => '', + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $widget->add_control( + 'mailchimp_double_opt_in', + [ + 'label' => esc_html__( 'Double Opt-In', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'condition' => [ + 'mailchimp_list!' => '', + ], + ] + ); + + $this->register_fields_map_control( $widget ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( + $element['settings']['mailchimp_api_key_source'], + $element['settings']['mailchimp_api_key'], + $element['settings']['mailchimp_list'], + $element['settings']['mailchimp_groups'], + $element['settings']['mailchimp_fields_map'] + ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $form_settings = $record->get( 'form_settings' ); + + if ( 'default' === $form_settings['mailchimp_api_key_source'] ) { + $this->api_key = $this->get_global_api_key(); + } else { + $this->api_key = $form_settings['mailchimp_api_key']; + } + + // Data from the form in the frontend. + $subscriber_data = $this->map_fields( $record ); + + // Create or update a subscriber. + $subscriber = $this->create_or_update_subscriber( $subscriber_data, $form_settings ); + + // Parse the Mailchimp tags. + $tags = $this->parse_tags( $form_settings['mailchimp_tags'] ); + + // Set the subscriber tags only if he doesn't have them already. + if ( ! $this->subscriber_has_tags( $subscriber, $tags ) ) { + $this->set_subscriber_tags( $subscriber, $tags ); + } + } + + /** + * @param string $tags - List of comma separated tags from the forms settings ( i.e. 'tag-1, tag-2' ). + * + * @return array|string[] - Array of tags that were extracted from the input ( i.e. [ 'tag-1', 'tag-2' ] ). + */ + private function parse_tags( $tags ) { + $parsed_tags = []; + + if ( ! empty( $tags ) ) { + $parsed_tags = explode( ',', trim( $tags ) ); + + // Remove empty tags. + $parsed_tags = array_filter( $parsed_tags ); + + // Trim tags. + $parsed_tags = array_map( 'trim', $parsed_tags ); + } + + return $parsed_tags; + } + + /** + * Determine if a subscriber has specific tags, and ONLY those tags. + * + * @param array $subscriber - Subscriber data from an API response. + * @param array $tags - List of tags to check ( i.e. [ 'tag-1', 'tag-2' ] ). + * + * @return bool + */ + private function subscriber_has_tags( array $subscriber, array $tags ) { + // Extract current tags. + $subscriber_tags = []; + + foreach ( $subscriber['tags'] as $tag ) { + $subscriber_tags[] = $tag['name']; + } + + return array_diff( $tags, $subscriber_tags ) === array_diff( $subscriber_tags, $tags ); + } + + /** + * Set Mailchimp subscriber tags. + * + * @param array $subscriber - Subscriber data from a create/update request. + * @param array $tags - List of tags to set. + * + * @return void + */ + private function set_subscriber_tags( array $subscriber, array $tags ) { + // Build the request tags. + $request_tags = []; + + // Set current tags to active. + foreach ( $subscriber['tags'] as $tag ) { + $request_tags[] = [ + 'name' => $tag['name'], + 'status' => 'active', + ]; + } + + // Set new tags to active. + foreach ( $tags as $tag ) { + $request_tags[] = [ + 'name' => $tag, + 'status' => 'active', + ]; + } + + // Send the API request. + $endpoint = sprintf( 'lists/%s/members/%s/tags', $subscriber['list_id'], md5( strtolower( $subscriber['email_address'] ) ) ); + $args = [ + 'tags' => $request_tags, + ]; + + $handler = new Mailchimp_Handler( $this->api_key ); + $response = $handler->post( $endpoint, $args ); + + if ( 204 !== $response['code'] ) { + $error = ! empty( $response['body']['detail'] ) ? $response['body']['detail'] : ''; + $code = $response['code']; + + throw new \Exception( "HTTP {$code} - {$error}" ); + } + } + + /** + * Get Mailchimp subscriber data. + * + * @param string $list - Mailchimp List ID. + * @param string $email_hash - Subscriber's email hash (lowercase + MD5). + * + * @return array|null + */ + private function get_subscriber_data( $list, $email_hash ) { + $handler = new Mailchimp_Handler( $this->api_key ); + $end_point = sprintf( 'lists/%s/members/%s', $list, $email_hash ); + + try { + return $handler->query( $end_point ); + } catch ( \Exception $e ) { + return null; + } + } + + /** + * Set Mailchimp subscriber data. + * + * @param string $list - Mailchimp List ID. + * @param string $email_hash - Subscriber's email hash (lowercase + MD5). + * @param array $data - New subscriber data to set. + * + * @return array + */ + private function set_subscriber_data( $list, $email_hash, $data ) { + $handler = new Mailchimp_Handler( $this->api_key ); + + $end_point = sprintf( 'lists/%s/members/%s', $list, $email_hash ); + + $response = $handler->post( $end_point, $data, [ + 'method' => 'PUT', // Add or Update + ] ); + + if ( 200 !== $response['code'] ) { + $error = ! empty( $response['body']['detail'] ) ? $response['body']['detail'] : ''; + $code = $response['code']; + + throw new \Exception( "HTTP {$code} - {$error}" ); + } + + return $response['body']; + } + + /** + * Create or update a Mailchimp subscriber. + * + * @param array $subscriber - Subscriber data from the form in the frontend. + * @param array $form_settings - Settings from the editor. + * + * @return array - An array that contains the newly created subscriber's data. + */ + private function create_or_update_subscriber( array $subscriber, array $form_settings ) { + if ( ! empty( $form_settings['mailchimp_groups'] ) ) { + $subscriber['interests'] = []; + } + + if ( is_array( $form_settings['mailchimp_groups'] ) ) { + foreach ( $form_settings['mailchimp_groups'] as $mailchimp_group ) { + $subscriber['interests'][ $mailchimp_group ] = true; + } + } + + if ( ! empty( $form_settings['mailchimp_tags'] ) ) { + $subscriber['tags'] = explode( ',', trim( $form_settings['mailchimp_tags'] ) ); + } + + $list = $form_settings['mailchimp_list']; + $email_hash = md5( strtolower( $subscriber['email_address'] ) ); + $double_opt_in = ( 'yes' === $form_settings['mailchimp_double_opt_in'] ); + + $subscriber['status_if_new'] = $double_opt_in ? 'pending' : 'subscribed'; + + if ( $double_opt_in ) { + $subscriber_data = $this->get_subscriber_data( $list, $email_hash ); + + // Change the current status only if the user isn't subscribed already. + if ( $subscriber_data && 'subscribed' !== $subscriber_data['status'] ) { + $subscriber['status'] = 'pending'; + } + } else { + $subscriber['status'] = 'subscribed'; + } + + return $this->set_subscriber_data( $list, $email_hash, $subscriber ); + } + + /** + * @param Form_Record $record + * + * @return array + */ + private function map_fields( $record ) { + $subscriber = []; + $fields = $record->get( 'fields' ); + + // Other form has a field mapping + foreach ( $record->get_form_settings( 'mailchimp_fields_map' ) as $map_item ) { + if ( empty( $fields[ $map_item['local_id'] ]['value'] ) ) { + continue; + } + + $value = $fields[ $map_item['local_id'] ]['value']; + if ( 'email' === $map_item['remote_id'] ) { + $subscriber['email_address'] = $value; + } else { + $subscriber['merge_fields'][ $map_item['remote_id'] ] = $value; + } + } + + return $subscriber; + } + + public function ajax_validate_api_token() { + check_ajax_referer( self::OPTION_NAME_API_KEY, '_nonce' ); + if ( ! isset( $_POST['api_key'] ) ) { + wp_send_json_error(); + } + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( 'Permission denied' ); + } + + try { + new Mailchimp_Handler( $_POST['api_key'] ); // phpcs:ignore -- No need to sanitize to support special characters. + } catch ( \Exception $exception ) { + wp_send_json_error(); + } + wp_send_json_success(); + } + + /** + * @param array $data + * + * @return array + * @throws \Exception + */ + public function handle_panel_request( array $data ) { + if ( ! empty( $data['use_global_api_key'] ) && 'default' === $data['use_global_api_key'] ) { + $api_key = $this->get_global_api_key(); + } elseif ( ! empty( $data['api_key'] ) ) { + $api_key = $data['api_key']; + } + + if ( empty( $api_key ) ) { + throw new \Exception( '`api_key` is required.', 400 ); + } + + $handler = new Mailchimp_Handler( $api_key ); + + switch ( $data['mailchimp_action'] ) { + case 'lists': + return $handler->get_lists(); + + case 'fields': + return $handler->get_fields( $data['mailchimp_list'] ); + + case 'groups': + return $handler->get_groups( $data['mailchimp_list'] ); + + default: + return $handler->get_list_details( $data['mailchimp_list'] ); + } + } + + public function register_admin_fields( Settings $settings ) { + $settings->add_section( Settings::TAB_INTEGRATIONS, 'mailchimp', [ + 'callback' => function() { + echo '

    ' . esc_html__( 'MailChimp', 'elementor-pro' ) . '

    '; + }, + 'fields' => [ + self::OPTION_NAME_API_KEY => [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + 'desc' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'To integrate with our forms you need an %1$sAPI Key%2$s.', 'elementor-pro' ), + '', + '' + ), + ], + ], + 'validate_api_data' => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => sprintf( '', self::OPTION_NAME_API_KEY . '_validate', wp_create_nonce( self::OPTION_NAME_API_KEY ), esc_html__( 'Validate API Key', 'elementor-pro' ) ), + ], + ], + ], + ] ); + } + + public function __construct() { + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 14 ); + } + add_action( 'wp_ajax_' . self::OPTION_NAME_API_KEY . '_validate', [ $this, 'ajax_validate_api_token' ] ); + } + + protected function get_fields_map_control_options() { + return [ + 'condition' => [ + 'mailchimp_list!' => '', + ], + ]; + } +} diff --git a/modules/forms/actions/mailerlite.php b/modules/forms/actions/mailerlite.php new file mode 100644 index 0000000..322c5fd --- /dev/null +++ b/modules/forms/actions/mailerlite.php @@ -0,0 +1,303 @@ +start_controls_section( + 'section_mailerlite', + [ + 'label' => esc_html__( 'MailerLite', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + self::global_api_control( + $widget, + $this->get_global_api_key(), + 'MailerLite API Key', + [ + 'mailerlite_api_key_source' => 'default', + ], + $this->get_name() + ); + + $widget->add_control( + 'mailerlite_api_key_source', + [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'label_block' => false, + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + ] + ); + + $widget->add_control( + 'mailerlite_custom_api_key', + [ + 'label' => esc_html__( 'Custom API Key', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'condition' => [ + 'mailerlite_api_key_source' => 'custom', + ], + 'description' => esc_html__( 'Use this field to set a custom API Key for the current form', 'elementor-pro' ), + 'ai' => [ + 'active' => false, + ], + ] + ); + + $widget->add_control( + 'mailerlite_group', + [ + 'label' => esc_html__( 'Group', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [], + 'render_type' => 'none', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'mailerlite_custom_api_key', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'mailerlite_api_key_source', + 'operator' => '=', + 'value' => 'default', + ], + ], + ], + ] + ); + + $this->register_fields_map_control( $widget ); + + $widget->add_control( + 'allow_resubscribe', + [ + 'label' => esc_html__( 'Allow Resubscribe', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'mailerlite_group!' => '', + ], + ] + ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( + $element['settings']['mailerlite_api_key_source'], + $element['settings']['mailerlite_custom_api_key'], + $element['settings']['mailerlite_group'], + $element['settings']['mailerlite_fields_map'] + ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $form_settings = $record->get( 'form_settings' ); + $subscriber = $this->create_subscriber_object( $record ); + + if ( ! $subscriber ) { + new \Exception( esc_html__( 'Integration requires an email field', 'elementor-pro' ) ); + } + + if ( 'default' === $form_settings['mailerlite_api_key_source'] ) { + $api_key = $this->get_global_api_key(); + } else { + $api_key = $form_settings['mailerlite_custom_api_key']; + } + + $handler = new Mailerlite_Handler( $api_key ); + $handler->create_subscriber( $form_settings['mailerlite_group'], $subscriber ); + } + + /** + * Create subscriber array from submitted data and form settings + * returns a subscriber array or false on error + * + * @param Form_Record $record + * + * @return array|bool + */ + private function create_subscriber_object( Form_Record $record ) { + $email = $this->get_mapped_field( $record, 'email' ); + + if ( ! $email ) { + return false; + } + + $subscriber = [ + 'email' => $email, + 'name' => $this->get_mapped_field( $record, 'name' ), + ]; + + $subscriber['fields'] = $this->get_mailerlite_custom_fields( $record ); + + // Allow re-subscribe + $allow_resubscribe = $record->get_form_settings( 'allow_resubscribe' ); + if ( ! empty( $allow_resubscribe ) && 'yes' === $allow_resubscribe ) { + $subscriber['resubscribe'] = true; + } + + return $subscriber; + } + + /** + * @param Form_Record $record + * + * @return array + */ + private function get_mailerlite_custom_fields( Form_Record $record ) { + $custom_fields = []; + $form_fields = $record->get( 'fields' ); + $field_mapping = $record->get_form_settings( 'mailerlite_fields_map' ); + foreach ( $field_mapping as $map_item ) { + if ( in_array( $map_item['remote_id'], [ 'email', 'name' ] ) ) { + continue; + } + + if ( empty( $map_item['local_id'] ) ) { + continue; + } + + foreach ( $form_fields as $id => $field ) { + if ( $id !== $map_item['local_id'] ) { + continue; + } + $custom_fields[ $map_item['remote_id'] ] = $field['value']; + } + } + + return $custom_fields; + } + + private function get_mapped_field( Form_Record $record, $field_id ) { + $fields = $record->get( 'fields' ); + foreach ( $record->get_form_settings( 'mailerlite_fields_map' ) as $map_item ) { + if ( empty( $fields[ $map_item['local_id'] ]['value'] ) ) { + continue; + } + + if ( $field_id === $map_item['remote_id'] ) { + return $fields[ $map_item['local_id'] ]['value']; + } + } + + return ''; + } + + public function handle_panel_request( array $data ) { + if ( ! empty( $data['api_key'] ) && 'default' === $data['api_key'] ) { + $api_key = $this->get_global_api_key(); + } elseif ( ! empty( $data['custom_api_key'] ) ) { + $api_key = $data['custom_api_key']; + } + + if ( empty( $api_key ) ) { + throw new \Exception( '`api_key` is required.', 400 ); + } + + $handler = new Mailerlite_Handler( $api_key ); + if ( 'groups' === $data['mailerlite_action'] ) { + return $handler->get_groups(); + } + } + + public function register_admin_fields( Settings $settings ) { + $settings->add_section( Settings::TAB_INTEGRATIONS, 'mailerlite', [ + 'callback' => function() { + echo '

    ' . esc_html__( 'MailerLite', 'elementor-pro' ) . '

    '; + }, + 'fields' => [ + self::OPTION_NAME_API_KEY => [ + 'label' => esc_html__( 'API Key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + 'desc' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'To integrate with our forms you need an %1$sAPI Key%2$s.', 'elementor-pro' ), + '', + '' + ), + ], + ], + 'validate_api_data' => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => sprintf( '', self::OPTION_NAME_API_KEY . '_validate', wp_create_nonce( self::OPTION_NAME_API_KEY ), esc_html__( 'Validate API Key', 'elementor-pro' ) ), + ], + ], + ], + ] ); + } + + public function ajax_validate_api_key() { + check_ajax_referer( self::OPTION_NAME_API_KEY, '_nonce' ); + if ( ! isset( $_POST['api_key'] ) ) { + wp_send_json_error(); + } + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( 'Permission denied' ); + } + + try { + new Mailerlite_Handler( $_POST['api_key'] ); // phpcs:ignore -- No need to sanitize to support special characters. + } catch ( \Exception $exception ) { + wp_send_json_error(); + } + wp_send_json_success(); + } + + public function __construct() { + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 15 ); + } + add_action( 'wp_ajax_' . self::OPTION_NAME_API_KEY . '_validate', [ $this, 'ajax_validate_api_key' ] ); + } + + protected function get_fields_map_control_options() { + return [ + 'condition' => [ + 'mailerlite_group!' => '', + ], + ]; + } +} diff --git a/modules/forms/actions/mailpoet.php b/modules/forms/actions/mailpoet.php new file mode 100644 index 0000000..0a08529 --- /dev/null +++ b/modules/forms/actions/mailpoet.php @@ -0,0 +1,129 @@ +start_controls_section( + 'section_mailpoet', + [ + 'label' => esc_html__( 'MailPoet', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + /** @var \WYSIJA_model_list $model_list */ + $model_list = \WYSIJA::get( 'list', 'model' ); + $mailpoet_lists = $model_list->get( [ 'name', 'list_id' ], [ 'is_enabled' => 1 ] ); + $options = []; + + foreach ( $mailpoet_lists as $list ) { + $options[ $list['list_id'] ] = $list['name']; + } + + $widget->add_control( + 'mailpoet_lists', + [ + 'label' => esc_html__( 'List', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'label_block' => true, + 'options' => $options, + 'render_type' => 'none', + ] + ); + + $this->register_fields_map_control( $widget ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( $element['mailpoet_lists'] ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $subscriber = $this->map_fields( $record ); + + /** @var \WYSIJA_help_user $helper_user */ + $helper_user = \WYSIJA::get( 'user', 'helper' ); + $helper_user->addSubscriber( $subscriber ); + } + + /** + * @param Form_Record $record + * + * @return array + */ + private function map_fields( $record ) { + $settings = $record->get( 'form_settings' ); + $fields = $record->get( 'fields' ); + + $subscriber = [ + 'user' => [ + 'email' => '', + ], + 'user_list' => [ 'list_ids' => (array) $settings['mailpoet_lists'] ], + ]; + + foreach ( $settings['mailpoet_fields_map'] as $map_item ) { + if ( empty( $fields[ $map_item['local_id'] ]['value'] ) ) { + continue; + } + + $value = $fields[ $map_item['local_id'] ]['value']; + if ( 'email' === $map_item['remote_id'] ) { + $subscriber['user']['email'] = $value; + } else { + $subscriber['user'][ $map_item['remote_id'] ] = $value; + } + } + + return $subscriber; + } + + protected function get_fields_map_control_options() { + return [ + 'default' => [ + [ + 'remote_id' => 'firstname', + 'remote_label' => esc_html__( 'First Name', 'elementor-pro' ), + 'remote_type' => 'text', + ], + [ + 'remote_id' => 'lastname', + 'remote_label' => esc_html__( 'Last Name', 'elementor-pro' ), + 'remote_type' => 'text', + ], + [ + 'remote_id' => 'email', + 'remote_label' => esc_html__( 'Email', 'elementor-pro' ), + 'remote_type' => 'email', + 'remote_required' => true, + ], + ], + 'condition' => [ + 'mailpoet_lists!' => '', + ], + ]; + } +} diff --git a/modules/forms/actions/mailpoet3.php b/modules/forms/actions/mailpoet3.php new file mode 100644 index 0000000..ba60c3d --- /dev/null +++ b/modules/forms/actions/mailpoet3.php @@ -0,0 +1,152 @@ +start_controls_section( + 'section_mailpoet3', + [ + 'label' => esc_html__( 'MailPoet 3', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + $mailpoet3_lists = API::MP( 'v1' )->getLists(); + $options = []; + + foreach ( $mailpoet3_lists as $list ) { + $options[ $list['id'] ] = $list['name']; + } + + $widget->add_control( + 'mailpoet3_lists', + [ + 'label' => esc_html__( 'List', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'label_block' => true, + 'options' => $options, + 'render_type' => 'none', + ] + ); + + $this->register_fields_map_control( $widget ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( $element['mailpoet3_lists'] ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $settings = $record->get( 'form_settings' ); + $subscriber = $this->map_fields( $record ); + + $existing_subscriber = false; + + try { + API::MP( 'v1' )->addSubscriber( $subscriber, (array) $settings['mailpoet3_lists'] ); + $existing_subscriber = false; + } catch ( \Exception $exception ) { + $error_string = esc_html__( 'This subscriber already exists.', 'mailpoet' ); // phpcs:ignore WordPress.WP.I18n + + if ( $error_string === $exception->getMessage() ) { + $existing_subscriber = true; + } else { + throw $exception; + } + } + + if ( $existing_subscriber ) { + API::MP( 'v1' )->subscribeToLists( $subscriber['email'], (array) $settings['mailpoet3_lists'] ); + } + } + + /** + * @param Form_Record $record + * + * @return array + */ + private function map_fields( $record ) { + $settings = $record->get( 'form_settings' ); + $fields = $record->get( 'fields' ); + + $subscriber = []; + + foreach ( $settings['mailpoet3_fields_map'] as $map_item ) { + if ( empty( $fields[ $map_item['local_id'] ]['value'] ) ) { + continue; + } + + $subscriber[ $map_item['remote_id'] ] = $fields[ $map_item['local_id'] ]['value']; + } + + return $subscriber; + } + + protected function get_fields_map_control_options() { + $mailpoet_fields = [ + [ + 'remote_id' => 'first_name', + 'remote_label' => esc_html__( 'First Name', 'elementor-pro' ), + 'remote_type' => 'text', + ], + [ + 'remote_id' => 'last_name', + 'remote_label' => esc_html__( 'Last Name', 'elementor-pro' ), + 'remote_type' => 'text', + ], + [ + 'remote_id' => 'email', + 'remote_label' => esc_html__( 'Email', 'elementor-pro' ), + 'remote_type' => 'email', + 'remote_required' => true, + ], + ]; + + $fields = API::MP( 'v1' )->getSubscriberFields(); + + if ( ! empty( $fields ) && is_array( $fields ) ) { + foreach ( $fields as $index => $remote ) { + if ( in_array( $remote['id'], [ 'first_name', 'last_name', 'email' ] ) ) { + continue; + } + $mailpoet_fields[] = [ + 'remote_id' => $remote['id'], + 'remote_label' => $remote['name'], + 'remote_type' => 'text', + ]; + } + } + + return [ + 'default' => $mailpoet_fields, + 'condition' => [ + 'mailpoet3_lists!' => '', + ], + ]; + } +} diff --git a/modules/forms/actions/redirect.php b/modules/forms/actions/redirect.php new file mode 100644 index 0000000..f315c8f --- /dev/null +++ b/modules/forms/actions/redirect.php @@ -0,0 +1,79 @@ +start_controls_section( + 'section_redirect', + [ + 'label' => esc_html__( 'Redirect', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + $widget->add_control( + 'redirect_to', + [ + 'label' => esc_html__( 'Redirect To', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'ai' => [ + 'active' => false, + ], + 'dynamic' => [ + 'active' => true, + 'categories' => [ + TagsModule::POST_META_CATEGORY, + TagsModule::TEXT_CATEGORY, + TagsModule::URL_CATEGORY, + ], + ], + 'label_block' => true, + 'render_type' => 'none', + 'classes' => 'elementor-control-direction-ltr', + ] + ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( + $element['settings']['redirect_to'] + ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $redirect_to = $record->get_form_settings( 'redirect_to' ); + + $redirect_to = $record->replace_setting_shortcodes( $redirect_to, true ); + + $redirect_to = esc_url_raw( $redirect_to ); + + if ( ! empty( $redirect_to ) && filter_var( $redirect_to, FILTER_VALIDATE_URL ) ) { + $ajax_handler->add_response_data( 'redirect_url', $redirect_to ); + } + } +} diff --git a/modules/forms/actions/slack.php b/modules/forms/actions/slack.php new file mode 100644 index 0000000..91e9322 --- /dev/null +++ b/modules/forms/actions/slack.php @@ -0,0 +1,252 @@ +start_controls_section( + 'section_slack', + [ + 'label' => esc_html__( 'Slack', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + $widget->add_control( + 'slack_webhook', + [ + 'label' => esc_html__( 'Webhook URL', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => 'https://hooks.slack.com/services/', + 'ai' => [ + 'active' => false, + ], + 'label_block' => true, + 'separator' => 'before', + 'description' => esc_html__( 'Enter the webhook URL that will receive the form\'s submitted data.', 'elementor-pro' ) . ' ' . sprintf( '%s.', 'https://slack.com/apps/A0F7XDUAZ-incoming-webhooks/', esc_html__( 'Click here for Instructions', 'elementor-pro' ) ), + 'render_type' => 'none', + 'classes' => 'elementor-control-direction-ltr', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'slack_channel', + [ + 'label' => esc_html__( 'Channel', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'slack_username', + [ + 'label' => esc_html__( 'Username', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'slack_pretext', + [ + 'label' => esc_html__( 'Pre Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'slack_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'slack_text', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'slack_add_fields', + [ + 'label' => esc_html__( 'Form Data', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + ] + ); + + $widget->add_control( + 'slack_add_ts', + [ + 'label' => esc_html__( 'Timestamp', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + ] + ); + + $widget->add_control( + 'slack_webhook_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'alpha' => false, + 'default' => '#D30C5C', + ] + ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( + $element['slack_add_ts'], + $element['slack_add_fields'], + $element['slack_webhook_color'], + $element['slack_text'], + $element['slack_pretext'], + $element['slack_title'], + $element['slack_username'], + $element['slack_webhook'], + $element['slack_channel'] + ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $settings = $record->get( 'form_settings' ); + + if ( empty( $settings['slack_webhook'] ) || false === strpos( $settings['slack_webhook'], 'https://hooks.slack.com/services/' ) ) { + return; + } + + // Build slack webhook data + $webhook_data = [ + 'username' => isset( $settings['slack_username'] ) ? $settings['slack_username'] : '', + ]; + + if ( ! empty( $settings['slack_channel'] ) ) { + $webhook_data['channel'] = $settings['slack_channel']; + } + + // The nonce already validated + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $referrer = Utils::_unstable_get_super_global_value( $_POST, 'referrer' ); + + $attachment = [ + 'text' => esc_html__( 'A new Form Submission has been received', 'elementor-pro' ), + 'title' => esc_html__( 'A new Submission', 'elementor-pro' ), + 'color' => isset( $settings['slack_webhook_color'] ) ? $settings['slack_webhook_color'] : '#D30C5C', + 'title_link' => esc_url( $referrer ?? site_url() ), + ]; + + if ( ! empty( $settings['slack_title'] ) ) { + $attachment['title'] = $settings['slack_title']; + } + + if ( ! empty( $settings['slack_text'] ) ) { + $attachment['text'] = $settings['slack_text']; + } + + if ( ! empty( $settings['slack_pretext'] ) ) { + $attachment['pretext'] = $settings['slack_pretext']; + } + + if ( ! empty( $settings['slack_add_fields'] ) && 'yes' === $settings['slack_add_fields'] ) { + // prepare Form Data + $raw_fields = $record->get( 'fields' ); + $fields = []; + foreach ( $raw_fields as $id => $field ) { + $fields[] = [ + 'title' => $field['title'] ? $field['title'] : $id, + 'value' => $field['value'], + 'short' => false, + ]; + } + + $attachment['fields'] = $fields; + } + + if ( ! empty( $settings['slack_add_ts'] ) && 'yes' === $settings['slack_add_ts'] ) { + $attachment = array_merge( $attachment, [ + 'footer' => sprintf( + /* translators: %s: Elementor. */ + esc_html__( 'Powered by %s', 'elementor-pro' ), + 'Elementor' + ), + 'footer_icon' => is_ssl() ? ELEMENTOR_ASSETS_URL . 'images/logo-icon.png' : null, + 'ts' => time(), + ] ); + } + + $webhook_data['attachments'] = [ $attachment ]; + + $webhook_data = apply_filters( 'elementor_pro/forms/slack/webhook_args', $webhook_data ); + + $response = wp_remote_post( $settings['slack_webhook'], [ + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'body' => wp_json_encode( $webhook_data ), + ] ); + + if ( 200 !== (int) wp_remote_retrieve_response_code( $response ) ) { + throw new \Exception( 'Webhook error.' ); + } + } +} diff --git a/modules/forms/actions/webhook.php b/modules/forms/actions/webhook.php new file mode 100644 index 0000000..44bd60b --- /dev/null +++ b/modules/forms/actions/webhook.php @@ -0,0 +1,123 @@ +start_controls_section( + 'section_webhook', + [ + 'label' => esc_html__( 'Webhook', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + $widget->add_control( + 'webhooks', + [ + 'label' => esc_html__( 'Webhook URL', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'https://your-webhook-url.com', 'elementor-pro' ), + 'ai' => [ + 'active' => false, + ], + 'label_block' => true, + 'separator' => 'before', + 'description' => esc_html__( 'Enter the integration URL (like Zapier) that will receive the form\'s submitted data.', 'elementor-pro' ), + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $widget->add_control( + 'webhooks_advanced_data', + [ + 'label' => esc_html__( 'Advanced Data', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'no', + 'render_type' => 'none', + ] + ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) {} + + public function run( $record, $ajax_handler ) { + $settings = $record->get( 'form_settings' ); + + if ( empty( $settings['webhooks'] ) ) { + return; + } + + if ( isset( $settings['webhooks_advanced_data'] ) && 'yes' === $settings['webhooks_advanced_data'] ) { + $body['form'] = [ + 'id' => $settings['id'], + 'name' => $settings['form_name'], + ]; + + $body['fields'] = $record->get( 'fields' ); + $body['meta'] = $record->get( 'meta' ); + } else { + $body = $record->get_formatted_data( true ); + $body['form_id'] = $settings['id']; + $body['form_name'] = $settings['form_name']; + } + + $args = [ + 'body' => $body, + ]; + + /** + * Forms webhook request arguments. + * + * Filters the request arguments delivered by the form webhook when executing + * an ajax request. + * + * @since 1.0.0 + * + * @param array $args Webhook request arguments. + * @param Form_Record $record An instance of the form record. + */ + $args = apply_filters( 'elementor_pro/forms/webhooks/request_args', $args, $record ); + + $response = wp_remote_post( $settings['webhooks'], $args ); + + /** + * Elementor form webhook response. + * + * Fires when the webhook response is retrieved by Elementor forms. This hook + * allows developers to add functionality after recieving webhook responses. + * + * @since 1.0.0 + * + * @param \WP_Error|array $response The response or WP_Error on failure. + * @param Form_Record $record An instance of the form record. + */ + do_action( 'elementor_pro/forms/webhooks/response', $response, $record ); + + if ( 200 !== (int) wp_remote_retrieve_response_code( $response ) ) { + throw new \Exception( 'Webhook error.' ); + } + } +} diff --git a/modules/forms/classes/action-base.php b/modules/forms/classes/action-base.php new file mode 100644 index 0000000..202727c --- /dev/null +++ b/modules/forms/classes/action-base.php @@ -0,0 +1,44 @@ +get_name(); + } + + /** + * @param Form_Record $record + * @param Ajax_Handler $ajax_handler + */ + abstract public function run( $record, $ajax_handler ); + + /** + * @param Form $form + */ + abstract public function register_settings_section( $form ); + + /** + * @param array $element + */ + abstract public function on_export( $element ); +} diff --git a/modules/forms/classes/activecampaign-handler.php b/modules/forms/classes/activecampaign-handler.php new file mode 100644 index 0000000..0b63178 --- /dev/null +++ b/modules/forms/classes/activecampaign-handler.php @@ -0,0 +1,149 @@ +init_rest_client( $api_key, $base_url ); + + if ( ! $this->is_valid_api_key() ) { + throw new \Exception( 'Invalid API key or URL.' ); + } + } + + private function init_rest_client( $api_key, $base_url ) { + $this->api_key = $api_key; + $this->rest_client = new Rest_Client( trailingslashit( $base_url ) . 'admin/api.php' ); + } + + /** + * validate api key + * + * @return bool + * @throws \Exception + */ + private function is_valid_api_key() { + $lists = $this->get_lists(); + + if ( ! empty( $lists ) ) { + return true; + } + + $this->api_key = ''; + + return false; + } + + /** + * get ActiveCampaign lists associated with API key + * @return array + * @throws \Exception + */ + public function get_lists() { + $results = $this->rest_client->get( '?api_action=list_list', [ + 'api_key' => $this->api_key, + 'ids' => 'all', + 'api_output' => 'json', + ] ); + + $lists = [ + '' => esc_html__( 'Select...', 'elementor-pro' ), + ]; + + if ( ! empty( $results['body'] ) ) { + foreach ( $results['body'] as $index => $list ) { + if ( ! is_array( $list ) ) { + continue; + } + + $lists[ $list['id'] ] = $list['name']; + } + } + + $return_array = [ + 'lists' => $lists, + 'fields' => $this->get_fields(), + ]; + + return $return_array; + } + + /** + * get ActiveCampaign custom fields associated with API key + * @return array + * @throws \Exception + */ + private function get_fields() { + $results = $this->rest_client->get( '?api_action=list_field_view', [ + 'api_key' => $this->api_key, + 'ids' => 'all', + 'api_output' => 'json', + ] ); + + $fields = []; + + if ( ! empty( $results['body'] ) ) { + foreach ( $results['body'] as $index => $field ) { + if ( ! is_array( $field ) ) { + continue; + } + $fields[] = [ + 'remote_label' => $field['title'], + 'remote_type' => $this->normalize_type( $field['type'] ), + 'remote_id' => 'field[' . $field['id'] . ',0]', + 'remote_required' => (bool) $field['isrequired'], + ]; + } + } + + return $fields; + } + + private function normalize_type( $type ) { + static $types = [ + 'text' => 'text', + 'number' => 'number', + 'address' => 'text', + 'phone' => 'text', + 'date' => 'text', + 'url' => 'url', + 'imageurl' => 'url', + 'radio' => 'radio', + 'dropdown' => 'select', + 'birthday' => 'text', + 'zip' => 'text', + ]; + + return $types[ $type ]; + } + + /** + * create contact at Activecampaign via api + * + * @param array $subscriber_data + * + * @return array|mixed + * @throws \Exception + */ + public function create_subscriber( $subscriber_data = [] ) { + $end_point = '?api_action=contact_sync&api_key=' . $this->api_key . '&api_output=json'; + + return $this->rest_client->request( 'POST', $end_point, $subscriber_data ); + } +} diff --git a/modules/forms/classes/ajax-handler.php b/modules/forms/classes/ajax-handler.php new file mode 100644 index 0000000..eadbf91 --- /dev/null +++ b/modules/forms/classes/ajax-handler.php @@ -0,0 +1,310 @@ + [], + 'error' => [], + 'admin_error' => [], + ]; + public $data = []; + public $errors = []; + + private $current_form; + + const SUCCESS = 'success'; + const ERROR = 'error'; + const FIELD_REQUIRED = 'required_field'; + const INVALID_FORM = 'invalid_form'; + const SERVER_ERROR = 'server_error'; + const SUBSCRIBER_ALREADY_EXISTS = 'subscriber_already_exists'; + + public static function is_form_submitted() { + // PHPCS - No nonce is required, all visitors may send the form. + return wp_doing_ajax() && isset( $_POST['action'] ) && 'elementor_pro_forms_send_form' === $_POST['action']; // phpcs:ignore WordPress.Security.NonceVerification.Missing + } + + public static function get_default_messages() { + return [ + self::SUCCESS => esc_html__( 'Your submission was successful.', 'elementor-pro' ), + self::ERROR => esc_html__( 'Your submission failed because of an error.', 'elementor-pro' ), + self::FIELD_REQUIRED => esc_html__( 'This field is required.', 'elementor-pro' ), + self::INVALID_FORM => esc_html__( 'Your submission failed because the form is invalid.', 'elementor-pro' ), + self::SERVER_ERROR => esc_html__( 'Your submission failed because of a server error.', 'elementor-pro' ), + self::SUBSCRIBER_ALREADY_EXISTS => esc_html__( 'Subscriber already exists.', 'elementor-pro' ), + ]; + } + + public static function get_default_message( $id, $settings ) { + if ( ! empty( $settings['custom_messages'] ) ) { + $field_id = $id . '_message'; + if ( isset( $settings[ $field_id ] ) ) { + return $settings[ $field_id ]; + } + } + + $default_messages = self::get_default_messages(); + + return isset( $default_messages[ $id ] ) ? $default_messages[ $id ] : esc_html__( 'Unknown error.', 'elementor-pro' ); + } + + public function ajax_send_form() { + $post_data = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing + // $post_id that holds the form settings. + $post_id = $post_data['post_id']; + + // $queried_id the post for dynamic values data. + if ( isset( $post_data['queried_id'] ) ) { + $queried_id = $post_data['queried_id']; + } else { + $queried_id = $post_id; + } + + // Make the post as global post for dynamic values. + Plugin::elementor()->db->switch_to_post( $queried_id ); + + $form_id = $post_data['form_id']; + + $elementor = Plugin::elementor(); + $document = $elementor->documents->get( $post_id ); + $form = null; + $template_id = null; + + if ( $document ) { + $form = Module::find_element_recursive( $document->get_elements_data(), $form_id ); + } + + if ( ! empty( $form['templateID'] ) ) { + $template = Plugin::elementor()->documents->get( $form['templateID'] ); + + if ( ! $template ) { + return false; + } + + $template_id = $template->get_id(); + $form = $template->get_elements_data()[0]; + } + + if ( empty( $form ) ) { + $this + ->add_error_message( self::get_default_message( self::INVALID_FORM, [] ) ) + ->send(); + } + + // restore default values + $widget = $elementor->elements_manager->create_element_instance( $form ); + $form['settings'] = $widget->get_settings_for_display(); + $form['settings']['id'] = $form_id; + $form['settings']['form_post_id'] = $template_id ? $template_id : $post_id; + + // TODO: Should be removed if there is an ability to edit "global widgets" + $form['settings']['edit_post_id'] = $post_id; + + $this->current_form = $form; + + if ( empty( $form['settings']['form_fields'] ) ) { + $this + ->add_error_message( self::get_default_message( self::INVALID_FORM, $form['settings'] ) ) + ->send(); + } + + $record = new Form_Record( $post_data['form_fields'], $form ); + + if ( ! $record->validate( $this ) ) { + $this + ->add_error( $record->get( 'errors' ) ) + ->add_error_message( self::get_default_message( self::ERROR, $form['settings'] ) ) + ->send(); + } + + $record->process_fields( $this ); + //check for process errors + if ( ! empty( $this->errors ) ) { + $this->send(); + } + + $module = Module::instance(); + + $actions = $module->actions_registrar->get(); + $errors = array_merge( $this->messages['error'], $this->messages['admin_error'] ); + + /** + * Filters the record before it sent to actions after submit. + * + * @since 3.3.0 + * + * @param Form_Record $record The form record. + * @param Ajax_Handler $this The class that handle the submission of the record + */ + $record = apply_filters( 'elementor_pro/forms/record/actions_before', $record, $this ); + + foreach ( $actions as $action ) { + if ( ! in_array( $action->get_name(), $form['settings']['submit_actions'], true ) ) { + continue; + } + + $exception = null; + + try { + $action->run( $record, $this ); + + $this->handle_bc_errors( $errors ); + } catch ( \Exception $e ) { + $exception = $e; + + // Add an admin error. + if ( ! in_array( $exception->getMessage(), $this->messages['admin_error'], true ) ) { + $this->add_admin_error_message( "{$action->get_label()} {$exception->getMessage()}" ); + } + + // Add a user error. + $this->add_error_message( $this->get_default_message( self::ERROR, $this->current_form['settings'] ) ); + } + + $errors = array_merge( $this->messages['error'], $this->messages['admin_error'] ); + + /** + * After form actions run. + * + * Fires after Elementor forms run actions. This hook allows + * developers to add functionality after certain actions run. + * + * @param Action_Base $action An instance of form action. + * @param \Exception|null $exception An instance of the exception. + */ + do_action( 'elementor_pro/forms/actions/after_run', $action, $exception ); + } + + $activity_log = $module->get_component( 'activity_log' ); + if ( $activity_log ) { + $activity_log->run( $record, $this ); + } + + $cf7db = $module->get_component( 'cf7db' ); + if ( $cf7db ) { + $cf7db->run( $record, $this ); + } + + /** + * New Elementor form record. + * + * Fires before a new form record is sent by ajax. This hook allows + * developers to add functionality before a new form record is sent. + * + * @since 1.0.0 + * + * @param Form_Record $record An instance of the form record. + * @param Ajax_Handler $this An instance of the ajax handler. + */ + do_action( 'elementor_pro/forms/new_record', $record, $this ); + + $this->send(); + } + + public function add_success_message( $message ) { + $this->messages['success'][] = $message; + + return $this; + } + + public function add_response_data( $key, $data ) { + $this->data[ $key ] = $data; + + return $this; + } + + public function add_error_message( $message ) { + $this->messages['error'][] = $message; + $this->set_success( false ); + + return $this; + } + + public function add_error( $field, $message = '' ) { + if ( is_array( $field ) ) { + $this->errors += $field; + } else { + $this->errors[ $field ] = $message; + } + + $this->set_success( false ); + + return $this; + } + + public function add_admin_error_message( $message ) { + $this->messages['admin_error'][] = $message; + $this->set_success( false ); + + return $this; + } + + public function set_success( $bool ) { + $this->is_success = $bool; + + return $this; + } + + public function send() { + if ( $this->is_success ) { + wp_send_json_success( [ + 'message' => $this->get_default_message( self::SUCCESS, $this->current_form['settings'] ), + 'data' => $this->data, + ] ); + } + + if ( empty( $this->messages['error'] ) && ! empty( $this->errors ) ) { + $this->add_error_message( $this->get_default_message( self::INVALID_FORM, $this->current_form['settings'] ) ); + } + + $post_id = Utils::_unstable_get_super_global_value( $_POST, 'post_id' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + + $error_msg = implode( '
    ', $this->messages['error'] ); + if ( current_user_can( 'edit_post', $post_id ) && ! empty( $this->messages['admin_error'] ) ) { + $this->add_admin_error_message( esc_html__( 'This message is not visible to site visitors.', 'elementor-pro' ) ); + $error_msg .= '
    ' . implode( '
    ', $this->messages['admin_error'] ) . '
    '; + } + + wp_send_json_error( [ + 'message' => $error_msg, + 'errors' => $this->errors, + 'data' => $this->data, + ] ); + } + + public function get_current_form() { + return $this->current_form; + } + + /** + * BC: checks if the current action add some errors to the errors array + * if it add an error the "run" method treat it as a failed action. + * + * @param $errors + * + * @throws \Exception + */ + private function handle_bc_errors( $errors ) { + $current_errors = array_merge( $this->messages['error'], $this->messages['admin_error'] ); + $errors_diff = array_diff( $current_errors, $errors ); + + if ( count( $errors_diff ) > 0 ) { + throw new \Exception( implode( ', ', $errors_diff ) ); + } + } + + public function __construct() { + add_action( 'wp_ajax_elementor_pro_forms_send_form', [ $this, 'ajax_send_form' ] ); + add_action( 'wp_ajax_nopriv_elementor_pro_forms_send_form', [ $this, 'ajax_send_form' ] ); + } +} diff --git a/modules/forms/classes/akismet.php b/modules/forms/classes/akismet.php new file mode 100644 index 0000000..10ebcab --- /dev/null +++ b/modules/forms/classes/akismet.php @@ -0,0 +1,181 @@ +is_akismet_active() ) { + return; + } + + $form->start_controls_section( + 'section_akismet', + [ + 'label' => esc_html__( 'Akismet Spam Protection', 'elementor-pro' ), + ] + ); + + $form->add_control( + 'akismet_info', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'Assign shortcodes to the fields below to enable spam protection on your form. %1$sShow me how%2$s', 'elementor-pro' ), + '', + '' + ), + ] + ); + + $form->add_control( + 'akismet_author', + [ + 'label' => esc_html__( 'Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => 'e.g. [field id="name"]', + 'ai' => [ + 'active' => false, + ], + 'label_block' => true, + 'render_type' => 'none', + ] + ); + + $form->add_control( + 'akismet_author_url', + [ + 'label' => esc_html__( 'URL', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => 'e.g. [field id="url"]', + 'ai' => [ + 'active' => false, + ], + 'label_block' => true, + 'render_type' => 'none', + ] + ); + + $form->add_control( + 'akismet_author_email', + [ + 'label' => esc_html__( 'Email', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => 'e.g. [field id="email"]', + 'ai' => [ + 'active' => false, + ], + 'label_block' => true, + 'render_type' => 'none', + ] + ); + + $form->add_control( + 'akismet_content', + [ + 'label' => esc_html__( 'Message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => 'e.g. [field id="message"]', + 'ai' => [ + 'active' => false, + ], + 'label_block' => true, + 'render_type' => 'none', + ] + ); + + $form->end_controls_section(); + } + + private function is_akismet_active() : bool { + $akismet_key = \Akismet::get_api_key(); + + return ! empty( $akismet_key ); + } + + /** + * @param Form_Record $record + * @param Ajax_Handler $ajax_handler + */ + public function validation( $record, $ajax_handler ) { + if ( ! $this->is_akismet_active() ) { + return; + } + + if ( ! $this->is_spammed( $record ) ) { + return; + } + + $ajax_handler->add_error_message( + esc_html__( 'We couldn’t submit your responses because they seem like spam.', 'elementor-pro' ) + ); + + $ajax_handler->add_error( 'akismet', esc_html__( 'Spam detected', 'elementor-pro' ) ); + } + + private function is_spammed( Form_Record $record ) : bool { + $settings = $record->get( 'form_settings' ); + + $params = []; + + $params['comment_author'] = $this->get_parsed_content( $record, $settings['akismet_author'] ); + $params['comment_author_email'] = $this->get_parsed_content( $record, $settings['akismet_author_email'] ); + $params['comment_author_url'] = $this->get_parsed_content( $record, $settings['akismet_author_url'] ); + $params['comment_content'] = $this->get_parsed_content( $record, $settings['akismet_content'] ); + + $params['blog'] = get_option( 'home' ); + $params['blog_lang'] = get_locale(); + $params['blog_charset'] = get_option( 'blog_charset' ); + + $params['user_ip'] = Utils::get_client_ip(); + $params['referrer'] = wp_get_referer(); + + if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) { + $params['user_agent'] = sanitize_textarea_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); + } + + // http://blog.akismet.com/2012/06/19/pro-tip-tell-us-your-comment_type/ + $params['comment_type'] = 'contact-form'; + + $ignore = array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' ); + foreach ( $_SERVER as $key => $value ) { + if ( ! in_array( $key, $ignore ) && is_string( $value ) ) { + $params[ $key ] = $value; + } + } + + return $this->remote_check_is_spam( $params ); + } + + private function get_parsed_content( $record, $content ) { + $setting = trim( $content ); + + return $record->replace_setting_shortcodes( $setting ); + } + + private function remote_check_is_spam( $params ) { + $response = \Akismet::http_post( _http_build_query( $params, '', '&' ), 'comment-check' ); + + return ( 'true' === $response[1] ); + } +} diff --git a/modules/forms/classes/convertkit-handler.php b/modules/forms/classes/convertkit-handler.php new file mode 100644 index 0000000..590373c --- /dev/null +++ b/modules/forms/classes/convertkit-handler.php @@ -0,0 +1,132 @@ +init_rest_client( $api_key ); + + if ( ! $this->is_valid_api_key() ) { + throw new \Exception( 'Invalid API key.' ); + } + } + + private function init_rest_client( $api_key ) { + $this->api_key = $api_key; + $this->rest_client = new Rest_Client( 'https://api.convertkit.com/v3/' ); + } + + /** + * validate api key + * + * @return bool + * @throws \Exception + */ + private function is_valid_api_key() { + $forms = $this->get_forms(); + if ( ! empty( $forms ) ) { + return true; + } + $this->api_key = ''; + + return false; + } + + public function get_forms_and_tags() { + $forms = $this->get_forms(); + $tags = $this->get_tags(); + + return [ + 'data' => [ + 'forms' => $forms['forms'], + 'tags' => $tags['tags'], + ], + ]; + } + + /** + * get GetResponse lists associated with API key + * @return array + * @throws \Exception + */ + public function get_forms() { + $results = $this->rest_client->get( 'forms/?api_key=' . $this->api_key ); + + $forms = [ + '' => esc_html__( 'Select...', 'elementor-pro' ), + ]; + + if ( ! empty( $results['body']['forms'] ) ) { + foreach ( $results['body']['forms'] as $index => $form ) { + if ( ! is_array( $form ) ) { + continue; + } + $forms[ $form['id'] ] = $form['name']; + } + } + + $return_array = [ + 'forms' => $forms, + ]; + + return $return_array; + } + + public function get_tags() { + $results = $this->rest_client->get( 'tags/?api_key=' . $this->api_key ); + + $tags = [ + '' => esc_html__( 'Select...', 'elementor-pro' ), + ]; + + if ( ! empty( $results['body']['tags'] ) ) { + foreach ( $results['body']['tags'] as $index => $tag ) { + if ( ! is_array( $tag ) ) { + continue; + } + $tags[ $tag['id'] ] = $tag['name']; + } + } + + $return_array = [ + 'tags' => $tags, + ]; + + return $return_array; + } + + /** + * create contact at ConvertKit via api + * + * @param array $subscriber_data + * + * @return array|mixed + * @throws \Exception + */ + public function create_subscriber( $form_id, $subscriber_data = [] ) { + $endpoint = sprintf( 'forms/' . $form_id . '/subscribe?api_key=%s', $this->api_key ); + $this->rest_client->add_headers( 'Content-Type', 'application/json' ); + + return $this->rest_client->post( $endpoint, $subscriber_data ); + } +} diff --git a/modules/forms/classes/drip-handler.php b/modules/forms/classes/drip-handler.php new file mode 100644 index 0000000..4d8d45b --- /dev/null +++ b/modules/forms/classes/drip-handler.php @@ -0,0 +1,93 @@ +init_rest_client( $api_token ); + if ( ! $this->is_valid_api_token() ) { + throw new \Exception( 'Invalid API key.' ); + } + } + + private function init_rest_client( $api_token ) { + $this->api_token = $api_token; + $this->rest_client = new Rest_Client( 'https://api.getdrip.com/v2/' ); + $this->rest_client->add_headers( [ + 'Authorization' => 'Basic ' . base64_encode( $this->api_token ), + 'Content-Type' => 'application/vnd.api+json', + ] ); + } + + /** + * validate api token + * + * @return bool + * @throws \Exception + */ + private function is_valid_api_token() { + $accounts = $this->get_accounts(); + if ( ! empty( $accounts ) ) { + return true; + } + $this->api_token = ''; + + return false; + } + + /** + * get drip accounts associated with API token + * @return array + * @throws \Exception + */ + public function get_accounts() { + $results = $this->rest_client->get( 'accounts' ); + + $accounts = [ + '' => esc_html__( 'Select...', 'elementor-pro' ), + ]; + + if ( ! empty( $results['body']['accounts'] ) ) { + foreach ( $results['body']['accounts'] as $index => $account ) { + $accounts[ $account['id'] ] = $account['name']; + } + } + + $return_array = [ + 'accounts' => $accounts, + ]; + + return $return_array; + } + + /** + * create subscriber at drip via api + * + * @param string $account_id + * @param array $subscriber_data + * + * @return array|mixed + * @throws \Exception + */ + public function create_subscriber( $account_id = '', $subscriber_data = [] ) { + $end_point = sprintf( '%s/subscribers/', $account_id ); + + return $this->rest_client->post( $end_point, [ 'subscribers' => [ $subscriber_data ] ] ); + } +} diff --git a/modules/forms/classes/form-base.php b/modules/forms/classes/form-base.php new file mode 100644 index 0000000..09ff623 --- /dev/null +++ b/modules/forms/classes/form-base.php @@ -0,0 +1,273 @@ +actions_registrar->get(); + + foreach ( $actions as $action ) { + $new_element_data = $action->on_export( $element ); + if ( null !== $new_element_data ) { + $element = $new_element_data; + } + } + + return $element; + } + + public static function get_button_sizes() { + return [ + 'xs' => esc_html__( 'Extra Small', 'elementor-pro' ), + 'sm' => esc_html__( 'Small', 'elementor-pro' ), + 'md' => esc_html__( 'Medium', 'elementor-pro' ), + 'lg' => esc_html__( 'Large', 'elementor-pro' ), + 'xl' => esc_html__( 'Extra Large', 'elementor-pro' ), + ]; + } + + protected function make_textarea_field( $item, $item_index ) { + $this->add_render_attribute( 'textarea' . $item_index, [ + 'class' => [ + 'elementor-field-textual', + 'elementor-field', + esc_attr( $item['css_classes'] ), + 'elementor-size-' . $item['input_size'], + ], + 'name' => $this->get_attribute_name( $item ), + 'id' => $this->get_attribute_id( $item ), + 'rows' => $item['rows'], + ] ); + + if ( $item['placeholder'] ) { + $this->add_render_attribute( 'textarea' . $item_index, 'placeholder', $item['placeholder'] ); + } + + if ( $item['required'] ) { + $this->add_required_attribute( 'textarea' . $item_index ); + } + + $value = empty( $item['field_value'] ) ? '' : $item['field_value']; + + return ''; + } + + protected function make_select_field( $item, $i ) { + $this->add_render_attribute( + [ + 'select-wrapper' . $i => [ + 'class' => [ + 'elementor-field', + 'elementor-select-wrapper', + 'remove-before', + esc_attr( $item['css_classes'] ), + ], + ], + 'select' . $i => [ + 'name' => $this->get_attribute_name( $item ) . ( ! empty( $item['allow_multiple'] ) ? '[]' : '' ), + 'id' => $this->get_attribute_id( $item ), + 'class' => [ + 'elementor-field-textual', + 'elementor-size-' . $item['input_size'], + ], + ], + ] + ); + + if ( $item['required'] ) { + $this->add_required_attribute( 'select' . $i ); + } + + if ( $item['allow_multiple'] ) { + $this->add_render_attribute( 'select' . $i, 'multiple' ); + if ( ! empty( $item['select_size'] ) ) { + $this->add_render_attribute( 'select' . $i, 'size', $item['select_size'] ); + } + } + + $options = preg_split( "/\\r\\n|\\r|\\n/", $item['field_options'] ); + + if ( ! $options ) { + return ''; + } + + ob_start(); + ?> +
    print_render_attribute_string( 'select-wrapper' . $i ); ?>> +
    + 'eicons', + 'value' => 'eicon-caret-down', + 'position' => 'right', + ]; + Icons_Manager::render_icon( $icon, [ 'aria-hidden' => 'true' ] ); + } + ?> +
    + +
    + '; + foreach ( $options as $key => $option ) { + $element_id = esc_attr( $item['custom_id'] ) . $key; + $html_id = $this->get_attribute_id( $item ) . '-' . $key; + $option_label = $option; + $option_value = $option; + if ( false !== strpos( $option, '|' ) ) { + list( $option_label, $option_value ) = explode( '|', $option ); + } + + $this->add_render_attribute( + $element_id, + [ + 'type' => $type, + 'value' => $option_value, + 'id' => $html_id, + 'name' => $this->get_attribute_name( $item ) . ( ( 'checkbox' === $type && count( $options ) > 1 ) ? '[]' : '' ), + ] + ); + + if ( ! empty( $item['field_value'] ) && $option_value === $item['field_value'] ) { + $this->add_render_attribute( $element_id, 'checked', 'checked' ); + } + + if ( $item['required'] && 'radio' === $type ) { + $this->add_required_attribute( $element_id ); + } + + $html .= 'get_render_attribute_string( $element_id ) . '> '; + } + $html .= '
    '; + } + + return $html; + } + + protected function form_fields_render_attributes( $i, $instance, $item ) { + $this->add_render_attribute( + [ + 'field-group' . $i => [ + 'class' => [ + 'elementor-field-type-' . $item['field_type'], + 'elementor-field-group', + 'elementor-column', + 'elementor-field-group-' . $item['custom_id'], + ], + ], + 'input' . $i => [ + 'type' => $item['field_type'], + 'name' => $this->get_attribute_name( $item ), + 'id' => $this->get_attribute_id( $item ), + 'class' => [ + 'elementor-field', + 'elementor-size-' . $item['input_size'], + empty( $item['css_classes'] ) ? '' : esc_attr( $item['css_classes'] ), + ], + ], + 'label' . $i => [ + 'for' => $this->get_attribute_id( $item ), + 'class' => 'elementor-field-label', + ], + ] + ); + + if ( empty( $item['width'] ) ) { + $item['width'] = '100'; + } + + $this->add_render_attribute( 'field-group' . $i, 'class', 'elementor-col-' . $item['width'] ); + + if ( ! empty( $item['width_tablet'] ) ) { + $this->add_render_attribute( 'field-group' . $i, 'class', 'elementor-md-' . $item['width_tablet'] ); + } + + if ( $item['allow_multiple'] ) { + $this->add_render_attribute( 'field-group' . $i, 'class', 'elementor-field-type-' . $item['field_type'] . '-multiple' ); + } + + if ( ! empty( $item['width_mobile'] ) ) { + $this->add_render_attribute( 'field-group' . $i, 'class', 'elementor-sm-' . $item['width_mobile'] ); + } + + // Allow zero as placeholder. + if ( ! Utils::is_empty( $item['placeholder'] ) ) { + $this->add_render_attribute( 'input' . $i, 'placeholder', $item['placeholder'] ); + } + + if ( ! empty( $item['field_value'] ) ) { + $this->add_render_attribute( 'input' . $i, 'value', $item['field_value'] ); + } + + if ( ! $instance['show_labels'] ) { + $this->add_render_attribute( 'label' . $i, 'class', 'elementor-screen-only' ); + } + + if ( ! empty( $item['required'] ) ) { + $class = 'elementor-field-required'; + if ( ! empty( $instance['mark_required'] ) ) { + $class .= ' elementor-mark-required'; + } + $this->add_render_attribute( 'field-group' . $i, 'class', $class ); + $this->add_required_attribute( 'input' . $i ); + } + } + + public function render_plain_content() {} + + public function get_attribute_name( $item ) { + return "form_fields[{$item['custom_id']}]"; + } + + public function get_attribute_id( $item ) { + return 'form-field-' . esc_attr( $item['custom_id'] ); + } + + private function add_required_attribute( $element ) { + $this->add_render_attribute( $element, 'required', 'required' ); + $this->add_render_attribute( $element, 'aria-required', 'true' ); + } +} diff --git a/modules/forms/classes/form-record.php b/modules/forms/classes/form-record.php new file mode 100644 index 0000000..6464fae --- /dev/null +++ b/modules/forms/classes/form-record.php @@ -0,0 +1,347 @@ +fields; + + if ( $with_meta ) { + $fields = array_merge( $fields, $this->meta ); + } + + foreach ( $fields as $key => $field ) { + if ( empty( $field['title'] ) ) { + $formatted[ $no_label . ' ' . $key ] = $field['value']; + } else { + $formatted[ $field['title'] ] = $field['value']; + } + } + + return $formatted; + } + + /** + * @param Ajax_Handler $ajax_handler An instance of the ajax handler. + * + * @return bool + */ + public function validate( $ajax_handler ) { + foreach ( $this->fields as $id => $field ) { + $field_type = $field['type']; + if ( ! empty( $field['required'] ) && '' === $field['value'] && 'upload' !== $field_type ) { + $ajax_handler->add_error( $id, Ajax_Handler::get_default_message( Ajax_Handler::FIELD_REQUIRED, $this->form_settings ) ); + } + + /** + * Elementor form field validation. + * + * Fires when a single form field is being validated. This hook allows developers + * to validate individual field types. + * + * The dynamic portion of the hook name, `$field_type`, refers to the field type. + * + * @since 2.0.0 + * + * @param array $field Form field. + * @param Form_Record $this An instance of the form record. + * @param Ajax_Handler $ajax_handler An instance of the ajax handler. + */ + do_action( "elementor_pro/forms/validation/{$field_type}", $field, $this, $ajax_handler ); + } + + /** + * Elementor form validation. + * + * Fires when form fields are being validated. This hook allows developers + * to validate all form fields. + * + * @since 2.0.0 + * + * @param Form_Record $this An instance of the form record. + * @param Ajax_Handler $ajax_handler An instance of the ajax handler. + */ + do_action( 'elementor_pro/forms/validation', $this, $ajax_handler ); + + return empty( $ajax_handler->errors ); + } + + /** + * @param Ajax_Handler $ajax_handler An instance of the ajax handler. + * + */ + public function process_fields( $ajax_handler ) { + foreach ( $this->fields as $id => $field ) { + $field_type = $field['type']; + + /** + * Elementor form field process. + * + * Fires when a single form field is being processed. This hook allows developers + * to process individual field types. + * + * The dynamic portion of the hook name, `$field_type`, refers to the field type. + * + * @since 2.0.0 + * + * @param array $field Form field. + * @param Form_Record $this An instance of the form record. + * @param Ajax_Handler $ajax_handler An instance of the ajax handler. + */ + do_action( "elementor_pro/forms/process/{$field_type}", $field, $this, $ajax_handler ); + } + + /** + * Elementor form process. + * + * Fires when form fields are being processed. This hook allows developers + * to process all form fields. + * + * @since 2.0.0 + * + * @param Form_Record $this An instance of the form record. + * @param Ajax_Handler $ajax_handler An instance of the ajax handler. + */ + do_action( 'elementor_pro/forms/process', $this, $ajax_handler ); + } + + public function get( $property ) { + if ( isset( $this->{$property} ) ) { + return $this->{$property}; + } + + return null; + } + + public function set( $property, $value ) { + $this->{$property} = $value; + } + + public function get_form_settings( $setting ) { + if ( isset( $this->form_settings[ $setting ] ) ) { + return $this->form_settings[ $setting ]; + } + + return null; + } + + public function get_field( $args ) { + return wp_list_filter( $this->fields, $args ); + } + + public function remove_field( $id ) { + unset( $this->fields[ $id ] ); + } + + public function update_field( $field_id, $property, $value ) { + if ( ! isset( $this->fields[ $field_id ] ) || ! isset( $this->fields[ $field_id ][ $property ] ) ) { + return; + } + $this->fields[ $field_id ][ $property ] = $value; + } + + public function get_form_meta( $meta_keys = [] ) { + $result = []; + + foreach ( $meta_keys as $metadata_type ) { + switch ( $metadata_type ) { + case 'date': + $result['date'] = [ + 'title' => esc_html__( 'Date', 'elementor-pro' ), + 'value' => date_i18n( get_option( 'date_format' ) ), + ]; + break; + + case 'time': + $result['time'] = [ + 'title' => esc_html__( 'Time', 'elementor-pro' ), + 'value' => date_i18n( get_option( 'time_format' ) ), + ]; + break; + + case 'page_url': + $result['page_url'] = [ + 'title' => esc_html__( 'Page URL', 'elementor-pro' ), + 'value' => isset( $_POST['referrer'] ) ? esc_url_raw( wp_unslash( $_POST['referrer'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Missing + ]; + break; + + case 'page_title': + $result['page_title'] = [ + 'title' => esc_html__( 'Page Title', 'elementor-pro' ), + 'value' => isset( $_POST['referer_title'] ) ? sanitize_text_field( wp_unslash( $_POST['referer_title'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Missing + ]; + break; + + case 'user_agent': + $result['user_agent'] = [ + 'title' => esc_html__( 'User Agent', 'elementor-pro' ), + 'value' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_textarea_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '', + ]; + break; + + case 'remote_ip': + $result['remote_ip'] = [ + 'title' => esc_html__( 'Remote IP', 'elementor-pro' ), + 'value' => Utils::get_client_ip(), + ]; + break; + case 'credit': + $result['credit'] = [ + 'title' => esc_html__( 'Powered by', 'elementor-pro' ), + 'value' => esc_html__( 'Elementor', 'elementor-pro' ), + ]; + break; + } + } + + return $result; + } + + private function set_meta() { + $form_metadata = $this->form_settings['form_metadata']; + + if ( empty( $form_metadata ) ) { + return; + } + + $this->meta = $this->get_form_meta( $form_metadata ); + } + + private function set_fields() { + foreach ( $this->form_settings['form_fields'] as $form_field ) { + $field = [ + 'id' => $form_field['custom_id'], + 'type' => $form_field['field_type'], + 'title' => $form_field['field_label'], + 'value' => '', + 'raw_value' => '', + 'required' => ! empty( $form_field['required'] ), + ]; + + if ( 'upload' === $field['type'] ) { + $field['file_sizes'] = $form_field['file_sizes'] ?? ''; + $field['file_types'] = $form_field['file_types'] ?? ''; + $field['max_files'] = $form_field['max_files'] ?? ''; + $field['attachment_type'] = $form_field['attachment_type'] ?? ''; + } + + if ( isset( $this->sent_data[ $form_field['custom_id'] ] ) ) { + $field['raw_value'] = $this->sent_data[ $form_field['custom_id'] ]; + + $value = $field['raw_value']; + + if ( is_array( $value ) ) { + $value = implode( ', ', $value ); + } + + $field['value'] = $this->sanitize_field( $field, $value ); + } + $this->fields[ $form_field['custom_id'] ] = $field; + } + } + + private function sanitize_field( $field, $value ) { + $field_type = $field['type']; + switch ( $field_type ) { + case 'text': + case 'password': + case 'hidden': + case 'search': + case 'checkbox': + case 'radio': + case 'select': + $value = sanitize_text_field( $value ); + break; + case 'url': + $value = esc_url_raw( $value ); + break; + case 'textarea': + $value = sanitize_textarea_field( $value ); + break; + case 'email': + $value = sanitize_email( $value ); + break; + default: + /** + * Sanitize field value. + * + * Filters the value of the form field for sanitization purpose. This hook allows + * developers to add custom sanitization for field values. + * + * The dynamic portion of the hook name, `$field_type`, refers to the field type. + * + * @since 1.0.0 + * + * @param string $value The field value. + * @param array $field The field array. + */ + $value = apply_filters( "elementor_pro/forms/sanitize/{$field_type}", $value, $field ); + } + + return $value; + } + + public function replace_setting_shortcodes( $setting, $urlencode = false ) { + // Shortcode can be `[field id="fds21fd"]` or `[field title="Email" id="fds21fd"]`, multiple shortcodes are allowed + return preg_replace_callback( '/(\[field[^]]*id="(\w+)"[^]]*\])/', function( $matches ) use ( $urlencode ) { + $value = ''; + + if ( isset( $this->fields[ $matches[2] ] ) ) { + $value = $this->fields[ $matches[2] ]['value']; + } + + if ( $urlencode ) { + $value = urlencode( $value ); + } + return $value; + }, $setting ); + } + + public function add_file( $id, $index, $filename ) { + if ( ! isset( $this->files[ $id ] ) || ! is_array( $this->files[ $id ] ) ) { + $this->files[ $id ] = [ + 'url' => [], + 'path' => [], + ]; + } + + $attachment_type = $this->fields[ $id ]['attachment_type']; + $this->files[ $id ]['url'][ $index ] = Upload::MODE_ATTACH === $attachment_type ? 'attached' : $filename['url']; + $this->files[ $id ]['path'][ $index ] = $filename['path']; + } + + public function has_field_type( $type ) { + foreach ( $this->fields as $id => $field ) { + if ( $type === $field['field_type'] ) { + return true; + } + } + + return false; + } + + public function __construct( $sent_data, $form ) { + $this->form_type = $form['widgetType']; + $this->form_settings = $form['settings']; + $this->sent_data = stripslashes_deep( $sent_data ); + + $this->set_fields(); + $this->set_meta(); + } +} diff --git a/modules/forms/classes/getresponse-handler.php b/modules/forms/classes/getresponse-handler.php new file mode 100644 index 0000000..59806ab --- /dev/null +++ b/modules/forms/classes/getresponse-handler.php @@ -0,0 +1,146 @@ +init_rest_client( $api_key ); + + if ( ! $this->is_valid_api_key() ) { + throw new \Exception( 'Invalid API key.' ); + } + } + + private function init_rest_client( $api_key ) { + $this->api_key = $api_key; + $this->rest_client = new Rest_Client( 'https://api.getresponse.com/v3/' ); + $this->rest_client->add_headers( [ + 'X-Auth-Token' => 'api-key ' . $api_key, + 'Content-Type' => 'application/json', + ] ); + } + + /** + * validate api key + * + * @return bool + * @throws \Exception + */ + private function is_valid_api_key() { + $lists = $this->get_lists(); + if ( ! empty( $lists ) ) { + return true; + } + $this->api_key = ''; + + return false; + } + + /** + * get GetResponse lists associated with API key + * @return array + * @throws \Exception + */ + public function get_lists() { + $results = $this->rest_client->get( 'campaigns' ); + + $lists = [ + '' => esc_html__( 'Select...', 'elementor-pro' ), + ]; + + if ( ! empty( $results['body'] ) ) { + foreach ( $results['body'] as $index => $list ) { + if ( ! is_array( $list ) ) { + continue; + } + $lists[ $list['campaignId'] ] = $list['name']; + } + } + + $return_array = [ + 'lists' => $lists, + ]; + + return $return_array; + } + + public function get_fields() { + $results = $this->rest_client->get( 'custom-fields' ); + + $fields = [ + [ + 'remote_label' => esc_html__( 'Email', 'elementor-pro' ), + 'remote_type' => 'email', + 'remote_id' => 'email', + 'remote_required' => true, + ], + [ + 'remote_label' => esc_html__( 'Name', 'elementor-pro' ), + 'remote_type' => 'text', + 'remote_id' => 'name', + 'remote_required' => false, + ], + ]; + + if ( ! empty( $results['body'] ) ) { + foreach ( $results['body'] as $field ) { + $fields[] = [ + 'remote_label' => $field['name'], + 'remote_type' => $this->normalize_type( $field['type'] ), + 'remote_id' => $field['customFieldId'], + 'remote_required' => false, + ]; + } + } + + $return_array = [ + 'fields' => $fields, + ]; + + return $return_array; + } + + private function normalize_type( $type ) { + static $types = [ + 'text' => 'text', + 'number' => 'number', + 'address' => 'text', + 'phone' => 'text', + 'date' => 'text', + 'url' => 'url', + 'imageurl' => 'url', + 'radio' => 'radio', + 'dropdown' => 'select', + 'single_select' => 'select', + 'textarea' => 'text', + 'birthday' => 'text', + 'zip' => 'text', + 'country' => 'text', + 'gender' => 'text', + ]; + + return $types[ $type ]; + } + + /** + * create contact at GetResponse via api + * + * @param array $subscriber_data + * + * @return array|mixed + * @throws \Exception + */ + public function create_subscriber( $subscriber_data = [] ) { + return $this->rest_client->request( 'POST', 'contacts', wp_json_encode( $subscriber_data ), 202 ); + } +} diff --git a/modules/forms/classes/honeypot-handler.php b/modules/forms/classes/honeypot-handler.php new file mode 100644 index 0000000..ca5f93d --- /dev/null +++ b/modules/forms/classes/honeypot-handler.php @@ -0,0 +1,100 @@ +set_render_attribute( 'field-group' . $item_index, 'class', 'elementor-field-type-text' ); + $item['field_label'] = false; + } + + return $item; + } + + /** + * @param string $item + * @param integer $item_index + * @param Widget_Base $widget + */ + public function render_field( $item, $item_index, $widget ) { + $widget->set_render_attribute( 'input' . $item_index, 'type', 'text' ); + $widget->add_render_attribute( 'input' . $item_index, 'style', 'display:none !important;' ); + + echo 'print_render_attribute_string( 'input' . $item_index ); + echo '>'; + } + + /** + * @param Form_Record $record + * @param Ajax_Handler $ajax_handler + */ + public function validation( $record, $ajax_handler ) { + $fields = $record->get_field( [ + 'type' => 'honeypot', + ] ); + + if ( empty( $fields ) ) { + return; + } + + foreach ( $fields as $field ) { + if ( ! empty( $field['value'] ) ) { + $ajax_handler->add_error( $field['id'], esc_html__( 'Invalid Form.', 'elementor-pro' ) ); + } else { + // If success - remove the field form list (don't send it in emails and etc ) + $record->remove_field( $field['id'] ); + } + } + + } + + public function update_controls( Widget_Base $widget ) { + $elementor = Plugin::elementor(); + + $control_data = $elementor->controls_manager->get_control_from_stack( $widget->get_unique_name(), 'form_fields' ); + + if ( is_wp_error( $control_data ) ) { + return; + } + + foreach ( $control_data['fields'] as $index => $field ) { + if ( 'required' === $field['name'] || 'width' === $field['name'] ) { + $control_data['fields'][ $index ]['conditions']['terms'][] = [ + 'name' => 'field_type', + 'operator' => '!in', + 'value' => [ + 'honeypot', + ], + ]; + } + } + + $widget->update_control( 'form_fields', $control_data ); + } + + public function __construct() { + add_filter( 'elementor_pro/forms/field_types', [ $this, 'add_field_type' ] ); + add_action( 'elementor_pro/forms/render/item', [ $this, 'hide_label' ], 10, 3 ); + add_action( 'elementor_pro/forms/render_field/honeypot', [ $this, 'render_field' ], 10, 3 ); + add_action( 'elementor_pro/forms/validation', [ $this, 'validation' ], 10, 2 ); + add_action( 'elementor/element/form/section_form_fields/before_section_end', [ $this, 'update_controls' ] ); + } +} diff --git a/modules/forms/classes/integration-base.php b/modules/forms/classes/integration-base.php new file mode 100644 index 0000000..ada843f --- /dev/null +++ b/modules/forms/classes/integration-base.php @@ -0,0 +1,76 @@ +', Settings::get_url() . '#tab-integrations' ), + '' + ); + $alert_type = 'warning'; + } else { + $content = sprintf( + /* translators: 1: Integration label, 2: Link opening tag, 3: Link closing tag. */ + esc_html__( 'You are using %1$s set in the %2$sIntegrations Settings%3$s.', 'elementor-pro' ), + $label, + sprintf( '', Settings::get_url() . '#tab-integrations' ), + '' + ); + $alert_type = 'info'; + } + + /* translators: %s: Integration label. */ + $content .= ' ' . sprintf( esc_html__( 'You can also set a different %s by choosing "Custom".', 'elementor-pro' ), $label ); + + $widget->add_control( + $id . '_api_key_msg', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => $alert_type, + 'content' => $content, + 'condition' => $condition, + ] + ); + } + + protected function get_fields_map_control_options() { + return []; + } + + final protected function register_fields_map_control( Form $form ) { + $repeater = new Repeater(); + + $repeater->add_control( 'remote_id', [ 'type' => Controls_Manager::HIDDEN ] ); + + $repeater->add_control( 'local_id', [ 'type' => Controls_Manager::SELECT ] ); + + $fields_map_control_options = [ + 'label' => esc_html__( 'Field Mapping', 'elementor-pro' ), + 'type' => Fields_Map::CONTROL_TYPE, + 'separator' => 'before', + 'fields' => $repeater->get_controls(), + ]; + + $fields_map_control_options = array_merge( $fields_map_control_options, $this->get_fields_map_control_options() ); + + $form->add_control( $this->get_name() . '_fields_map', $fields_map_control_options ); + } +} diff --git a/modules/forms/classes/mailchimp-handler.php b/modules/forms/classes/mailchimp-handler.php new file mode 100644 index 0000000..368fd72 --- /dev/null +++ b/modules/forms/classes/mailchimp-handler.php @@ -0,0 +1,179 @@ +api_key = $api_key; + $this->api_base_url = 'https://' . $key_parts[1] . '.api.mailchimp.com/3.0/'; + $this->api_request_args = [ + 'headers' => [ + 'Authorization' => 'Basic ' . base64_encode( 'user:' . $this->api_key ), + ], + ]; + } + + public function query( $end_point ) { + $response = wp_remote_get( $this->api_base_url . $end_point, $this->api_request_args ); + + if ( is_wp_error( $response ) || 200 != (int) wp_remote_retrieve_response_code( $response ) ) { + throw new \Exception( 'Mailchimp error.' ); + } + + $body = json_decode( wp_remote_retrieve_body( $response ), true ); + + if ( ! is_array( $body ) ) { + throw new \Exception( 'Mailchimp error.' ); + } + + return $body; + } + + public function post( $end_point, $data, $request_args = [] ) { + $this->api_request_args += $request_args; + $this->api_request_args['headers']['Content-Type'] = 'application/json; charset=utf-8'; + $this->api_request_args['body'] = wp_json_encode( $data ); + $response = wp_remote_post( $this->api_base_url . $end_point, $this->api_request_args ); + + if ( is_wp_error( $response ) ) { + throw new \Exception( 'Mailchimp error.' ); + } + + $body = json_decode( wp_remote_retrieve_body( $response ), true ); + $code = (int) wp_remote_retrieve_response_code( $response ); + + // Throw an exception if there is no response body. + // NOTE: HTTP 204 doesn't have a body. + if ( 204 !== $code && ! is_array( $body ) ) { + throw new \Exception( 'Mailchimp error.' ); + } + + return [ + 'code' => $code, + 'body' => $body, + ]; + } + + public function get_lists() { + $results = $this->query( 'lists?count=999' ); + + $lists = [ + '' => 'Select...', + ]; + + if ( ! empty( $results['lists'] ) ) { + foreach ( $results['lists'] as $list ) { + $lists[ $list['id'] ] = $list['name']; + } + } + + $return_array = [ + 'lists' => $lists, + ]; + + return $return_array; + } + + public function get_groups( $list_id ) { + $results = $this->query( 'lists/' . $list_id . '/interest-categories?count=999' ); + $groups = []; + + if ( ! empty( $results['categories'] ) ) { + foreach ( $results['categories'] as $category ) { + $interests_results = $this->query( 'lists/' . $list_id . '/interest-categories/' . $category['id'] . '/interests?count=999' ); + + foreach ( $interests_results['interests'] as $interest ) { + $groups[ $interest['id'] ] = $category['title'] . ' - ' . $interest['name']; + } + } + } + + $return_array = [ + 'groups' => $groups, + ]; + + return $return_array; + } + + public function get_fields( $list_id ) { + $results = $this->query( 'lists/' . $list_id . '/merge-fields?count=999' ); + + $fields = [ + [ + 'remote_label' => 'Email', + 'remote_type' => 'email', + 'remote_id' => 'email', + 'remote_required' => true, + ], + ]; + + if ( ! empty( $results['merge_fields'] ) ) { + foreach ( $results['merge_fields'] as $field ) { + $fields[] = [ + 'remote_label' => $field['name'], + 'remote_type' => $this->normalize_type( $field['type'] ), + 'remote_id' => $field['tag'], + 'remote_required' => $field['required'], + ]; + } + } + + $return_array = [ + 'fields' => $fields, + ]; + + return $return_array; + } + + public function get_list_details( $list_id ) { + $groups = $this->get_groups( $list_id ); + $fields = $this->get_fields( $list_id ); + + return [ + 'list_details' => $groups + $fields, + ]; + } + + private function normalize_type( $type ) { + static $types = [ + 'text' => 'text', + 'number' => 'number', + 'address' => 'text', + 'phone' => 'text', + 'date' => 'text', + 'url' => 'url', + 'imageurl' => 'url', + 'radio' => 'radio', + 'dropdown' => 'select', + 'birthday' => 'text', + 'zip' => 'text', + ]; + + return $types[ $type ]; + } +} diff --git a/modules/forms/classes/mailerlite-handler.php b/modules/forms/classes/mailerlite-handler.php new file mode 100644 index 0000000..8992193 --- /dev/null +++ b/modules/forms/classes/mailerlite-handler.php @@ -0,0 +1,123 @@ +init_rest_client( $api_key ); + if ( ! $this->is_valid_api_key() ) { + throw new \Exception( 'Invalid API key.' ); + } + } + + private function init_rest_client( $api_key ) { + $this->api_key = $api_key; + $this->rest_client = new Rest_Client( 'https://api.mailerlite.com/api/v2/' ); + $this->rest_client->add_headers( [ + 'X-MailerLite-ApiKey' => $api_key, + 'Content-Type' => 'application/json', + ] ); + } + + /** + * validate api key + * + * @return bool + * @throws \Exception + */ + private function is_valid_api_key() { + $groups = $this->rest_client->get( 'groups' ); + if ( ! empty( $groups ) ) { + return true; + } + $this->api_key = ''; + + return false; + } + + /** + * get MailerLite groups associated with API key + * @return array + * @throws \Exception + */ + public function get_groups() { + $results = $this->rest_client->get( 'groups' ); + + $groups = [ + '' => esc_html__( 'Select...', 'elementor-pro' ), + ]; + + if ( 200 === $results['code'] ) { + foreach ( $results['body'] as $index => $group ) { + $groups[ $group['id'] ] = $group['name']; + } + } + + $return_array = [ + 'groups' => $groups, + 'fields' => $this->get_fields(), + ]; + + return $return_array; + } + + /** + * get MailerLite fields associated with API key + * @return array + * @throws \Exception + */ + public function get_fields() { + $results = $this->rest_client->get( 'fields' ); + + $fields = []; + if ( ! empty( $results['body'] ) ) { + foreach ( $results['body'] as $index => $field ) { + if ( ! is_array( $field ) || empty( $field['date_updated'] ) ) { + continue; + } + $fields[] = [ + 'remote_label' => $field['title'], + 'remote_type' => strtolower( $field['type'] ), + 'remote_id' => $field['key'], + 'remote_required' => false, + ]; + } + } + + return $fields; + } + + /** + * create subscriber at drip via api + * + * @param string $group + * @param array $subscriber_data + * + * @return array|mixed + * @throws \Exception + */ + public function create_subscriber( $group = '', $subscriber_data = [] ) { + $end_point = sprintf( 'groups/%s/subscribers', $group ); + + return $this->rest_client->post( $end_point, $subscriber_data ); + } +} diff --git a/modules/forms/classes/recaptcha-handler.php b/modules/forms/classes/recaptcha-handler.php new file mode 100644 index 0000000..4a78cee --- /dev/null +++ b/modules/forms/classes/recaptcha-handler.php @@ -0,0 +1,301 @@ + Elementor > Settings > Integrations > reCAPTCHA.', 'elementor-pro' ); + } + + public function register_admin_fields( Settings $settings ) { + $settings->add_section( Settings::TAB_INTEGRATIONS, static::get_recaptcha_name(), [ + 'label' => esc_html__( 'reCAPTCHA', 'elementor-pro' ), + 'callback' => function () { + echo sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( '%1$sreCAPTCHA%2$s is a free service by Google that protects your website from spam and abuse. It does this while letting your valid users pass through with ease.', 'elementor-pro' ), + '', + '' + ); + }, + 'fields' => [ + 'pro_recaptcha_site_key' => [ + 'label' => esc_html__( 'Site Key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + ], + ], + 'pro_recaptcha_secret_key' => [ + 'label' => esc_html__( 'Secret Key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + ], + ], + ], + ] ); + } + + public function localize_settings( $settings ) { + $settings = array_replace_recursive( $settings, [ + 'forms' => [ + static::get_recaptcha_name() => [ + 'enabled' => static::is_enabled(), + 'type' => static::get_recaptcha_type(), + 'site_key' => static::get_site_key(), + 'setup_message' => static::get_setup_message(), + ], + ], + ] ); + + return $settings; + } + + protected static function get_script_render_param() { + return 'explicit'; + } + + protected static function get_script_name() { + return 'elementor-' . static::get_recaptcha_name() . '-api'; + } + + public function register_scripts() { + $script_name = static::get_script_name(); + $src = 'https://www.google.com/recaptcha/api.js?render=explicit'; + wp_register_script( $script_name, $src, [], ELEMENTOR_PRO_VERSION, true ); + } + + public function enqueue_scripts() { + if ( Plugin::elementor()->preview->is_preview_mode() ) { + return; + } + $script_name = static::get_script_name(); + wp_enqueue_script( $script_name ); + } + + /** + * @param Form_Record $record + * @param Ajax_Handler $ajax_handler + */ + public function validation( $record, $ajax_handler ) { + $fields = $record->get_field( [ + 'type' => static::get_recaptcha_name(), + ] ); + + if ( empty( $fields ) ) { + return; + } + + $field = current( $fields ); + + // PHPCS - response protected by recaptcha secret + $recaptcha_response = Utils::_unstable_get_super_global_value( $_POST, 'g-recaptcha-response' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + + if ( empty( $recaptcha_response ) ) { + $ajax_handler->add_error( $field['id'], esc_html__( 'The Captcha field cannot be blank. Please enter a value.', 'elementor-pro' ) ); + + return; + } + + $recaptcha_errors = [ + 'missing-input-secret' => esc_html__( 'The secret parameter is missing.', 'elementor-pro' ), + 'invalid-input-secret' => esc_html__( 'The secret parameter is invalid or malformed.', 'elementor-pro' ), + 'missing-input-response' => esc_html__( 'The response parameter is missing.', 'elementor-pro' ), + 'invalid-input-response' => esc_html__( 'The response parameter is invalid or malformed.', 'elementor-pro' ), + ]; + + $recaptcha_secret = static::get_secret_key(); + $client_ip = Utils::get_client_ip(); + + $request = [ + 'body' => [ + 'secret' => $recaptcha_secret, + 'response' => $recaptcha_response, + 'remoteip' => $client_ip, + ], + ]; + + $response = wp_remote_post( 'https://www.google.com/recaptcha/api/siteverify', $request ); + + $response_code = wp_remote_retrieve_response_code( $response ); + + if ( 200 !== (int) $response_code ) { + /* translators: %d: Response code. */ + $ajax_handler->add_error( $field['id'], sprintf( esc_html__( 'Can not connect to the reCAPTCHA server (%d).', 'elementor-pro' ), $response_code ) ); + + return; + } + + $body = wp_remote_retrieve_body( $response ); + + $result = json_decode( $body, true ); + + if ( ! $this->validate_result( $result, $field ) ) { + $message = esc_html__( 'Invalid form, reCAPTCHA validation failed.', 'elementor-pro' ); + + if ( isset( $result['error-codes'] ) ) { + $result_errors = array_flip( $result['error-codes'] ); + + foreach ( $recaptcha_errors as $error_key => $error_desc ) { + if ( isset( $result_errors[ $error_key ] ) ) { + $message = $recaptcha_errors[ $error_key ]; + break; + } + } + } + + $this->add_error( $ajax_handler, $field, $message ); + + } + + // If success - remove the field form list (don't send it in emails and etc ) + $record->remove_field( $field['id'] ); + + } + + /** + * @param Ajax_Handler $ajax_handler + * @param $field + * @param $message + */ + protected function add_error( $ajax_handler, $field, $message ) { + $ajax_handler->add_error( $field['id'], $message ); + } + + protected function validate_result( $result, $field ) { + if ( ! $result['success'] ) { + return false; + } + + return true; + } + + /** + * @param $item + * @param $item_index + * @param $widget Widget_Base + */ + public function render_field( $item, $item_index, $widget ) { + $recaptcha_html = '
    '; + + $recaptcha_name = static::get_recaptcha_name(); + + if ( static::is_enabled() ) { + $this->enqueue_scripts(); + $this->add_render_attributes( $item, $item_index, $widget ); + $recaptcha_html .= '
    get_render_attribute_string( $recaptcha_name . $item_index ) . '>
    '; + } elseif ( current_user_can( 'manage_options' ) ) { + $recaptcha_html .= '
    '; + $recaptcha_html .= static::get_setup_message(); + $recaptcha_html .= '
    '; + } + + $recaptcha_html .= '
    '; + + // PHPCS - It's all escaped + echo $recaptcha_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + /** + * @param $item + * @param $item_index + * @param $widget Widget_Base + */ + protected function add_render_attributes( $item, $item_index, $widget ) { + $recaptcha_name = static::get_recaptcha_name(); + + $widget->add_render_attribute( [ + $recaptcha_name . $item_index => [ + 'class' => 'elementor-g-recaptcha', + 'data-sitekey' => static::get_site_key(), + 'data-type' => static::get_recaptcha_type(), + ], + ] ); + + $this->add_version_specific_render_attributes( $item, $item_index, $widget ); + } + + /** + * @param $item + * @param $item_index + * @param $widget Widget_Base + */ + protected function add_version_specific_render_attributes( $item, $item_index, $widget ) { + $recaptcha_name = static::get_recaptcha_name(); + $widget->add_render_attribute( $recaptcha_name . $item_index, [ + 'data-theme' => $item['recaptcha_style'], + 'data-size' => $item['recaptcha_size'], + ] ); + } + + public function add_field_type( $field_types ) { + $field_types['recaptcha'] = esc_html__( 'reCAPTCHA', 'elementor-pro' ); + + return $field_types; + } + + public function filter_field_item( $item ) { + if ( static::get_recaptcha_name() === $item['field_type'] ) { + $item['field_label'] = false; + } + + return $item; + } + + public function __construct() { + $this->register_scripts(); + + add_filter( 'elementor_pro/forms/field_types', [ $this, 'add_field_type' ] ); + add_action( 'elementor_pro/forms/render_field/' . static::get_recaptcha_name(), [ $this, 'render_field' ], 10, 3 ); + add_filter( 'elementor_pro/forms/render/item', [ $this, 'filter_field_item' ] ); + add_filter( 'elementor_pro/editor/localize_settings', [ $this, 'localize_settings' ] ); + + if ( static::is_enabled() ) { + add_action( 'elementor_pro/forms/validation', [ $this, 'validation' ], 10, 2 ); + add_action( 'elementor/preview/enqueue_scripts', [ $this, 'enqueue_scripts' ] ); + } + + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ] ); + } + } +} diff --git a/modules/forms/classes/recaptcha-v3-handler.php b/modules/forms/classes/recaptcha-v3-handler.php new file mode 100644 index 0000000..c7d3680 --- /dev/null +++ b/modules/forms/classes/recaptcha-v3-handler.php @@ -0,0 +1,152 @@ + $threshold || 1 < $threshold ) { + return self::V3_DEFAULT_THRESHOLD; + } + return $threshold; + } + + public static function is_enabled() { + return static::get_site_key() && static::get_secret_key(); + } + + public static function get_setup_message() { + return esc_html__( 'To use reCAPTCHA V3, you need to add the API Key and complete the setup process in Dashboard > Elementor > Settings > Integrations > reCAPTCHA V3.', 'elementor-pro' ); + } + + public function register_admin_fields( Settings $settings ) { + $settings->add_section( Settings::TAB_INTEGRATIONS, 'recaptcha_v3', [ + 'label' => esc_html__( 'reCAPTCHA V3', 'elementor-pro' ), + 'callback' => function() { + echo sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( '%1$sreCAPTCHA V3%2$s is a free service by Google that protects your website from spam and abuse. It does this while letting your valid users pass through with ease.', 'elementor-pro' ), + '', + '' + ); + }, + 'fields' => [ + 'pro_recaptcha_v3_site_key' => [ + 'label' => esc_html__( 'Site Key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + ], + ], + 'pro_recaptcha_v3_secret_key' => [ + 'label' => esc_html__( 'Secret Key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + ], + ], + 'pro_recaptcha_v3_threshold' => [ + 'label' => esc_html__( 'Score Threshold', 'elementor-pro' ), + 'field_args' => [ + 'attributes' => [ + 'min' => 0, + 'max' => 1, + 'placeholder' => '0.5', + 'step' => '0.1', + ], + 'std' => 0.5, + 'type' => 'number', + 'desc' => esc_html__( 'Score threshold should be a value between 0 and 1, default: 0.5', 'elementor-pro' ), + ], + ], + ], + ] ); + } + + /** + * @param $item + * @param $item_index + * @param $widget Widget_Base + */ + protected function add_version_specific_render_attributes( $item, $item_index, $widget ) { + $recaptcha_name = static::get_recaptcha_name(); + $widget->add_render_attribute( $recaptcha_name . $item_index, [ + 'data-action' => self::V3_DEFAULT_ACTION, + 'data-badge' => $item['recaptcha_badge'], + 'data-size' => 'invisible', + ] ); + } + + /** + * @param Ajax_Handler $ajax_handler + * @param $field + * @param $message + */ + protected function add_error( $ajax_handler, $field, $message ) { + parent::add_error( $ajax_handler, $field, $message ); + $ajax_handler->add_error_message( esc_html__( 'reCAPTCHA V3 validation failed, suspected as abusive usage', 'elementor-pro' ) ); + } + + protected function validate_result( $result, $field ) { + $action = self::V3_DEFAULT_ACTION; + $action_ok = ! isset( $result['action'] ) ? true : $action === $result['action']; + return $action_ok && ( $result['score'] > self::get_recaptcha_threshold() ); + } + + public function add_field_type( $field_types ) { + $field_types['recaptcha_v3'] = esc_html__( 'reCAPTCHA V3', 'elementor-pro' ); + + return $field_types; + } + + /** + * @param $item + * @param $item_index + * @param Widget_Base $widget + * + * @return $item + */ + public function filter_recaptcha_item( $item, $item_index, $widget ) { + $widget->add_render_attribute( 'field-group' . $item_index, 'class', [ + self::get_recaptcha_name() . '-' . $item['recaptcha_badge'], + ] ); + + return $item; + } + + public function __construct() { + parent::__construct(); + add_filter( 'elementor_pro/forms/render/item/' . self::get_recaptcha_name(), [ $this, 'filter_recaptcha_item' ], 10, 3 ); + } +} diff --git a/modules/forms/classes/rest-client.php b/modules/forms/classes/rest-client.php new file mode 100644 index 0000000..937f570 --- /dev/null +++ b/modules/forms/classes/rest-client.php @@ -0,0 +1,170 @@ +api_base_url = $rest_base_url; + //setup defaults + $this->set_request_arg( 'timeout', 30 ) + ->set_request_arg( 'sslverify', false ) + ->add_headers( 'User-Agent', $this->user_agent ); + + /** + * Initiate Elementor form REST API client. + * + * Fires when Elementor forms are initiated on REST API client. + * + * @since 2.4.0 + * + * @param Rest_Client $this An instance of form REST API client. + */ + do_action( 'elementor-pro/forms/rest_client/init', $this ); + + return $this; + } + + /** + * Set REST API base url. + * + * @param string $url + */ + public function set_base_url( $url ) { + $this->api_base_url = $url; + } + + /** + * Get REST API base url. + * + * @return string + */ + public function get_base_url() { + return $this->api_base_url; + } + + /** + * Add headers to REST API. + * + * @param $key Header key. + * @param $value Optional. Header value. Default is null. + * + * @return $this An instance of REST API client. + */ + public function add_headers( $key, $value = null ) { + if ( ! is_array( $key ) ) { + $this->headers[ $key ] = $value; + + return $this; + } + foreach ( $key as $header => $header_value ) { + $this->headers[ $header ] = $header_value; + } + + return $this; + } + + /** + * Set REST API request arguments. + * + * @param string $name Optional. Request argument name. Default is ''. + * @param null $value Optional. Request argument value. Default is null. + * + * @return $this An instance of REST API client. + */ + public function set_request_arg( $name = '', $value = null ) { + $this->request_args[ $name ] = $value; + + return $this; + } + + /** + * @uses request + * + * @param string $endpoint Optional. Default is ''. + * @param null $data Optional. Default is null. + * + * @return array|mixed + * @throws \Exception + */ + public function post( $endpoint = '', $data = null ) { + $request_body = wp_json_encode( $data ); + + return $this->request( 'POST', $endpoint, $request_body ); + } + + /** + * @uses request + * + * @param string $endpoint Optional. Default is ''. + * @param null $data Optional. Default is null. + * + * @return array|mixed + * @throws \Exception + */ + public function get( $endpoint = '', $data = null ) { + return $this->request( 'GET', $endpoint, $data ); + } + + /** + * @param string $method Optional. Default is 'GET'. + * @param string $endpoint Optional. Default is ''. + * @param null $request_body Optional. Default is null. + * @param int $valid_response_code Optional. Default is '200'. + * + * @return array + * @throws \Exception + */ + public function request( $method = 'GET', $endpoint = '', $request_body = null, $valid_response_code = 200 ) { + $request_url = $this->api_base_url . $endpoint; + $base_args = [ + 'method' => $method, + 'headers' => $this->headers, + ]; + $api_request_args = array_merge( $base_args, $this->request_args ); + if ( null !== $request_body ) { + if ( in_array( $method, [ 'POST', 'PUT' ] ) ) { + $api_request_args['body'] = $request_body; + } else { + $request_url = add_query_arg( $request_body, $request_url ); + } + } + + $cache_key = md5( $method . $endpoint . json_encode( $api_request_args ) ); + if ( isset( $this->request_cache[ $cache_key ] ) && isset( $this->request_cache[ $cache_key ]['parsed'] ) ) { + $this->request_cache[ $cache_key ]['parsed']; + } + + $response = wp_remote_request( $request_url, $api_request_args ); + $response_code = (int) wp_remote_retrieve_response_code( $response ); + + $this->request_cache[ $cache_key ]['raw'] = $response; + + if ( is_wp_error( $response ) || $valid_response_code !== $response_code ) { + throw new \Exception( "Rest Client Error: response code {$response_code}." ); + } + + $response_body = json_decode( wp_remote_retrieve_body( $response ), true ); + + if ( ! is_array( $response_body ) ) { + throw new \Exception( 'Rest Client Error: unexpected response type.' ); + } + + $return = [ + 'code' => $response_code, + 'body' => $response_body, + ]; + $this->request_cache[ $cache_key ]['parsed'] = $return; + + return $return; + } +} diff --git a/modules/forms/controls/fields-map.php b/modules/forms/controls/fields-map.php new file mode 100644 index 0000000..677fb66 --- /dev/null +++ b/modules/forms/controls/fields-map.php @@ -0,0 +1,45 @@ + 'none', + 'fields' => [ + [ + 'name' => 'remote_id', + 'type' => Controls_Manager::HIDDEN, + ], + [ + 'name' => 'local_id', + 'type' => Controls_Manager::SELECT, + ], + ], + ] ); + } +} diff --git a/modules/forms/controls/fields-repeater.php b/modules/forms/controls/fields-repeater.php new file mode 100644 index 0000000..d377bd5 --- /dev/null +++ b/modules/forms/controls/fields-repeater.php @@ -0,0 +1,18 @@ +controls_manager->get_control_from_stack( $widget->get_unique_name(), 'form_fields' ); + + if ( is_wp_error( $control_data ) ) { + return; + } + + $field_controls = [ + 'acceptance_text' => [ + 'name' => 'acceptance_text', + 'label' => esc_html__( 'Acceptance Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + 'checked_by_default' => [ + 'name' => 'checked_by_default', + 'label' => esc_html__( 'Checked by Default', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + ]; + + $control_data['fields'] = $this->inject_field_controls( $control_data['fields'], $field_controls ); + $widget->update_control( 'form_fields', $control_data ); + } + + public function render( $item, $item_index, $form ) { + $label = ''; + $form->add_render_attribute( 'input' . $item_index, 'class', 'elementor-acceptance-field' ); + $form->add_render_attribute( 'input' . $item_index, 'type', 'checkbox', true ); + + if ( ! empty( $item['acceptance_text'] ) ) { + $label = ''; + } + + if ( ! empty( $item['checked_by_default'] ) ) { + $form->add_render_attribute( 'input' . $item_index, 'checked', 'checked' ); + } + + ?> +
    + + print_render_attribute_string( 'input' . $item_index ); ?>> + + +
    + add_render_attribute( 'input' . $item_index, 'class', 'elementor-field-textual elementor-date-field' ); + $form->add_render_attribute( 'input' . $item_index, 'pattern', '[0-9]{4}-[0-9]{2}-[0-9]{2}' ); + if ( isset( $item['use_native_date'] ) && 'yes' === $item['use_native_date'] ) { + $form->add_render_attribute( 'input' . $item_index, 'class', 'elementor-use-native' ); + } + + if ( ! empty( $item['min_date'] ) ) { + $form->add_render_attribute( 'input' . $item_index, 'min', esc_attr( $item['min_date'] ) ); + } + + if ( ! empty( $item['max_date'] ) ) { + $form->add_render_attribute( 'input' . $item_index, 'max', esc_attr( $item['max_date'] ) ); + } + ?> + + print_render_attribute_string( 'input' . $item_index ); ?>> + controls_manager->get_control_from_stack( $widget->get_unique_name(), 'form_fields' ); + + if ( is_wp_error( $control_data ) ) { + return; + } + + $field_controls = [ + 'min_date' => [ + 'name' => 'min_date', + 'label' => esc_html__( 'Min. Date', 'elementor-pro' ), + 'type' => Controls_Manager::DATE_TIME, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'label_block' => false, + 'picker_options' => [ + 'enableTime' => false, + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + 'max_date' => [ + 'name' => 'max_date', + 'label' => esc_html__( 'Max. Date', 'elementor-pro' ), + 'type' => Controls_Manager::DATE_TIME, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'label_block' => false, + 'picker_options' => [ + 'enableTime' => false, + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + 'use_native_date' => [ + 'name' => 'use_native_date', + 'label' => esc_html__( 'Native HTML5', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + ]; + + foreach ( $control_data['fields'] as $index => $field ) { + if ( 'placeholder' !== $field['name'] ) { + continue; + } + foreach ( $field['conditions']['terms'] as $condition_index => $terms ) { + if ( ! isset( $terms['name'] ) || 'field_type' !== $terms['name'] || ! isset( $terms['operator'] ) || 'in' !== $terms['operator'] ) { + continue; + } + $control_data['fields'][ $index ]['conditions']['terms'][ $condition_index ]['value'][] = $this->get_type(); + break; + } + break; + } + + $control_data['fields'] = $this->inject_field_controls( $control_data['fields'], $field_controls ); + $widget->update_control( 'form_fields', $control_data ); + } +} diff --git a/modules/forms/fields/field-base.php b/modules/forms/fields/field-base.php new file mode 100644 index 0000000..ff16b32 --- /dev/null +++ b/modules/forms/fields/field-base.php @@ -0,0 +1,96 @@ +get_type(); + } + + abstract public function render( $item, $item_index, $form ); + + public function validation( $field, Classes\Form_Record $record, Classes\Ajax_Handler $ajax_handler ) {} + + public function process_field( $field, Classes\Form_Record $record, Classes\Ajax_Handler $ajax_handler ) {} + + public function add_assets_depends( $form ) { + foreach ( $this->depended_scripts as $script ) { + $form->add_script_depends( $script ); + } + + foreach ( $this->depended_styles as $style ) { + $form->add_style_depends( $style ); + } + } + + public function add_preview_depends() { + foreach ( $this->depended_scripts as $script ) { + wp_enqueue_script( $script ); + } + + foreach ( $this->depended_styles as $style ) { + wp_enqueue_style( $style ); + } + } + + public function add_field_type( $field_types ) { + if ( ! in_array( $this->get_type(), $field_types ) ) { + $field_types[ $this->get_type() ] = $this->get_name(); + } + + return $field_types; + } + + public function field_render( $item, $item_index, $form ) { + $this->add_assets_depends( $form ); + $this->render( $item, $item_index, $form ); + } + + public function sanitize_field( $value, $field ) { + return sanitize_text_field( $value ); + } + + public function inject_field_controls( $array, $controls_to_inject ) { + $keys = array_keys( $array ); + $key_index = array_search( 'required', $keys ) + 1; + + return array_merge( array_slice( $array, 0, $key_index, true ), + $controls_to_inject, + array_slice( $array, $key_index, null, true ) + ); + } + + public function __construct() { + $field_type = $this->get_type(); + add_action( "elementor_pro/forms/render_field/{$field_type}", [ $this, 'field_render' ], 10, 3 ); + add_action( "elementor_pro/forms/validation/{$field_type}", [ $this, 'validation' ], 10, 3 ); + add_action( "elementor_pro/forms/process/{$field_type}", [ $this, 'process_field' ], 10, 3 ); + add_filter( 'elementor_pro/forms/field_types', [ $this, 'add_field_type' ] ); + add_filter( "elementor_pro/forms/sanitize/{$field_type}", [ $this, 'sanitize_field' ], 10, 2 ); + add_action( 'elementor/preview/enqueue_scripts', [ $this, 'add_preview_depends' ] ); + if ( method_exists( $this, 'update_controls' ) ) { + add_action( 'elementor/element/form/section_form_fields/before_section_end', [ $this, 'update_controls' ] ); + } + } +} diff --git a/modules/forms/fields/number.php b/modules/forms/fields/number.php new file mode 100644 index 0000000..6aa356d --- /dev/null +++ b/modules/forms/fields/number.php @@ -0,0 +1,95 @@ +add_render_attribute( 'input' . $item_index, 'class', 'elementor-field-textual' ); + + if ( isset( $item['field_min'] ) ) { + $form->add_render_attribute( 'input' . $item_index, 'min', esc_attr( $item['field_min'] ) ); + } + if ( isset( $item['field_max'] ) ) { + $form->add_render_attribute( 'input' . $item_index, 'max', esc_attr( $item['field_max'] ) ); + } + + ?> + print_render_attribute_string( 'input' . $item_index ); ?> > + controls_manager->get_control_from_stack( $widget->get_unique_name(), 'form_fields' ); + + if ( is_wp_error( $control_data ) ) { + return; + } + + $field_controls = [ + 'field_min' => [ + 'name' => 'field_min', + 'label' => esc_html__( 'Min. Value', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + 'field_max' => [ + 'name' => 'field_max', + 'label' => esc_html__( 'Max. Value', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + ]; + + $control_data['fields'] = $this->inject_field_controls( $control_data['fields'], $field_controls ); + $widget->update_control( 'form_fields', $control_data ); + } + + public function validation( $field, Classes\Form_Record $record, Classes\Ajax_Handler $ajax_handler ) { + + if ( ! empty( $field['field_max'] ) && $field['field_max'] < (int) $field['value'] ) { + /* translators: %s: The value of max field. */ + $ajax_handler->add_error( $field['id'], sprintf( esc_html__( 'The field value must be less than or equal to %s.', 'elementor-pro' ), $field['field_max'] ) ); + } + + if ( ! empty( $field['field_min'] ) && $field['field_min'] > (int) $field['value'] ) { + /* translators: %s: The value of min field. */ + $ajax_handler->add_error( $field['id'], sprintf( esc_html__( 'The field value must be greater than or equal to %s.', 'elementor-pro' ), $field['field_min'] ) ); + } + } + + public function sanitize_field( $value, $field ) { + return intval( $value ); + } +} diff --git a/modules/forms/fields/step.php b/modules/forms/fields/step.php new file mode 100644 index 0000000..684dde4 --- /dev/null +++ b/modules/forms/fields/step.php @@ -0,0 +1,133 @@ +experiments->is_feature_active( 'e_font_icon_svg' ) && $item['selected_icon']['value'] ) { + if ( 'svg' === $item['selected_icon']['library'] ) { + $font_icon = Icons_Manager::render_uploaded_svg_icon( $item['selected_icon']['value'] ); + } else { + $font_icon = Icons_Manager::render_font_icon( $item['selected_icon'] ); + } + } + + $form->add_render_attribute( 'step' . $item_index, [ + 'class' => 'e-field-step elementor-hidden', + 'data-label' => $item['field_label'], + 'data-previousButton' => $item['previous_button'], + 'data-nextButton' => $item['next_button'], + 'data-iconUrl' => 'svg' === $item['selected_icon']['library'] && $item['selected_icon']['value'] ? $item['selected_icon']['value']['url'] : '', + 'data-iconLibrary' => 'svg' !== $item['selected_icon']['library'] && $item['selected_icon']['value'] ? $item['selected_icon']['value'] : '', + 'data-icon' => $font_icon, + ] ); + + ?> +
    print_render_attribute_string( 'step' . $item_index ); ?> >
    + + controls_manager->get_control_from_stack( $widget->get_unique_name(), 'form_fields' ); + + if ( is_wp_error( $control_data ) ) { + return; + } + + $field_controls = [ + 'previous_button' => [ + 'name' => 'previous_button', + 'label' => esc_html__( 'Previous Button', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + 'next_button' => [ + 'name' => 'next_button', + 'label' => esc_html__( 'Next Button', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + 'selected_icon' => [ + 'name' => 'selected_icon', + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'description' => esc_html__( 'Visible only if selected step type contains "Icon"', 'elementor-pro' ), + 'default' => [ + 'value' => 'fas fa-star', + 'library' => 'fa-solid', + ], + 'recommended' => [ + 'fa-solid' => [ + 'chevron-down', + 'angle-down', + 'angle-double-down', + 'caret-down', + 'caret-square-down', + ], + 'fa-regular' => [ + 'caret-square-down', + ], + ], + 'skin' => 'inline', + 'label_block' => false, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + ]; + + $control_data['fields'] = $this->inject_field_controls( $control_data['fields'], $field_controls ); + $widget->update_control( 'form_fields', $control_data ); + } +} diff --git a/modules/forms/fields/tel.php b/modules/forms/fields/tel.php new file mode 100644 index 0000000..eab8006 --- /dev/null +++ b/modules/forms/fields/tel.php @@ -0,0 +1,38 @@ +add_render_attribute( 'input' . $item_index, 'class', 'elementor-field-textual' ); + $form->add_render_attribute( 'input' . $item_index, 'pattern', '[0-9()#&+*-=.]+' ); + $form->add_render_attribute( 'input' . $item_index, 'title', esc_html__( 'Only numbers and phone characters (#, -, *, etc) are accepted.', 'elementor-pro' ) ); + ?> + print_render_attribute_string( 'input' . $item_index ); ?>> + + add_error( $field['id'], esc_html__( 'The field accepts only numbers and phone characters (#, -, *, etc).', 'elementor-pro' ) ); + } + } +} diff --git a/modules/forms/fields/time.php b/modules/forms/fields/time.php new file mode 100644 index 0000000..b2ebffa --- /dev/null +++ b/modules/forms/fields/time.php @@ -0,0 +1,91 @@ +controls_manager->get_control_from_stack( $widget->get_unique_name(), 'form_fields' ); + + if ( is_wp_error( $control_data ) ) { + return; + } + + $field_controls = [ + 'use_native_time' => [ + 'name' => 'use_native_time', + 'label' => esc_html__( 'Native HTML5', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + ]; + + foreach ( $control_data['fields'] as $index => $field ) { + if ( 'placeholder' !== $field['name'] ) { + continue; + } + foreach ( $field['conditions']['terms'] as $condition_index => $terms ) { + if ( ! isset( $terms['name'] ) || 'field_type' !== $terms['name'] || ! isset( $terms['operator'] ) || 'in' !== $terms['operator'] ) { + continue; + } + $control_data['fields'][ $index ]['conditions']['terms'][ $condition_index ]['value'][] = $this->get_type(); + break; + } + break; + } + + $control_data['fields'] = $this->inject_field_controls( $control_data['fields'], $field_controls ); + + $widget->update_control( 'form_fields', $control_data ); + } + + public function render( $item, $item_index, $form ) { + $form->add_render_attribute( 'input' . $item_index, 'class', 'elementor-field-textual elementor-time-field' ); + if ( isset( $item['use_native_time'] ) && 'yes' === $item['use_native_time'] ) { + $form->add_render_attribute( 'input' . $item_index, 'class', 'elementor-use-native' ); + } + ?> + print_render_attribute_string( 'input' . $item_index ); ?>> + add_error( $field['id'], esc_html__( 'The field should be in HH:MM format.', 'elementor-pro' ) ); + } + } +} diff --git a/modules/forms/fields/upload.php b/modules/forms/fields/upload.php new file mode 100644 index 0000000..22aa7cf --- /dev/null +++ b/modules/forms/fields/upload.php @@ -0,0 +1,556 @@ +controls_manager->get_control_from_stack( $widget->get_unique_name(), 'form_fields' ); + + if ( is_wp_error( $control_data ) ) { + return; + } + + $field_controls = [ + 'attachment_type' => [ + 'name' => 'attachment_type', + 'label' => esc_html__( 'Send files', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'options' => [ + self::MODE_LINK => esc_html__( 'Email with link', 'elementor-pro' ), + self::MODE_ATTACH => esc_html__( 'Email with attachment', 'elementor-pro' ), + self::MODE_BOTH => esc_html__( 'Email with both', 'elementor-pro' ), + ], + 'default' => self::MODE_LINK, + 'description' => esc_html__( "Uploads you receive via link are stored on your server. However, uploads via attachment won't be saved on your server, and under Submissions", 'elementor-pro' ), + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + 'file_sizes' => [ + 'name' => 'file_sizes', + 'label' => esc_html__( 'Max. File Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'options' => $this->get_upload_file_size_options(), + 'description' => esc_html__( 'If you need to increase max upload size please contact your hosting.', 'elementor-pro' ), + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + 'file_types' => [ + 'name' => 'file_types', + 'label' => esc_html__( 'Allowed File Types', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'description' => esc_html__( 'Enter the allowed file types, separated by a comma (jpg, gif, pdf, etc).', 'elementor-pro' ), + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + 'allow_multiple_upload' => [ + 'name' => 'allow_multiple_upload', + 'label' => esc_html__( 'Multiple Files', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'field_type' => $this->get_type(), + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + 'max_files' => [ + 'name' => 'max_files', + 'label' => esc_html__( 'Max. Files', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'condition' => [ + 'field_type' => $this->get_type(), + 'allow_multiple_upload' => 'yes', + ], + 'tab' => 'content', + 'inner_tab' => 'form_fields_content_tab', + 'tabs_wrapper' => 'form_fields_tabs', + ], + ]; + + $control_data['fields'] = $this->inject_field_controls( $control_data['fields'], $field_controls ); + $widget->update_control( 'form_fields', $control_data ); + } + + /** + * @param $item + * @param $item_index + * @param Form $form + */ + public function render( $item, $item_index, $form ) { + $form->add_render_attribute( 'input' . $item_index, 'class', 'elementor-upload-field' ); + $form->add_render_attribute( 'input' . $item_index, 'type', 'file', true ); + + if ( ! empty( $item['allow_multiple_upload'] ) ) { + $form->add_render_attribute( 'input' . $item_index, 'multiple', 'multiple' ); + $form->add_render_attribute( 'input' . $item_index, 'name', $form->get_attribute_name( $item ) . '[]', true ); + } + + if ( ! empty( $item['file_sizes'] ) ) { + $form->add_render_attribute( + 'input' . $item_index, + [ + 'data-maxsize' => $item['file_sizes'], //MB + 'data-maxsize-message' => esc_html__( 'This file exceeds the maximum allowed size.', 'elementor-pro' ), + ] + ); + } + ?> + print_render_attribute_string( 'input' . $item_index ); ?>> + + fixed_files_indices ) { + return; + } + // a mapping of $_FILES indices for validity checking + $names = [ + 'name', + 'type', + 'tmp_name', + 'error', + 'size', + ]; + $files = $_FILES['form_fields']; // phpcs:ignore -- escaped when processing the file later on. + // iterate over each uploaded file + foreach ( $files as $key => $part ) { + $key = (string) $key; + if ( in_array( $key, $names, true ) && is_array( $part ) ) { + foreach ( $part as $position => $value ) { + if ( is_array( $value ) ) { + foreach ( $value as $index => $inner_val ) { + $files[ $position ][ $index ][ $key ] = $inner_val; + } + } else { + $files[ $position ][0][ $key ] = $value; + } + } + } + + // remove original key reference + unset( $files[ $key ] ); + } + $_FILES['form_fields'] = $files; + $this->fixed_files_indices = true; + } + + /** + * validate uploaded file size against allowed file size + * + * @param array $field + * @param $file + * + * @return bool + */ + private function is_file_size_valid( $field, $file ) { + $allowed_size = ( ! empty( $field['file_sizes'] ) ) ? $field['file_sizes'] : wp_max_upload_size() / pow( 1024, 2 ); + // File size validation + $file_size_meta = $allowed_size * pow( 1024, 2 ); + $upload_file_size = $file['size']; + + return ( $upload_file_size < $file_size_meta ); + } + + /** + * validates uploaded file type against allowed file types + * + * @param array $field + * @param $file + * + * @return bool + */ + private function is_file_type_valid( $field, $file ) { + // File type validation + if ( empty( $field['file_types'] ) ) { + $field['file_types'] = 'jpg,jpeg,png,gif,pdf,doc,docx,ppt,pptx,odt,avi,ogg,m4a,mov,mp3,mp4,mpg,wav,wmv'; + } + + $file_extension = pathinfo( $file['name'], PATHINFO_EXTENSION ); + $file_types_meta = explode( ',', $field['file_types'] ); + $file_types_meta = array_map( 'trim', $file_types_meta ); + + $file_types_meta = array_map( 'strtolower', $file_types_meta ); + $file_extension = strtolower( $file_extension ); + + return ( in_array( $file_extension, $file_types_meta ) && ! in_array( $file_extension, $this->get_blacklist_file_ext() ) ); + } + + /** + * A blacklist of file extensions. + * + * @return array + */ + private function get_blacklist_file_ext() { + static $blacklist = false; + if ( ! $blacklist ) { + $blacklist = [ + 'php', + 'php3', + 'php4', + 'php5', + 'php6', + 'phps', + 'php7', + 'phtml', + 'shtml', + 'pht', + 'swf', + 'html', + 'asp', + 'aspx', + 'cmd', + 'csh', + 'bat', + 'htm', + 'hta', + 'jar', + 'exe', + 'com', + 'js', + 'lnk', + 'htaccess', + 'htpasswd', + 'phtml', + 'ps1', + 'ps2', + 'py', + 'rb', + 'tmp', + 'cgi', + 'svg', + 'php2', + 'phtm', + 'phar', + 'hphp', + 'phpt', + 'svgz', + ]; + + /** + * Elementor forms blacklisted file extensions. + * + * Filters the list of file types that won't be uploaded using Elementor forms. + * + * By default Elementor forms doesn't upload some file types for security reasons. + * This hook allows developers to alter this list, either add more file types to + * strengthen the security or remove file types to increase flexibility. + * + * @since 1.0.0 + * + * @param array $blacklist A blacklist of file extensions. + */ + $blacklist = apply_filters( 'elementor_pro/forms/filetypes/blacklist', $blacklist ); + } + + return $blacklist; + } + + /** + * validate uploaded file field + * + * @param array $field + * @param Classes\Form_Record $record + * @param Classes\Ajax_Handler $ajax_handler + */ + public function validation( $field, Classes\Form_Record $record, Classes\Ajax_Handler $ajax_handler ) { + static $upload_errors = false; + + if ( ! $upload_errors ) { + $upload_errors = [ + UPLOAD_ERR_OK => esc_html__( 'There is no error, the file uploaded with success.', 'elementor-pro' ), + /* translators: 1: upload_max_filesize, 2: php.ini */ + UPLOAD_ERR_INI_SIZE => sprintf( esc_html__( 'The uploaded file exceeds the %1$s directive in %2$s.', 'elementor-pro' ), 'upload_max_filesize', 'php.ini' ), + /* translators: %s: MAX_FILE_SIZE */ + UPLOAD_ERR_FORM_SIZE => sprintf( esc_html__( 'The uploaded file exceeds the %s directive that was specified in the HTML form.', 'elementor-pro' ), 'MAX_FILE_SIZE' ), + UPLOAD_ERR_PARTIAL => esc_html__( 'The uploaded file was only partially uploaded.', 'elementor-pro' ), + UPLOAD_ERR_NO_FILE => esc_html__( 'No file was uploaded.', 'elementor-pro' ), + UPLOAD_ERR_NO_TMP_DIR => esc_html__( 'Missing a temporary folder.', 'elementor-pro' ), + UPLOAD_ERR_CANT_WRITE => esc_html__( 'Failed to write file to disk.', 'elementor-pro' ), + /* translators: %s: phpinfo() */ + UPLOAD_ERR_EXTENSION => sprintf( esc_html__( 'A PHP extension stopped the file upload. PHP does not provide a way to ascertain which extension caused the file upload to stop; examining the list of loaded extensions with %s may help.', 'elementor-pro' ), 'phpinfo()' ), + ]; + } + + $this->fix_file_indices(); + + $id = $field['id']; + $files = Utils::_unstable_get_super_global_value( $_FILES, 'form_fields' ); + + if ( ! empty( $field['max_files'] ) ) { + if ( count( $files[ $id ] ) > $field['max_files'] ) { + $error_message = sprintf( + /* translators: %d: The number of allowed files. */ + _n( 'You can upload only %d file.', 'You can upload up to %d files.', intval( $field['max_files'] ), 'elementor-pro' ), + intval( $field['max_files'] ) + ); + $ajax_handler->add_error( $id, $error_message ); + + return; + } + } + + foreach ( $files[ $id ] as $index => $file ) { + // not uploaded + if ( ! $field['required'] && UPLOAD_ERR_NO_FILE === $file['error'] ) { + return; + } + + // is the file required and missing? + if ( $field['required'] && UPLOAD_ERR_NO_FILE === $file['error'] ) { + $ajax_handler->add_error( $id, $upload_errors[ $file['error'] ] ); + + return; + } + + // Has any error with upload the file? + if ( $file['error'] > UPLOAD_ERR_OK ) { + $ajax_handler->add_error( $id, $upload_errors[ $file['error'] ] ); + + return; + } + + // valid file type? + if ( ! $this->is_file_type_valid( $field, $file ) ) { + $ajax_handler->add_error( $id, esc_html__( 'This file type is not allowed.', 'elementor-pro' ) ); + } + + // allowed file size? + if ( ! $this->is_file_size_valid( $field, $file ) ) { + $ajax_handler->add_error( $id, esc_html__( 'This file exceeds the maximum allowed size.', 'elementor-pro' ) ); + } + } + } + + /** + * Gets the path to uploaded file. + * + * @return string + */ + private function get_upload_dir() { + $wp_upload_dir = wp_upload_dir(); + $path = $wp_upload_dir['basedir'] . '/elementor/forms'; + + /** + * Elementor forms upload file path. + * + * Filters the path to a file uploaded using Elementor forms. + * + * By default Elementor forms defines a path to uploaded file. This + * hook allows developers to alter this path. + * + * @since 1.0.0 + * + * @param string $path Path to uploaded files. + */ + $path = apply_filters( 'elementor_pro/forms/upload_path', $path ); + + return $path; + } + + /** + * Gets the URL to uploaded file. + * + * @param $file_name + * + * @return string + */ + private function get_file_url( $file_name ) { + $wp_upload_dir = wp_upload_dir(); + $url = $wp_upload_dir['baseurl'] . '/elementor/forms/' . $file_name; + + /** + * Elementor forms upload file URL. + * + * Filters the URL to a file uploaded using Elementor forms. + * + * By default Elementor forms defines a URL to uploaded file. This + * hook allows developers to alter this URL. + * + * @since 1.0.0 + * + * @param string $url Upload file URL. + * @param string $file_name Upload file name. + */ + $url = apply_filters( 'elementor_pro/forms/upload_url', $url, $file_name ); + + return $url; + } + + /** + * This function returns the uploads folder after making sure + * it is created and has protection files + * @return string + */ + private function get_ensure_upload_dir() { + $path = $this->get_upload_dir(); + if ( file_exists( $path . '/index.php' ) ) { + return $path; + } + + wp_mkdir_p( $path ); + + $files = [ + [ + 'file' => 'index.php', + 'content' => [ + ' '.htaccess', + 'content' => [ + 'Options -Indexes', + '', + ' ', + ' Header set Content-Disposition attachment', + ' ', + '', + ], + ], + ]; + + foreach ( $files as $file ) { + if ( ! file_exists( trailingslashit( $path ) . $file['file'] ) ) { + $content = implode( PHP_EOL, $file['content'] ); + @ file_put_contents( trailingslashit( $path ) . $file['file'], $content ); + } + } + + return $path; + } + + /** + * creates array of upload sizes based on server limits + * to use in the file_sizes control + * @return array + */ + private function get_upload_file_size_options() { + $max_file_size = wp_max_upload_size() / pow( 1024, 2 ); //MB + + $sizes = []; + + for ( $file_size = 1; $file_size <= $max_file_size; $file_size++ ) { + $sizes[ $file_size ] = $file_size . 'MB'; + } + + return $sizes; + } + + /** + * process file and move it to uploads directory + * + * @param array $field + * @param Classes\Form_Record $record + * @param Classes\Ajax_Handler $ajax_handler + */ + public function process_field( $field, Classes\Form_Record $record, Classes\Ajax_Handler $ajax_handler ) { + $id = $field['id']; + $files = Utils::_unstable_get_super_global_value( $_FILES, 'form_fields' ); + + foreach ( $files[ $id ] as $index => $file ) { + if ( UPLOAD_ERR_NO_FILE === $file['error'] ) { + continue; + } + + $uploads_dir = $this->get_ensure_upload_dir(); + $file_extension = pathinfo( $file['name'], PATHINFO_EXTENSION ); + $filename = uniqid() . '.' . $file_extension; + $filename = wp_unique_filename( $uploads_dir, $filename ); + $new_file = trailingslashit( $uploads_dir ) . $filename; + + if ( is_dir( $uploads_dir ) && is_writable( $uploads_dir ) ) { + $move_new_file = Plugin::instance()->php_api->move_uploaded_file( $file['tmp_name'], $new_file ); + if ( false !== $move_new_file ) { + // Set correct file permissions. + $perms = 0644; + @ chmod( $new_file, $perms ); + + $record->add_file( $id, $index, + [ + 'path' => $new_file, + 'url' => $this->get_file_url( $filename ), + ] + ); + } else { + $ajax_handler->add_error( $id, esc_html__( 'There was an error while trying to upload your file.', 'elementor-pro' ) ); + } + } else { + $ajax_handler->add_admin_error_message( esc_html__( 'Upload directory is not writable or does not exist.', 'elementor-pro' ) ); + } + } + } + + /** + * Used to set the upload filed values with + * value => file url + * raw_value => file path + * + * @param Classes\Form_Record $record + * @param Classes\Ajax_Handler $ajax_handler + */ + public function set_file_fields_values( Classes\Form_Record $record, Classes\Ajax_Handler $ajax_handler ) { + $files = $record->get( 'files' ); + if ( empty( $files ) ) { + return; + } + foreach ( $files as $id => $files_array ) { + $record->update_field( $id, 'value', implode( ' , ', $files_array['url'] ) ); + $record->update_field( $id, 'raw_value', implode( ' , ', $files_array['path'] ) ); + } + } + + public function __construct() { + parent::__construct(); + add_action( 'elementor_pro/forms/process', [ $this, 'set_file_fields_values' ], 10, 2 ); + } +} diff --git a/modules/forms/module.php b/modules/forms/module.php new file mode 100644 index 0000000..f508d5a --- /dev/null +++ b/modules/forms/module.php @@ -0,0 +1,232 @@ + 'Form', + 'login' => 'Login', + ]; + + public function get_name() { + return 'forms'; + } + + public function get_widgets() { + return API::filter_active_features( static::WIDGET_NAME_CLASS_NAME_MAP ); + } + + /** + * @deprecated 3.1.0 + */ + public function localize_settings() { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); + + return []; + } + + public static function find_element_recursive( $elements, $form_id ) { + foreach ( $elements as $element ) { + if ( $form_id === $element['id'] ) { + return $element; + } + + if ( ! empty( $element['elements'] ) ) { + $element = self::find_element_recursive( $element['elements'], $form_id ); + + if ( $element ) { + return $element; + } + } + } + + return false; + } + + public function register_controls( Controls_Manager $controls_manager ) { + $controls_manager->register( new Fields_Repeater() ); + $controls_manager->register( new Fields_Map() ); + } + + /** + * @param array $data + * + * @return array + * @throws \Exception + */ + public function forms_panel_action_data( array $data ) { + $document = Utils::_unstable_get_document_for_edit( $data['editor_post_id'] ); + + if ( empty( $data['service'] ) ) { + throw new \Exception( 'Service required.' ); + } + + /** @var \ElementorPro\Modules\Forms\Classes\Integration_Base $integration */ + $integration = $this->actions_registrar->get( $data['service'] ); + + if ( ! $integration ) { + throw new \Exception( 'Action not found.' ); + } + + return $integration->handle_panel_request( $data ); + } + + /** + * @deprecated 3.5.0 Use `fields_registrar->register()` instead. + */ + public function add_form_field_type( $type, $instance ) { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( + __METHOD__, + '3.5.0', + 'fields_registrar->register()' + ); + + $this->fields_registrar->register( $instance, $type ); + } + + /** + * @deprecated 3.5.0 Use `actions_registrar->register()` instead. + */ + public function add_form_action( $id, $instance ) { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( + __METHOD__, + '3.5.0', + 'actions_registrar->register()' + ); + + $this->actions_registrar->register( $instance, $id ); + } + + /** + * @deprecated 3.5.0 Use `actions_registrar->get()` instead. + */ + public function get_form_actions( $id = null ) { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( + __METHOD__, + '3.5.0', + 'actions_registrar->get()' + ); + + return $this->actions_registrar->get( $id ); + } + + public function register_ajax_actions( Ajax $ajax ) { + $ajax->register_ajax_action( 'pro_forms_panel_action_data', [ $this, 'forms_panel_action_data' ] ); + } + + /** + * Register submissions + */ + private function register_submissions_component() { + $experiments_manager = Plugin::elementor()->experiments; + $name = Form_Submissions_Component::NAME; + + $experiments_manager->add_feature( [ + 'name' => $name, + 'title' => esc_html__( 'Form Submissions', 'elementor-pro' ), + 'description' => esc_html__( 'Never lose another submission! Using “Actions After Submit” you can now choose to save all submissions to an internal database.', 'elementor-pro' ), + 'release_status' => Manager::RELEASE_STATUS_STABLE, + 'default' => Manager::STATE_ACTIVE, + ] ); + + if ( ! $experiments_manager->is_feature_active( $name ) ) { + return; + } + + $this->add_component( $name, new Form_Submissions_Component() ); + } + + /** + * Module constructor. + */ + public function __construct() { + parent::__construct(); + + add_action( 'elementor/controls/register', [ $this, 'register_controls' ] ); + add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); + + $this->add_component( 'recaptcha', new Classes\Recaptcha_Handler() ); + $this->add_component( 'recaptcha_v3', new Classes\Recaptcha_V3_Handler() ); + $this->add_component( 'honeypot', new Classes\Honeypot_Handler() ); + + // Akismet + if ( class_exists( '\Akismet' ) && API::is_licence_has_feature( static::AKISMET_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->add_component( 'akismet', new Classes\Akismet() ); + } + + if ( API::is_licence_has_feature( Form_Submissions_Component::NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->register_submissions_component(); + } else { + add_action( 'elementor/admin/menu/register', function( $admin_menu ) { + $admin_menu->register( Form_Submissions_Component::PAGE_ID, new Submissions_Promotion_Menu_Item() ); + }, 9 /* After "Settings" */ ); + } + + // Initialize registrars. + $this->actions_registrar = new Form_Actions_Registrar(); + $this->fields_registrar = new Form_Fields_Registrar(); + + // Add Actions as components, that runs manually in the Ajax_Handler + + // Activity Log + if ( function_exists( 'aal_insert_log' ) && API::is_licence_has_feature( static::ACTIVITY_LOG_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->add_component( 'activity_log', new Actions\Activity_Log() ); + } + + // Contact Form to Database + if ( function_exists( 'CF7DBPlugin_init' ) && API::is_licence_has_feature( static::CF7DB_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->add_component( 'cf7db', new Actions\CF7DB() ); + } + + // Ajax Handler + if ( Classes\Ajax_Handler::is_form_submitted() ) { + $this->add_component( 'ajax_handler', new Classes\Ajax_Handler() ); + + /** + * Elementor form submitted. + * + * Fires when the form is submitted. This hook allows developers + * to add functionality after form submission. + * + * @since 2.0.0 + * + * @param Module $this An instance of the form module. + */ + do_action( 'elementor_pro/forms/form_submitted', $this ); + } + } +} diff --git a/modules/forms/registrars/form-actions-registrar.php b/modules/forms/registrars/form-actions-registrar.php new file mode 100644 index 0000000..5ec1910 --- /dev/null +++ b/modules/forms/registrars/form-actions-registrar.php @@ -0,0 +1,96 @@ + 'Email', + 'email2' => 'Email2', + 'redirect' => 'Redirect', + 'webhook' => 'Webhook', + 'mailchimp' => 'Mailchimp', + 'drip' => 'Drip', + 'activecampaign' => 'Activecampaign', + 'getresponse' => 'Getresponse', + 'convertkit' => 'Convertkit', + 'mailerlite' => 'Mailerlite', + 'slack' => 'Slack', + 'discord' => 'Discord', + ]; + + /** + * Form_Actions_Registrar constructor. + * + * @return void + */ + public function __construct() { + parent::__construct(); + + $this->init(); + } + + /** + * Initialize the default fields. + * + * @return void + */ + public function init() { + // Register the actions handlers using a hook since some actions need to be registered before those actions (e.g: save-to-database). + add_action( 'elementor_pro/forms/actions/register', function ( Form_Actions_Registrar $actions_registrar ) { + $form_actions = API::filter_active_features( static::FEATURE_NAME_CLASS_NAME_MAP ); + + foreach ( $form_actions as $action ) { + $class_name = 'ElementorPro\Modules\Forms\Actions\\' . $action; + $actions_registrar->register( new $class_name() ); + } + } ); + + /** + * Deprecated actions registration hook. + * + * @deprecated 3.5.0 Use `elementor_pro/forms/actions/register` instead. + */ + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->do_deprecated_action( + 'elementor_pro/forms/register_action', + [ $this ], + '3.5.0', + 'elementor_pro/forms/actions/register' + ); + + /** + * Elementor Pro form actions registration. + * + * Fires when a new form action is registered. This hook allows developers to + * register new form actions. + * + * @since 3.5.0 + * + * @param Form_Actions_Registrar $this An instance of form actions registration + * manager. + */ + do_action( 'elementor_pro/forms/actions/register', $this ); + + // MailPoet + if ( class_exists( '\WYSIJA' ) ) { + $this->register( new Actions\Mailpoet() ); + } + + // MailPoet + if ( class_exists( '\MailPoet\API\API' ) ) { + $this->register( new Actions\Mailpoet3() ); + } + } +} diff --git a/modules/forms/registrars/form-fields-registrar.php b/modules/forms/registrars/form-fields-registrar.php new file mode 100644 index 0000000..0adbd65 --- /dev/null +++ b/modules/forms/registrars/form-fields-registrar.php @@ -0,0 +1,55 @@ +init(); + } + + /** + * Initialize the default fields. + * + * @return void + */ + public function init() { + $this->register( new Fields\Time() ); + $this->register( new Fields\Date() ); + $this->register( new Fields\Tel() ); + $this->register( new Fields\Number() ); + $this->register( new Fields\Acceptance() ); + $this->register( new Fields\Upload() ); + $this->register( new Fields\Step() ); + + /** + * Elementor Pro form fields registration. + * + * Fires when a new form field is registered. This hook allows developers to + * register new form fields. + * + * @since 3.5.0 + * + * @param Form_Actions_Registrar $this An instance of form fields registration + * manager. + */ + do_action( 'elementor_pro/forms/fields/register', $this ); + } +} diff --git a/modules/forms/submissions/actions/save-to-database.php b/modules/forms/submissions/actions/save-to-database.php new file mode 100644 index 0000000..d070746 --- /dev/null +++ b/modules/forms/submissions/actions/save-to-database.php @@ -0,0 +1,181 @@ +start_controls_section( + 'section_submissions', + [ + 'label' => esc_html__( 'Collect Submissions', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + $widget->add_control( + 'submissions_action_message', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => sprintf( + /* translators: 1: Link open tag, 2: Link open tag, 3: Link close tag. */ + esc_html__( 'Collected Submissions will be saved to Elementor > %1$s Submissions %2$s', 'elementor-pro' ), + sprintf( '', self_admin_url( 'admin.php?page=' . Component::PAGE_ID ) ), + '', + ), + ] + ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + // This action does not have to do nothing on export. + } + + /** + * @param \ElementorPro\Modules\Forms\Classes\Form_Record $record + * @param \ElementorPro\Modules\Forms\Classes\Ajax_Handler $ajax_handler + */ + public function run( $record, $ajax_handler ) { + $meta = $record->get_form_meta( [ 'page_url', 'page_title', 'user_agent', 'remote_ip' ] ); + + $actions_count = ( new Collection( $record->get_form_settings( 'submit_actions' ) ) ) + ->filter(function ( $value ) { + return $value !== $this->get_name(); + }) + ->count(); + + $post_id = $record->get_form_settings( 'form_post_id' ); + $element_id = $ajax_handler->get_current_form()['id']; + $form_name = $record->get_form_settings( 'form_name' ); + + $this->submission_id = Query::get_instance()->add_submission( [ + 'main_meta_id' => 0, + 'post_id' => $post_id, + 'referer' => remove_query_arg( + [ 'preview_id', 'preview_nonce', 'preview' ], + $meta['page_url']['value'] + ), + 'referer_title' => $meta['page_title']['value'], + 'element_id' => $element_id, + 'form_name' => $form_name, + 'campaign_id' => 0, + 'user_id' => get_current_user_id(), + 'user_ip' => $meta['remote_ip']['value'], + 'user_agent' => $meta['user_agent']['value'], + 'actions_count' => $actions_count, + 'actions_succeeded_count' => 0, + 'meta' => wp_json_encode( [ + // TODO: Should be removed if there is an ability to edit "global widgets" + 'edit_post_id' => $record->get_form_settings( 'edit_post_id' ), + ] ), + ], $record->get_field( null ) ); + + /** @var Form $form_instance */ + $form_instance = Plugin::elementor()->elements_manager->create_element_instance( $ajax_handler->get_current_form() ); + $fields = $record->get_form_settings( 'form_fields' ); + + // When created new submission, it should also update or create + // a form snapshot to save to new state of the form in case the form changed or will + // be deleted later. + Form_Snapshot_Repository::instance() + ->create_or_update( $post_id, $element_id, [ + 'name' => $form_name, + 'fields' => array_map( function ( $field, $index ) use ( $form_instance ) { + // Apply filters to demonstrate the same behavior as the render behavior. (adding select fields dynamically, etc.) + // Ref: modules/forms/widgets/form.php:2116 + $field = apply_filters( 'elementor_pro/forms/render/item', $field, $index, $form_instance ); + $field = apply_filters( "elementor_pro/forms/render/item/{$field['field_type']}", $field, $index, $form_instance ); + + $mapped_field = [ + 'id' => $field['custom_id'], + 'type' => $field['field_type'], + 'label' => $field['field_label'], + ]; + + if ( isset( $field['field_options'] ) ) { + $mapped_field['options'] = preg_split( '/(\r\n|\n|\r)/', $field['field_options'] ); + } + + if ( isset( $field['allow_multiple'] ) ) { + $mapped_field['is_multiple'] = 'true' === $field['allow_multiple']; + } + + return $mapped_field; + }, $fields, array_keys( $fields ) ), + ] ); + } + + /** + * It listen for all the form actions and log the result into the database. + * + * @param Action_Base $action Should be class based on ActionBase (do not type hint to support third party plugins) + * @param \Exception|null $exception + */ + private function save_action_log( $action, \Exception $exception = null ) { + if ( ! $this->submission_id || $action->get_name() === $this->get_name() ) { + return; + } + + $query = Query::get_instance(); + $error_message = null; + + if ( $exception ) { + $error_message = $exception->getMessage(); + + $status = Query::ACTIONS_LOG_STATUS_FAILED; + } else { + $this->actions_succeeded_count += 1; + + $query->update_submission( $this->submission_id, [ + 'actions_succeeded_count' => $this->actions_succeeded_count, + ] ); + + $status = Query::ACTIONS_LOG_STATUS_SUCCESS; + } + + $query->add_action_log( + $this->submission_id, + $action, + $status, + $error_message + ); + } + + /** + * Save_To_Database constructor. + */ + public function __construct() { + add_action( 'elementor_pro/forms/actions/after_run', function ( Action_Base $action, \Exception $exception = null ) { + $this->save_action_log( $action, $exception ); + }, 10, 2 ); + } +} diff --git a/modules/forms/submissions/admin-menu-items/submissions-menu-item.php b/modules/forms/submissions/admin-menu-items/submissions-menu-item.php new file mode 100644 index 0000000..509659b --- /dev/null +++ b/modules/forms/submissions/admin-menu-items/submissions-menu-item.php @@ -0,0 +1,45 @@ + +
    +

    +
    +
    +
    + get_page_title(); + } + + public function get_page_title(): string { + return esc_html__( 'Submissions', 'elementor-pro' ); + } + + protected function get_promotion_title(): string { + return esc_html__( 'Seal the deal on your Form Submissions', 'elementor-pro' ) . '
    '; + } + + protected function get_content_lines():array { + return [ + esc_html__( 'Integrate your favorite marketing software', 'elementor-pro' ), + esc_html__( 'Collect lead submissions directly within your WordPress Admin to manage, analyze and perform bulk actions on the submitted lead', 'elementor-pro' ), + ]; + } + + protected function get_video_url(): string { + return 'https://www.youtube-nocookie.com/embed/LNfnwba9C-8?si=JLHk3UAexnvTfU1a'; + } + + public function get_cta_text() { + if ( ! API::active_licence_has_feature( Form_Submissions_Component::NAME ) ) { + return esc_html__( 'Upgrade Now', 'elementor-pro' ); + } + + return API::is_license_expired() + ? esc_html__( 'Renew now', 'elementor-pro' ) + : esc_html__( 'Connect & Activate', 'elementor-pro' ); + } + + protected function get_cta_url(): string { + if ( ! API::active_licence_has_feature( Form_Submissions_Component::NAME ) ) { + $upgrade_url = 'https://go.elementor.com/go-pro-advanced-form-submissions/'; + + return $upgrade_url; + } + + $connect_url = Plugin::instance()->license_admin->get_connect_url( [ + 'utm_source' => 'wp-dash-submissions', + 'utm_medium' => 'wp-dash', + 'utm_campaign' => 'connect-and-activate-license', + ] ); + + $renew_url = 'https://go.elementor.com/renew-submissions/'; + + return API::is_license_expired() + ? $renew_url + : $connect_url; + } +} diff --git a/modules/forms/submissions/component.php b/modules/forms/submissions/component.php new file mode 100644 index 0000000..e23732c --- /dev/null +++ b/modules/forms/submissions/component.php @@ -0,0 +1,220 @@ +add_submenu( [ + 'menu_title' => $this->get_title(), + 'menu_slug' => self::PAGE_ID, + 'function' => function () { + $this->render_admin_page(); + }, + 'index' => 35, + ] ); + } + + /** + * Register admin menu + */ + private function register_admin_menu_legacy( Admin_Menu_Manager $admin_menu ) { + $admin_menu->register( static::PAGE_ID, + $this->can_use_submissions() + ? new Submissions_Menu_Item() + : new Submissions_Promotion_Menu_Item() + ); + } + + private function can_use_submissions() : bool { + return API::is_license_active() && API::active_licence_has_feature( static::NAME ); + } + + private function render_admin_page() { + ?> +
    +

    +
    +
    +
    + get_css_assets_url( 'e-select2', '../elementor/assets/lib/e-select2/css/' ), + [], + '4.0.6-rc.1' + ); + + wp_enqueue_style( + 'elementor-app-base', + $this->get_css_assets_url( 'modules/forms/submissions/admin', null, 'default', true ), + [ 'select2' ], + ELEMENTOR_PRO_VERSION + ); + + wp_register_script( + 'select2', + $this->get_js_assets_url( 'e-select2.full', '../elementor/assets/lib/e-select2/js/' ), + [ + 'jquery', + ], + '4.0.6-rc.1', + true + ); + + wp_enqueue_script( + 'form-submission-admin', + $this->get_js_assets_url( 'form-submission-admin' ), + [ + 'select2', + 'wp-url', + 'wp-i18n', + 'wp-date', + 'react', + 'react-dom', + ], + ELEMENTOR_PRO_VERSION, + true + ); + + $is_trash_enabled = (int) ( EMPTY_TRASH_DAYS !== 0 ); + + wp_add_inline_script( + 'form-submission-admin', + "window.elementorSubmissionsConfig = { isTrashEnabled: {$is_trash_enabled} };", + 'before' + ); + + wp_set_script_translations( 'form-submission-admin', 'elementor-pro' ); + } + + private function scheduled_submissions_delete() { + $query = Query::get_instance(); + $delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS ); + + $ids = $query->get_trashed_submission_ids_to_delete( $delete_timestamp ); + + foreach ( $ids as $id ) { + $query->delete_submission( $id ); + } + } + + private function get_title() { + return esc_html__( 'Submissions', 'elementor-pro' ); + } + + /** + * Component constructor. + */ + public function __construct() { + parent::__construct(); + + Plugin::elementor()->data_manager->register_controller( Controller::class ); + Plugin::elementor()->data_manager->register_controller( Forms_Controller::class ); + + new Personal_Data(); + + add_action( 'admin_init', function () { + Migration::install(); + } ); + + add_action( 'elementor_pro/forms/actions/register', function ( Form_Actions_Registrar $actions_registrar ) { + $actions_registrar->register( new Save_To_Database() ); + }, 0 /* Before all the actions */ ); + + add_filter( 'elementor_pro/forms/default_submit_actions', function ( $actions ) { + return array_merge( $actions, [ 'save-to-database' ] ); + } ); + + add_action( 'wp_scheduled_delete', function () { + $this->scheduled_submissions_delete(); + } ); + + if ( Plugin::elementor()->experiments->is_feature_active( 'admin_menu_rearrangement' ) ) { + add_action( 'elementor/admin/menu_registered/elementor', function( MainMenu $menu ) { + $this->register_admin_menu( $menu ); + } ); + } else { + add_action( 'elementor/admin/menu/register', function( Admin_Menu_Manager $admin_menu ) { + $this->register_admin_menu_legacy( $admin_menu ); + }, 9 /* After "Settings" */ ); + + // TODO: BC - Remove after `Admin_Menu_Manager` will be the standard. + add_action( 'admin_menu', function () { + if ( did_action( 'elementor/admin/menu/register' ) ) { + return; + } + + $title = $this->get_title(); + + add_submenu_page( + Settings::PAGE_ID, + $title, + $title, + 'manage_options', + self::PAGE_ID, + function () { + $this->render_admin_page(); + } + ); + }, 21 /* after Elementor page */ ); + } + + if ( $this->is_current() ) { + add_action( 'admin_enqueue_scripts', function () { + $this->enqueue_scripts(); + } ); + } + } +} diff --git a/modules/forms/submissions/data/controller.php b/modules/forms/submissions/data/controller.php new file mode 100644 index 0000000..9e55dd2 --- /dev/null +++ b/modules/forms/submissions/data/controller.php @@ -0,0 +1,363 @@ +get_collection_params(); + + foreach ( $collection_params as $collection_param_key => $collection_param ) { + if ( isset( $collection_param['additionalProperties']['context'] ) && $context === $collection_param['additionalProperties']['context'] ) { + $result[ $collection_param_key ] = $collection_param; + } + } + + return $result; + } + + public function get_collection_params() { + $default_collection_params = parent::get_collection_params(); + + return array_merge( $default_collection_params, [ + 'page' => [ + 'description' => 'Current page of the collection.', + 'type' => 'integer', + 'default' => 1, + 'minimum' => 1, + 'required' => false, + ], + 'per_page' => [ + 'description' => 'Maximum number of items to be returned in result set.', + 'type' => 'integer', + 'default' => 50, + 'minimum' => 1, + 'maximum' => 100, + 'required' => false, + ], + 'order' => [ + 'description' => 'Order sort attribute ascending or descending.', + 'type' => 'string', + 'default' => 'desc', + 'enum' => [ + 'asc', + 'desc', + ], + 'required' => false, + ], + 'order_by' => [ + 'description' => 'Sort collection by object attribute.', + 'type' => 'string', + 'default' => 'created_at', + 'enum' => [ + 'created_at', + 'id', + 'main_meta_id', + ], + 'required' => false, + ], + 'status' => [ + 'description' => 'Limit result set to submissions assigned one or more statuses.', + 'type' => 'string', + 'default' => 'all', + 'enum' => [ + 'all', + 'unread', + 'read', + 'trash', + ], + 'additionalProperties' => [ + 'context' => 'filter', + ], + 'required' => false, + ], + 'search' => [ + 'description' => 'Limit results to those matching a string.', + 'type' => 'string', + 'required' => false, + 'additionalProperties' => [ + 'context' => 'filter', + ], + ], + 'form' => [ + 'description' => 'Limit result set to submissions assigned to specific forms. The form id should follow this pattern {post_id}_{element_id} e.g: 10_476d0ce', + 'type' => 'string', + 'required' => false, + 'additionalProperties' => [ + 'context' => 'filter', + ], + ], + 'referer' => [ + 'description' => 'Limit result set to submissions assigned to specific referer.', + 'type' => 'string', + 'required' => false, + 'additionalProperties' => [ + 'context' => 'filter', + ], + ], + 'after' => [ + 'description' => 'Limit response to submissions sent after a given ISO8601 compliant date.', + 'type' => 'string', + 'format' => 'date', + 'required' => false, + 'additionalProperties' => [ + 'context' => 'filter', + ], + ], + 'before' => [ + 'description' => 'Limit response to submissions sent before a given ISO8601 compliant date.', + 'type' => 'string', + 'format' => 'date', + 'required' => false, + 'additionalProperties' => [ + 'context' => 'filter', + ], + ], + ] ); + } + + public function get_items( $request ) { + $filters = []; + + // Get & set filters with values. + foreach ( $this->get_collection_params_by_additional_props_context( 'filter' ) as $collection_param_name => $collection_param_values ) { + $request_filter_value = $request->get_param( $collection_param_name ); + + if ( null !== $request_filter_value ) { + $collection_param_values['value'] = $request_filter_value; + + $filters[ $collection_param_name ] = $collection_param_values; + } + } + + $result = $this->query->get_submissions( [ + 'page' => $request->get_param( 'page' ), + 'per_page' => $request->get_param( 'per_page' ), + 'filters' => $filters, + 'order' => [ + 'order' => $request->get_param( 'order' ), + 'by' => $request->get_param( 'order_by' ), + ], + ] ); + + $result['meta']['count'] = $this->query + ->count_submissions_by_status( $filters ) + ->all(); + + return $result; + } + + public function get_item( $request ) { + return $this->query->get_submission( (int) $request->get_param( 'id' ) ); + } + + /** + * @param \WP_REST_Request $request + * + * @return \WP_Error|\WP_REST_Response + */ + public function delete_items( $request ) { + return $this->delete( + $request->get_param( 'ids' ), + $request->get_param( 'force' ) + ); + } + + /** + * Delete single submission + * + * @param \WP_REST_Request $request + * + * @return \WP_Error|\WP_REST_Response + */ + public function delete_item( $request ) { + return $this->delete( + [ $request->get_param( 'id' ) ], + $request->get_param( 'force' ) + ); + } + + /** + * Update a single submission. + * + * @param \WP_REST_Request $request + * + * @return \WP_Error|\WP_REST_Response + */ + public function update_item( $request ) { + return $this->update( + [ (int) $request->get_param( 'id' ) ], + $request + ); + } + + /** + * Update multiple submissions. + * + * @param $request + * + * @return \WP_Error|\WP_REST_Response + */ + public function update_items( $request ) { + return $this->update( + $request->get_param( 'ids' ), + $request + ); + } + + public function get_permission_callback( $request ) { + return current_user_can( 'manage_options' ); + } + + public function register_endpoints() { + $this->register_endpoint( Endpoints\Restore::class ); + $this->register_endpoint( Endpoints\Export::class ); + $this->register_endpoint( Endpoints\Referer::class ); + } + + protected function register_internal_endpoints() { + // Register as internal to remove the default endpoint generated by the base controller. + $this->register_endpoint( Endpoints\Index::class ); + } + + protected function register() { + parent::register(); + + $this->query = Query::get_instance(); + } + + /** + * Delete one or more submissions. + * + * @param array $ids + * @param false $force + * + * @return Query_Failed_Response|\WP_Error|\WP_REST_Response + */ + private function delete( array $ids, $force = false ) { + $affected = 0; + $failed = 0; + + foreach ( $ids as $id ) { + if ( $force ) { + $affected_rows = $this->query->delete_submission( $id ); + } else { + $affected_rows = $this->query->move_to_trash_submission( $id ); + } + + if ( false === $affected_rows ) { + $failed++; + } else { + $affected += $affected_rows; + } + } + + if ( count( $ids ) === $failed ) { + return new Query_Failed_Response( + $this->query->get_last_error() + ); + } + + if ( 1 === count( $ids ) && 0 === $affected ) { + return new \WP_Error( + 'rest_not_found', + __( 'Submission not found.', 'elementor-pro' ), + [ 'status' => 404 ] + ); + } + + return new \WP_REST_Response( [ + 'data' => [], + 'meta' => [ + 'affected' => $affected, + 'failed' => $failed, + ], + ], 200 ); + } + + /** + * Update one or more submissions. + * + * @param array $ids + * @param \WP_REST_Request $request + * + * @return Query_Failed_Response|\WP_Error|\WP_REST_Response + */ + private function update( array $ids, \WP_REST_Request $request ) { + $allowed_args = ( new Collection( $request->get_attributes()['args'] ) ) + ->except( [ 'id', 'ids', 'values' ] ) + ->keys(); + + $data = ( new Collection( $request->get_body_params() ) ) + ->only( $allowed_args ) + ->all(); + + $values = $request->get_param( 'values' ); + + $affected = 0; + $failed = 0; + + foreach ( $ids as $id ) { + $affected_rows = $this->query->update_submission( $id, $data, $values ); + + if ( false === $affected_rows ) { + $failed++; + } else { + $affected += $affected_rows; + } + } + + if ( count( $ids ) === $failed ) { + return new Query_Failed_Response( + $this->query->get_last_error() + ); + } + + if ( 1 === count( $ids ) ) { + if ( 0 === $affected ) { + return new \WP_Error( + 'rest_not_found', + __( 'Submission not found.', 'elementor-pro' ), + [ 'status' => 404 ] + ); + } + + return new \WP_REST_Response( $this->query->get_submission( $ids[0] ) ); + } + + return new \WP_REST_Response( [ + 'data' => [], + 'meta' => [ + 'affected' => $affected, + 'failed' => $failed, + ], + ], 200 ); + } +} diff --git a/modules/forms/submissions/data/endpoints/export.php b/modules/forms/submissions/data/endpoints/export.php new file mode 100644 index 0000000..337eb7f --- /dev/null +++ b/modules/forms/submissions/data/endpoints/export.php @@ -0,0 +1,134 @@ +register_route( + '', + \WP_REST_Server::READABLE, + function ( $request ) { + return $this->base_callback( \WP_REST_Server::READABLE, $request, true ); + }, + array_merge( $this->controller->get_collection_params(), [ + 'ids' => [ + 'description' => 'Unique identifiers for the objects.', + 'type' => 'array', + 'items' => [ + 'type' => 'integer', + ], + 'required' => false, + 'additionalProperties' => [ + 'context' => 'filter', + ], + ], + 'format' => [ + 'description' => 'The format of the export (for now only csv).', + 'types' => 'string', + 'enum' => [ + 'csv', + ], + 'default' => 'csv', + 'required' => false, + ], + 'per_page' => [ + 'description' => 'Maximum number of items to be returned in result set.', + 'type' => 'integer', + 'default' => 10, + 'minimum' => 1, + 'maximum' => 10000, + 'sanitize_callback' => 'absint', + 'validate_callback' => 'rest_validate_request_arg', + ], + ] ) + ); + } + + /** + * @param \WP_REST_Request $request + * + * @return \WP_Error|\WP_REST_Response + */ + public function get_items( $request ) { + wp_raise_memory_limit( 'admin' ); + + $submissions = new Collection( + $this->get_submissions_by_filter( $request ) + ); + + if ( 0 === $submissions->count() ) { + return new \WP_Error( + 'nothing_to_export', + __( 'There is nothing to export.', 'elementor-pro' ), + [ 'status' => 400 ] + ); + } + + $response = $submissions + ->group_by( 'element_id' ) + ->map( function ( array $submissions_by_form ) { + $exporter = new CSV_Export( + new Collection( $submissions_by_form ) + ); + + return $exporter->prepare_for_json_response(); + } ); + + return new \WP_REST_Response([ + 'data' => $response->values(), + ] ); + } + + /** + * Get submissions by filter. + * + * @param $request + * + * @return array|mixed + */ + private function get_submissions_by_filter( $request ) { + $args = $request->get_attributes()['args']; + + $filters = ( new Collection( $request->get_query_params() ) ) + ->filter(function ( $value, $key ) use ( $args ) { + return isset( $args[ $key ]['additionalProperties']['context'] ) && + 'filter' === $args[ $key ]['additionalProperties']['context']; + }) + ->map( function ( $value ) use ( $request ) { + return [ 'value' => $value ]; + } ) + ->all(); + + return Query::get_instance()->get_submissions( + [ + 'page' => $request->get_param( 'page' ), + 'per_page' => $request->get_param( 'per_page' ), + 'filters' => $filters, + 'order' => [ + 'order' => $request->get_param( 'order' ), + 'by' => $request->get_param( 'order_by' ), + ], + 'with_meta' => true, + ] + )['data']; + } +} diff --git a/modules/forms/submissions/data/endpoints/forms-index.php b/modules/forms/submissions/data/endpoints/forms-index.php new file mode 100644 index 0000000..596c438 --- /dev/null +++ b/modules/forms/submissions/data/endpoints/forms-index.php @@ -0,0 +1,35 @@ +register_route( + '', + \WP_REST_Server::READABLE, + function ( $request ) { + return $this->base_callback( \WP_REST_Server::READABLE, $request, true ); + }, + [ + 'context' => [ + 'description' => 'Scope under which the request is made, determines fields present in response. (only "options" available for now)', + 'type' => 'string', + 'enum' => [ + 'options', + ], + 'default' => 'options', + 'required' => false, + ], + ] + ); + } +} diff --git a/modules/forms/submissions/data/endpoints/index.php b/modules/forms/submissions/data/endpoints/index.php new file mode 100644 index 0000000..7bffae9 --- /dev/null +++ b/modules/forms/submissions/data/endpoints/index.php @@ -0,0 +1,146 @@ +register_route( + '', + \WP_REST_Server::READABLE, + function ( $request ) { + return $this->base_callback( \WP_REST_Server::READABLE, $request, true ); + }, + $this->controller->get_collection_params() + ); + + $this->register_route( + '(?P[\d]+)/', + \WP_REST_Server::READABLE, + function ( $request ) { + return $this->base_callback( \WP_REST_Server::READABLE, $request ); + }, + [ + 'id' => [ + 'description' => 'Unique identifier for the object.', + 'type' => 'string', + 'required' => true, + ], + ] + ); + + $this->register_route( + '(?P[\d]+)/', + \WP_REST_Server::DELETABLE, + function ( $request ) { + return $this->base_callback( \WP_REST_Server::DELETABLE, $request ); + }, + [ + 'id' => [ + 'description' => 'Unique identifier for the object.', + 'type' => 'string', + 'required' => true, + ], + 'force' => [ + 'description' => 'Delete the object permanently.', + 'type' => 'boolean', + 'required' => false, + ], + ] + ); + + $this->register_route( + '', + \WP_REST_Server::DELETABLE, + function ( $request ) { + return $this->base_callback( \WP_REST_Server::DELETABLE, $request, true ); + }, + [ + 'ids' => [ + 'description' => 'Unique identifiers for the objects.', + 'type' => 'array', + 'items' => [ + 'type' => 'integer', + ], + 'validate_callback' => function ( $param ) { + return ! empty( $param ); + }, + 'required' => true, + ], + 'force' => [ + 'description' => 'Delete the object permanently.', + 'type' => 'boolean', + 'required' => false, + ], + ] + ); + + $this->register_route( + '(?P[\d]+)/', + \WP_REST_Server::EDITABLE, + function ( $request ) { + return $this->base_callback( \WP_REST_Server::EDITABLE, $request ); + }, + [ + 'id' => [ + 'description' => 'Unique identifier for the object.', + 'type' => 'string', + 'required' => true, + ], + 'is_read' => [ + 'description' => 'mark whether the submission was read or not', + 'type' => 'boolean', + 'required' => false, + ], + 'values' => [ + 'description' => 'Form field values, receive an array, the key should be the form field id and the value should be the value.', + 'type' => 'object', + 'required' => false, + 'sanitize_callback' => function ( $values ) { + $result = []; + + foreach ( $values as $key => $value ) { + $result[ $key ] = sanitize_text_field( $value ); + } + + return $result; + }, + ], + ] + ); + + $this->register_route( + '', + \WP_REST_Server::EDITABLE, + function ( $request ) { + return $this->base_callback( \WP_REST_Server::EDITABLE, $request, true ); + }, + [ + 'ids' => [ + 'description' => 'Unique identifiers for the objects.', + 'type' => 'array', + 'items' => [ + 'type' => 'integer', + ], + 'validate_callback' => function ( $param ) { + return ! empty( $param ); + }, + 'required' => true, + ], + 'is_read' => [ + 'description' => 'mark whether the submission was read or not', + 'type' => 'boolean', + 'required' => false, + ], + ] + ); + } +} diff --git a/modules/forms/submissions/data/endpoints/referer.php b/modules/forms/submissions/data/endpoints/referer.php new file mode 100644 index 0000000..bd1fbfa --- /dev/null +++ b/modules/forms/submissions/data/endpoints/referer.php @@ -0,0 +1,70 @@ +register_route( + '', + \WP_REST_Server::READABLE, + function ( $request ) { + return $this->base_callback( \WP_REST_Server::READABLE, $request, true ); + }, + [ + 'context' => [ + 'description' => 'Scope under which the request is made, determines fields present in response. (only "options" available for now)', + 'type' => 'string', + 'enum' => [ + 'options', + ], + 'default' => 'options', + 'required' => false, + ], + 'search' => [ + 'description' => 'Limit results to those matching a string.', + 'type' => 'string', + 'required' => false, + 'additionalProperties' => [ + 'context' => 'filter', + ], + ], + 'value' => [ + 'description' => 'Limit results specific referer.', + 'type' => 'string', + 'required' => false, + 'additionalProperties' => [ + 'context' => 'filter', + ], + ], + ] + ); + } + + public function get_items( $request ) { + $referrers = Query::get_instance()->get_referrers( + $request->get_param( 'search' ), + $request->get_param( 'value' ) + ); + + // For now return only as "options" + return [ + 'data' => $referrers->map(function ( $referer ) { + return [ + 'label' => $referer['referer_title'], + 'value' => $referer['referer'], + ]; + })->values(), + 'meta' => [], + ]; + } +} diff --git a/modules/forms/submissions/data/endpoints/restore.php b/modules/forms/submissions/data/endpoints/restore.php new file mode 100644 index 0000000..5c2ec27 --- /dev/null +++ b/modules/forms/submissions/data/endpoints/restore.php @@ -0,0 +1,136 @@ +restore( + [ $request->get_param( 'id' ) ] + ); + } + + /** + * Restore multiple trashed submissions. + * + * @param \WP_REST_Request $request + * + * @return \WP_Error|\WP_REST_Response + */ + public function update_items( $request ) { + return $this->restore( + $request->get_param( 'ids' ) + ); + } + + protected function register() { + $this->register_route( + '/(?P[\d]+)/', + \WP_REST_Server::EDITABLE, + function ( $request ) { + return $this->base_callback( \WP_REST_Server::EDITABLE, $request ); + }, + [ + 'id' => [ + 'description' => 'Unique identifier for the object.', + 'type' => 'string', + 'required' => true, + ], + ] + ); + + $this->register_route( + '', + \WP_REST_Server::EDITABLE, + function ( $request ) { + return $this->base_callback( \WP_REST_Server::EDITABLE, $request, true ); + }, + [ + 'ids' => [ + 'description' => 'Unique identifiers for the objects.', + 'type' => 'array', + 'items' => [ + 'type' => 'integer', + ], + 'validate_callback' => function ( $param ) { + return ! empty( $param ); + }, + 'required' => true, + ], + ] + ); + } + + /** + * Restore on or more submissions. + * + * @param array $ids + * + * @return Query_Failed_Response|\WP_Error|\WP_REST_Response + */ + private function restore( array $ids ) { + $affected = 0; + $failed = 0; + + foreach ( $ids as $id ) { + $affected_rows = $this->query->restore( $id ); + + if ( false === $affected_rows ) { + $failed++; + } else { + $affected += $affected_rows; + } + } + + if ( count( $ids ) === $failed ) { + return new Query_Failed_Response( + $this->query->get_last_error() + ); + } + + if ( 1 === count( $ids ) && 0 === $affected ) { + return new \WP_Error( + 'rest_not_found', + __( 'Submission not found or not in trash.', 'elementor-pro' ), + [ 'status' => 404 ] + ); + } + + return new \WP_REST_Response( [ + 'data' => [], + 'meta' => [ + 'affected' => $affected, + 'failed' => $failed, + ], + ], 200 ); + } + + public function __construct( $controller ) { + $this->query = Query::get_instance(); + + parent::__construct( $controller ); + } +} diff --git a/modules/forms/submissions/data/forms-controller.php b/modules/forms/submissions/data/forms-controller.php new file mode 100644 index 0000000..f371056 --- /dev/null +++ b/modules/forms/submissions/data/forms-controller.php @@ -0,0 +1,44 @@ +all(); + + // For now return only as "options" + return [ + 'data' => $forms->map(function ( Form_Snapshot $form ) { + return [ + 'label' => $form->get_label(), + 'value' => $form->get_key(), + ]; + })->values(), + 'meta' => [], + ]; + } + + public function register_endpoints() { + // + } + + protected function register_internal_endpoints() { + // Register as internal to remove the default endpoint generated by the base controller. + $this->register_endpoint( Endpoints\Forms_Index::class ); + } + + public function get_permission_callback( $request ) { + return current_user_can( 'manage_options' ); + } +} diff --git a/modules/forms/submissions/data/responses/query-failed-response.php b/modules/forms/submissions/data/responses/query-failed-response.php new file mode 100644 index 0000000..1113475 --- /dev/null +++ b/modules/forms/submissions/data/responses/query-failed-response.php @@ -0,0 +1,28 @@ +log_error( $query_error_message ); + + parent::__construct( + 'rest_internal_error', + $message, + [ 'status' => 500 ] + ); + } + + private function log_error( $query_error_message ) { + Plugin::elementor()->logger->error( $query_error_message ); + } +} diff --git a/modules/forms/submissions/database/entities/form-snapshot.php b/modules/forms/submissions/database/entities/form-snapshot.php new file mode 100644 index 0000000..f6ef3e4 --- /dev/null +++ b/modules/forms/submissions/database/entities/form-snapshot.php @@ -0,0 +1,92 @@ +post_id, $this->id ); + } + + /** + * @return string + */ + public function get_label() { + return "{$this->name} ($this->id)"; + } + + /** + * Implement for the JsonSerializable method, will trigger when trying to json_encode this object. + * + * @return array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'fields' => $this->fields, + ]; + } + + + /** + * Form constructor. + * + * @param $post_id + * @param $data + */ + public function __construct( $post_id, $data ) { + $this->post_id = (int) $post_id; + $this->id = $data['id']; + $this->name = $data['name']; + $this->fields = $data['fields']; + } +} diff --git a/modules/forms/submissions/database/migration.php b/modules/forms/submissions/database/migration.php new file mode 100644 index 0000000..5514d04 --- /dev/null +++ b/modules/forms/submissions/database/migration.php @@ -0,0 +1,50 @@ + Migrations\Initial::class, + // It jumps from version 1 to 4 because some users already migrated the DB when the migrations system worked with the Elementor Pro version + // when the int value of the version "3.2.0" was 3. + 4 => Migrations\Referer_Extra::class, + 5 => Migrations\Fix_Indexes::class, + ]; + + /** + * Checks if there is a need to run migrations. + */ + public static function install() { + $installed_version = intval( get_option( self::OPTION_DB_VERSION ) ); + + // Up to date. Nothing to do. + if ( static::CURRENT_DB_VERSION <= $installed_version ) { + return; + } + + global $wpdb; + + ( new Collection( static::$migrations ) ) + ->filter( function ( $_, $version ) use ( $installed_version ) { + // Filter all the migrations that already done. + return $version > $installed_version; + } ) + ->map( function ( $migration_class_name, $version ) use ( $wpdb ) { + /** @var Migrations\Base_Migration $migration */ + $migration = new $migration_class_name( $wpdb ); + $migration->run(); + + // In case some migration failed it updates version every migration. + update_option( static::OPTION_DB_VERSION, $version ); + } ); + + update_option( static::OPTION_DB_VERSION, self::CURRENT_DB_VERSION ); + } +} diff --git a/modules/forms/submissions/database/migrations/base-migration.php b/modules/forms/submissions/database/migrations/base-migration.php new file mode 100644 index 0000000..dc218a1 --- /dev/null +++ b/modules/forms/submissions/database/migrations/base-migration.php @@ -0,0 +1,47 @@ +wpdb = $wpdb; + $this->query = Query::get_instance(); + } + + /** + * Run migration. + * + * @return void + */ + abstract public function run(); +} diff --git a/modules/forms/submissions/database/migrations/fix-indexes.php b/modules/forms/submissions/database/migrations/fix-indexes.php new file mode 100644 index 0000000..c627b82 --- /dev/null +++ b/modules/forms/submissions/database/migrations/fix-indexes.php @@ -0,0 +1,101 @@ +get_indexes(); + + foreach ( $indexes as $table => $table_indexes ) { + $existing_indexes = $this->get_existed_indexes_of( $table ); + + // Protect from database errors (for example: table do not exists somehow). + if ( null === $existing_indexes ) { + continue; + } + + $indexes_query = $table_indexes->except( $existing_indexes )->implode( ',' ); + + $this->wpdb->query( "ALTER TABLE `{$table}` {$indexes_query};" ); // phpcs:ignore + } + } + + /** + * Get the user exited indexes + * + * @param $table_name + * + * @return array|null + */ + private function get_existed_indexes_of( $table_name ) { + $result = $this->wpdb->get_results( "SHOW INDEX FROM {$table_name};", ARRAY_A ); // phpcs:ignore + + if ( false === $result ) { + return null; + } + + return ( new Collection( $result ) ) + ->map( function ( $row ) { + if ( ! isset( $row['Key_name'] ) ) { + return null; + } + + return $row['Key_name']; + } ) + ->filter() + ->values(); + } + + /** + * Get all the database indexes. + * + * @return Collection[] + */ + private function get_indexes() { + $max_index_length = static::MAX_INDEX_LENGTH; + + return [ + $this->query->get_table_submissions() => new Collection( [ + 'main_meta_id_index' => 'ADD INDEX `main_meta_id_index` (`main_meta_id`)', + 'hash_id_unique_index' => "ADD UNIQUE INDEX `hash_id_unique_index` (`hash_id` ({$max_index_length}))", + 'hash_id_index' => "ADD INDEX `hash_id_index` (`hash_id` ({$max_index_length}))", + 'type_index' => "ADD INDEX `type_index` (`type` ({$max_index_length}))", + 'post_id_index' => 'ADD INDEX `post_id_index` (`post_id`)', + 'element_id_index' => "ADD INDEX `element_id_index` (`element_id` ({$max_index_length}))", + 'campaign_id_index' => 'ADD INDEX `campaign_id_index` (`campaign_id`)', + 'user_id_index' => 'ADD INDEX `user_id_index` (`user_id`)', + 'user_ip_index' => 'ADD INDEX `user_ip_index` (`user_ip`)', + 'status_index' => 'ADD INDEX `status_index` (`status`)', + 'is_read_index' => 'ADD INDEX `is_read_index` (`is_read`)', + 'created_at_gmt_index' => 'ADD INDEX `created_at_gmt_index` (`created_at_gmt`)', + 'updated_at_gmt_index' => 'ADD INDEX `updated_at_gmt_index` (`updated_at_gmt`)', + 'created_at_index' => 'ADD INDEX `created_at_index` (`created_at`)', + 'updated_at_index' => 'ADD INDEX `updated_at_index` (`updated_at`)', + 'referer_index' => "ADD INDEX `referer_index` (`referer` ({$max_index_length}))", + 'referer_title_index' => "ADD INDEX `referer_title_index` (`referer_title` ({$max_index_length}))", + ] ), + $this->query->get_table_submissions_values() => new Collection( [ + 'submission_id_index' => 'ADD INDEX `submission_id_index` (`submission_id`)', + 'key_index' => "ADD INDEX `key_index` (`key` ({$max_index_length}))", + ] ), + $this->query->get_table_form_actions_log() => new Collection( [ + 'submission_id_index' => 'ADD INDEX `submission_id_index` (`submission_id`)', + 'action_name_index' => "ADD INDEX `action_name_index` (`action_name` ({$max_index_length}))", + 'status_index' => 'ADD INDEX `status_index` (`status`)', + 'created_at_gmt_index' => 'ADD INDEX `created_at_gmt_index` (`created_at_gmt`)', + 'updated_at_gmt_index' => 'ADD INDEX `updated_at_gmt_index` (`updated_at_gmt`)', + 'created_at_index' => 'ADD INDEX `created_at_index` (`created_at`)', + 'updated_at_index' => 'ADD INDEX `updated_at_index` (`updated_at`)', + ] ), + ]; + } +} diff --git a/modules/forms/submissions/database/migrations/initial.php b/modules/forms/submissions/database/migrations/initial.php new file mode 100644 index 0000000..0d5d14e --- /dev/null +++ b/modules/forms/submissions/database/migrations/initial.php @@ -0,0 +1,98 @@ +create_tables(); + $this->add_indexes(); + } + + private function create_tables() { + $charset_collate = $this->wpdb->get_charset_collate(); + + $e_submission_table = "CREATE TABLE `{$this->query->get_table_submissions()}` ( + id bigint(20) unsigned auto_increment primary key, + type varchar(60) null, + hash_id varchar(60) not null, + main_meta_id bigint(20) unsigned not null comment 'Id of main field. to represent the main meta field', + post_id bigint(20) unsigned not null, + referer varchar(500) not null, + element_id varchar(20) not null, + form_name varchar(60) not null, + campaign_id bigint(20) unsigned not null, + user_id bigint(20) unsigned null, + user_ip varchar(46) not null, + user_agent text not null, + actions_count INT DEFAULT 0, + actions_succeeded_count INT DEFAULT 0, + status varchar(20) not null, + is_read tinyint(1) default 0 not null, + meta text null, + created_at_gmt datetime not null, + updated_at_gmt datetime not null, + created_at datetime not null, + updated_at datetime not null + ) {$charset_collate};"; + + $e_submission_values_table = "CREATE TABLE `{$this->query->get_table_submissions_values()}` ( + id bigint(20) unsigned auto_increment primary key, + submission_id bigint(20) unsigned not null default 0, + `key` varchar(60) null, + value longtext null + ) {$charset_collate};"; + + $e_submission_actions_log_table = "CREATE TABLE `{$this->query->get_table_form_actions_log()}` ( + id bigint(20) unsigned auto_increment primary key, + submission_id bigint(20) unsigned not null, + action_name varchar(60) not null, + action_label varchar(60) null, + status varchar(20) not null, + log text null, + created_at_gmt datetime not null, + updated_at_gmt datetime not null, + created_at datetime not null, + updated_at datetime not null + ) {$charset_collate};"; + + require_once ABSPATH . 'wp-admin/includes/upgrade.php'; + + dbDelta( $e_submission_table . $e_submission_values_table . $e_submission_actions_log_table ); + } + + private function add_indexes() { + // phpcs:disable + $this->wpdb->query( "ALTER TABLE `{$this->query->get_table_submissions()}` + ADD INDEX `main_meta_id_index` (`main_meta_id`), + ADD UNIQUE INDEX `hash_id_unique_index` (`hash_id`), + ADD INDEX `hash_id_index` (`hash_id`), + ADD INDEX `type_index` (`type`), + ADD INDEX `post_id_index` (`post_id`), + ADD INDEX `element_id_index` (`element_id`), + ADD INDEX `campaign_id_index` (`campaign_id`), + ADD INDEX `user_id_index` (`user_id`), + ADD INDEX `user_ip_index` (`user_ip`), + ADD INDEX `status_index` (`status`), + ADD INDEX `is_read_index` (`is_read`), + ADD INDEX `created_at_gmt_index` (`created_at_gmt`), + ADD INDEX `updated_at_gmt_index` (`updated_at_gmt`), + ADD INDEX `created_at_index` (`created_at`), + ADD INDEX `updated_at_index` (`updated_at`) + " ); + + $this->wpdb->query( "ALTER TABLE `{$this->query->get_table_submissions_values()}` + ADD INDEX `submission_id_index` (`submission_id`), + ADD INDEX `key_index` (`key`) + " ); + + $this->wpdb->query( "ALTER TABLE `{$this->query->get_table_form_actions_log()}` + ADD INDEX `submission_id_index` (`submission_id`), + ADD INDEX `action_name_index` (`action_name`), + ADD INDEX `status_index` (`status`), + ADD INDEX `created_at_gmt_index` (`created_at_gmt`), + ADD INDEX `updated_at_gmt_index` (`updated_at_gmt`), + ADD INDEX `created_at_index` (`created_at`), + ADD INDEX `updated_at_index` (`updated_at`) + " ); + // phpcs:enable + } +} diff --git a/modules/forms/submissions/database/migrations/referer-extra.php b/modules/forms/submissions/database/migrations/referer-extra.php new file mode 100644 index 0000000..8370dab --- /dev/null +++ b/modules/forms/submissions/database/migrations/referer-extra.php @@ -0,0 +1,21 @@ +wpdb->query(" + ALTER TABLE `{$this->query->get_table_submissions()}` + ADD COLUMN `referer_title` varchar(300) null AFTER `referer`; + "); + + $this->wpdb->query(" + ALTER TABLE `{$this->query->get_table_submissions()}` + ADD INDEX `referer_index` (`referer`({$max_index_length})), + ADD INDEX `referer_title_index` (`referer_title`({$max_index_length})); + "); + // phpcs:enable + } +} diff --git a/modules/forms/submissions/database/query.php b/modules/forms/submissions/database/query.php new file mode 100644 index 0000000..aec66f0 --- /dev/null +++ b/modules/forms/submissions/database/query.php @@ -0,0 +1,904 @@ + 1, + 'per_page' => 10, + 'filters' => [], + 'order' => [], + 'with_meta' => false, + 'with_form_fields' => false, + ] ); + + $page = $args['page']; + $per_page = $args['per_page']; + $filters = $args['filters']; + $order = $args['order']; + $with_meta = $args['with_meta']; + $with_form_fields = $args['with_form_fields']; + + $result = [ + 'data' => [], + 'meta' => [], + ]; + + $where_sql = $this->apply_filter( $filters ); + $order_sql = ''; + + $total = (int) $this->wpdb->get_var("SELECT COUNT(*) FROM `{$this->get_table_submissions()}` t_submissions {$where_sql}" );// phpcs:ignore + + $last_page = 0 < $total && 0 < $per_page ? (int) ceil( $total / $per_page ) : 1; + + if ( $page > $last_page ) { + $page = $last_page; + } + + $offset = (int) ceil( ( $page - 1 ) * $per_page ); + + $result['meta']['pagination'] = [ + 'current_page' => $page, + 'per_page' => $per_page, + 'total' => $total, + 'last_page' => $last_page, + ]; + + $this->handle_order( $order, $order_sql ); + + $per_page = (int) $per_page; + + $q = "SELECT t_submissions.* FROM `{$this->get_table_submissions()}` t_submissions {$where_sql} {$order_sql} LIMIT {$per_page} OFFSET {$offset}"; + + $submissions = $this->wpdb->get_results( $q );// phpcs:ignore + + $data = new Collection( [] ); + + foreach ( $submissions as $current_submission ) { + $data[] = $this->get_submission_body( $current_submission, $with_form_fields ); + } + + $submissions_meta = $this + ->get_submissions_meta( $data, ! $with_meta ) + ->group_by( 'submission_id' ); + + $result['data'] = $data + ->map( function ( $submission ) use ( $submissions_meta ) { + $current_submission_meta = $submissions_meta->get( $submission['id'], [] ); + + foreach ( $current_submission_meta as $meta ) { + $extract_meta = $this->extract( $meta, [ 'id', 'key', 'value' ], [ 'id:int' ] ); + + if ( $meta->id === $submission['main_meta_id'] ) { + $submission['main'] = $extract_meta; + } + + $submission['values'][] = $extract_meta; + } + + return $submission; + } ) + ->all(); + + return $result; + } + + /** + * Get count by status. + * + * @param $filter + * + * @return Collection + */ + public function count_submissions_by_status( $filter = [] ) { + // Should not filter by status. + unset( $filter['status'] ); + + $where_sql = $this->apply_filter( $filter ); + + $trash_status = '"' . static::STATUS_TRASH . '"'; + + $sql = " + SELECT + SUM(IF(`status` != {$trash_status}, 1, 0)) AS `all`, + SUM(IF(`status` = {$trash_status}, 1, 0)) AS `trash`, + SUM(IF(is_read = 1 AND `status` != {$trash_status}, 1, 0)) AS `read`, + SUM(IF(is_read = 0 AND `status` != {$trash_status}, 1, 0)) AS `unread` + FROM {$this->get_table_submissions()} AS `t_submissions` {$where_sql}; + "; + + $sql_result = $this->wpdb->get_row( $sql, ARRAY_A ); // phpcs:ignore + + $result = new Collection( [ + 'all' => 0, + 'trash' => 0, + 'read' => 0, + 'unread' => 0, + ] ); + + if ( ! $sql_result ) { + return $result; + } + + return $result->map( function ( $count, $key ) use ( $sql_result ) { + if ( ! isset( $sql_result[ $key ] ) ) { + return $count; + } + + return intval( $sql_result[ $key ] ); + } ); + } + + public function get_submissions_by_email( $email, $include_submission_values = false ) { + $user = get_user_by( 'email', $email ); + $user_filter = ''; + + if ( $user ) { + $user_filter = $this->wpdb->prepare( 't_submissions.user_id = %s OR', $user->ID ); + } + + $query = " + SELECT DISTINCT(t_submissions.id), t_submissions.* + FROM `{$this->get_table_submissions()}` t_submissions + INNER JOIN {$this->get_table_submissions_values()} t_submissions_meta + ON t_submissions.id = t_submissions_meta.submission_id + WHERE + {$user_filter} + t_submissions_meta.value = %s + ; + "; + + $data = $this->wpdb->get_results( + $this->wpdb->prepare( $query, $email ) // phpcs:ignore + ); + + if ( ! $data ) { + return new Collection( [] ); + } + + $data = new Collection( $data ); + + if ( $include_submission_values ) { + $submissions_meta = $this->get_submissions_meta( $data ) + ->group_by( 'submission_id' ); + + $data->map( function ( $submission ) use ( $submissions_meta ) { + $submission->values = $submissions_meta->get( $submission->id, [] ); + + return $submission; + } ); + } + + return $data; + } + + /** + * @param int $delete_timestamp + * + * @return array + */ + public function get_trashed_submission_ids_to_delete( $delete_timestamp ) { + $date = gmdate( 'Y-m-d H:i:s', $delete_timestamp ); + + $sql = " + SELECT s.id FROM `{$this->get_table_submissions()}` s + WHERE s.status = %s AND s.updated_at_gmt < %s; + "; + + return $this->wpdb->get_col( + $this->wpdb->prepare( $sql, static::STATUS_TRASH, $date ) // phpcs:ignore + ); + } + + public function get_submission( $id ) { + $result = [ + 'data' => [], + 'meta' => [], + ]; + + $q = "SELECT * FROM `{$this->get_table_submissions()}` WHERE id=%d"; + $submission = $this->wpdb->get_row( $this->wpdb->prepare( $q, [ $id ] ) );// phpcs:ignore + + if ( ! $submission ) { + return null; + } + + $result['data'] = $this->get_submission_body( $submission, true ); + + $current_submission_meta = $this->get_submissions_meta( $submission, false )->all(); + + foreach ( $current_submission_meta as $meta ) { + $extract_meta = $this->extract( $meta, [ 'id', 'key', 'value' ], [ 'id:int' ] ); + + if ( $meta->id === $result['data']['main_meta_id'] ) { + $result['data']['main'] = $extract_meta; + } + + $result['data']['values'][] = $extract_meta; + } + + $result['data']['form_actions_log'] = ( new Collection( $this->get_submissions_actions_log( $id ) ) ) + ->map( function ( $value ) { + return $this->extract( + $value, + [ 'id', 'action_name', 'action_label', 'status', 'log', 'created_at', 'updated_at' ], + [ 'id:int', 'name', 'label' ] + ); + } ) + ->all(); + + return $result; + } + + public function get_referrers( $search = '', $value = '' ) { + $where = ''; + + if ( $search ) { + $search = '%' . $this->wpdb->esc_like( $search ) . '%'; + + $where = $this->wpdb->prepare( ' AND s.referer_title LIKE %s', $search ); + } + + if ( $value ) { + $where = $this->wpdb->prepare( ' AND s.referer = %s', $value ); // phpcs:ignore + } + + $where = 'WHERE 1=1' . $where; + + $query = " + SELECT DISTINCT (s.referer), s.referer_title + FROM {$this->get_table_submissions()} s + {$where}; + "; + + $result = $this->wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore + + if ( ! $result ) { + return new Collection( [] ); + } + + return new Collection( $result ); + } + + /** + * @param $submissions + * @param false $only_main + * + * @return Collection + */ + public function get_submissions_meta( $submissions, $only_main = false ) { + if ( ! ( $submissions instanceof Collection ) ) { + $submissions = new Collection( + is_array( $submissions ) ? $submissions : [ $submissions ] + ); + } + + if ( $submissions->is_empty() ) { + return new Collection( [] ); + } + + if ( $only_main ) { + $column = 'id'; + $ids = $submissions->pluck( 'main_meta_id' ); + } else { + $column = 'submission_id'; + $ids = $submissions->pluck( 'id' ); + } + + $placeholders = $ids->map( function () { + return '%d'; + } )->implode( ',' ); + + $q = "SELECT * FROM `{$this->get_table_submissions_values()}` WHERE `{$column}` IN ({$placeholders})"; + + $result = $this->wpdb->get_results( $this->wpdb->prepare( $q, $ids->all() ) ); // phpcs:ignore + + if ( ! $result ) { + return new Collection( [] ); + } + + return new Collection( $result ); + } + + /** + * @param $post_id + * @param $element_id + * + * @return Collection + */ + public function get_submissions_value_keys( $post_id, $element_id ) { + $sql = " + SELECT DISTINCT(wesv.`key`) + FROM {$this->get_table_submissions_values()} wesv + INNER JOIN {$this->get_table_submissions()} wes + ON wes.id = wesv.submission_id + WHERE wes.post_id = %s AND wes.element_id = %s; + "; + + $result = $this->wpdb->get_col( $this->wpdb->prepare( $sql, $post_id, $element_id ) ); // phpcs:ignore + + $form = Form_Snapshot_Repository::instance()->find( $post_id, $element_id ); + if ( $form ) { + $ordered_keys = array_map( function( $field ) { + return $field['id']; + }, $form->fields ); + + $result = array_merge( array_intersect( $ordered_keys, $result ), array_diff( $result, $ordered_keys ) ); + } + + if ( ! $result ) { + return new Collection( [] ); + } + + return new Collection( $result ); + } + + /** + * @param $submission_id + * + * @return array|null + */ + public function get_submissions_actions_log( $submission_id ) { + $query = "SELECT * FROM `{$this->get_table_form_actions_log()}` WHERE `submission_id` = %d;"; + + return $this->wpdb->get_results( + $this->wpdb->prepare( $query, (int) $submission_id ), // phpcs:ignore + ARRAY_A + ); + } + + /** + * Add submission. + * + * @param array $submission_data + * @param array $fields_data + * + * @return int id or 0 + */ + public function add_submission( array $submission_data, array $fields_data ) { + global $wpdb; + + $result = 0; + + $submission_data = $this->get_new_submission_initial_data( $submission_data ); + + try { + $wpdb->query( 'START TRANSACTION' ); + + $submission_success = $wpdb->insert( $this->get_table_submissions(), $submission_data ); + + if ( ! $submission_success ) { + throw new Exception( $wpdb->last_error ); + } + + $submission_id = $wpdb->insert_id; + + // Meta's keys/values. + $main_meta_id = 0; + $first_submissions_meta_id = 0; + foreach ( $fields_data as $field ) { + $wpdb->insert( $this->get_table_submissions_values(), [ + 'submission_id' => $submission_id, + 'key' => $field['id'], + 'value' => $field['value'], + ] ); + + if ( ! $first_submissions_meta_id ) { + $first_submissions_meta_id = $wpdb->insert_id; + } + + if ( 0 === $main_meta_id && 'email' === $field['type'] ) { + $main_meta_id = $wpdb->insert_id; + } + } + + // If `$main_meta_id` not determined. + if ( ! $main_meta_id ) { + $main_meta_id = $first_submissions_meta_id; + } + + // Update main_meta_id. + $wpdb->update( $this->get_table_submissions(), + [ + 'main_meta_id' => $main_meta_id, + ], + [ + 'id' => $submission_id, + ] + ); + + $wpdb->query( 'COMMIT' ); + + $result = $submission_id; + } catch ( Exception $e ) { + $wpdb->query( 'ROLLBACK' ); + + Plugin::elementor()->logger->get_logger()->error( 'Save submission failed, db error: ' . $e->getMessage() ); + } + + return $result; + } + + /** + * @param $submission_id + * @param array $data + * @param array $values + * + * @return bool|int affected rows + */ + public function update_submission( $submission_id, array $data, $values = [] ) { + if ( $values ) { + foreach ( $values as $key => $value ) { + $this->wpdb->update( + $this->get_table_submissions_values(), + [ 'value' => $value ], + [ + 'submission_id' => $submission_id, + 'key' => $key, + ] + ); + } + } + + $data['updated_at_gmt'] = current_time( 'mysql', true ); + $data['updated_at'] = get_date_from_gmt( $data['updated_at_gmt'] ); + + return $this->wpdb->update( + $this->get_table_submissions(), + $data, + [ 'id' => $submission_id ] + ); + } + + /** + * Move single submission to trash + * + * @param $id + * + * @return bool|int number of affected rows or false if failed + */ + public function move_to_trash_submission( $id ) { + return $this->update_submission( + $id, + [ 'status' => static::STATUS_TRASH ] + ); + } + + /** + * Delete a single submission. + * + * @param $id + * + * @return bool|int number of affected rows or false if failed + */ + public function delete_submission( $id ) { + $this->wpdb->delete( + $this->get_table_submissions_values(), + [ 'submission_id' => $id ] + ); + + $this->wpdb->delete( + $this->get_table_form_actions_log(), + [ 'submission_id' => $id ] + ); + + return $this->wpdb->delete( + $this->get_table_submissions(), + [ 'id' => $id ] + ); + } + + /** + * Restore a single submission. + * + * @param $id + * + * @return bool|int number of affected rows or false if failed + */ + public function restore( $id ) { + return $this->wpdb->update( + $this->get_table_submissions(), + [ 'status' => self::STATUS_NEW ], + [ 'id' => $id ] + ); + } + + /** + * @param $submission_id + * @param Action_Base $action Should be class based on ActionBase (do not type hint to support third party plugins) + * @param $status + * @param null $log_message + * + * @return bool|int + */ + public function add_action_log( $submission_id, $action, $status, $log_message = null ) { + $current_datetime_gmt = current_time( 'mysql', true ); + $current_datetime = get_date_from_gmt( $current_datetime_gmt ); + + return $this->wpdb->insert( + $this->get_table_form_actions_log(), + [ + 'submission_id' => $submission_id, + 'action_name' => $action->get_name(), + 'action_label' => $action->get_label(), + 'status' => $status, + 'log' => $log_message, + 'created_at_gmt' => $current_datetime_gmt, + 'updated_at_gmt' => $current_datetime_gmt, + 'created_at' => $current_datetime, + 'updated_at' => $current_datetime, + ] + ); + } + + public function get_last_error() { + $this->wpdb->last_error; + } + + public function get_table_submissions() { + return $this->table_submissions; + } + + public function get_table_submissions_values() { + return $this->table_submissions_values; + } + + public function get_table_form_actions_log() { + return $this->table_submissions_actions_log; + } + + /** + * Extract data from `$target` by selected `$keys`. + * `$as` used to convert extracted data with different keys. + * `$as` support converting to types. using ':' after the key name. + * + * Example: `$target = [ 'someId' => '5' ];` + * `$keys = [ 'someId' ]` + * `$as = [ 'id:int' ]` + * Output will be `[ 'id' => 5 ];` + * + * @param array|\stdClass $target + * @param array $keys + * @param array $as + * + * @return array + */ + private function extract( $target, $keys, $as = [] ) { + $result = []; + + $as_types = []; + $as_count = 0; + + if ( is_object( $target ) ) { + $target = (array) $target; + } + + foreach ( $as as $key => $as_item ) { + $exploded = explode( ':', $as_item ); + + if ( count( $exploded ) > 1 ) { + $as_types [] = $exploded[1]; + $as[ $key ] = $exploded[0]; + } + } + + foreach ( $keys as $key ) { + if ( isset( $target[ $key ] ) ) { + if ( isset( $as[ $as_count ] ) ) { + $value = $target[ $key ]; + if ( isset( $as_types[ $as_count ] ) ) { + settype( $value, $as_types[ $as_count ] ); + } + $result[ $as[ $as_count ] ] = $value; + } else { + $result[ $key ] = $target[ $key ]; + } + } + ++$as_count; + } + + return $result; + } + + private function handle_and_for_where_query( &$target_where_query ) { + if ( $target_where_query ) { + $target_where_query .= ' AND '; + } + } + + private function filter_after_and_before( $filters, &$target_where_query ) { + // Filters 'after' & 'before' known in advance, and could handled systematically. + if ( isset( $filters['after'] ) || isset( $filters['before'] ) ) { + $this->handle_and_for_where_query( $target_where_query ); + + // TODO: This logic applies only for 'date' format not 'date-time' format. + $after = '0000-01-01 00:00:00'; + $before = '9999-12-31 23:59:59'; + + if ( isset( $filters['after'] ) ) { + $after = $filters['after']['value'] . ' 00:00:00'; + } + + if ( isset( $filters['before'] ) ) { + $before = $filters['before']['value'] . ' 23:59:59'; + } + + $after = get_gmt_from_date( $after ); + $before = get_gmt_from_date( $before ); + + $target_where_query .= $this->wpdb->prepare( 'created_at_gmt BETWEEN %s and %s', [ $after, $before ] ); + } + } + + private function filter_status( $filters, &$target_where_query ) { + if ( isset( $filters['status'] ) ) { + $this->handle_and_for_where_query( $target_where_query ); + + switch ( $filters['status']['value'] ) { + case 'all': + $target_where_query .= 'status != \'' . self::STATUS_TRASH . '\''; + break; + case 'unread': + $target_where_query .= 'status = \'' . self::STATUS_NEW . '\' AND is_read = 0'; + break; + case 'read': + $target_where_query .= 'status = \'' . self::STATUS_NEW . '\' AND is_read > 0'; + break; + case 'trash': + $target_where_query .= 'status = \'' . self::STATUS_TRASH . '\''; + break; + } + } + } + + private function filter_ids( $filters, &$target_where_query ) { + if ( ! isset( $filters['ids'] ) || empty( $filters['ids'] ) ) { + return; + } + + $this->handle_and_for_where_query( $target_where_query ); + + $ids_collection = new Collection( $filters['ids']['value'] ); + + $placeholder = $ids_collection->map( function () { + return '%d'; + } )->implode( ', ' ); + + $target_where_query .= $this->wpdb->prepare( "`id` IN ({$placeholder})", $ids_collection->all() ); // phpcs:ignore + } + + private function filter_search( $filters, &$target_where_query ) { + if ( isset( $filters['search'] ) ) { + $this->handle_and_for_where_query( $target_where_query ); + + $like = '%' . $this->wpdb->esc_like( $filters['search']['value'] ) . '%'; + $meta_table_name = $this->get_table_submissions_values(); + + $search = $this->wpdb->prepare( 'LIKE %s OR t_submissions.id LIKE %s', [ $like, $like ] ); + + $target_where_query .= "( + ( + SELECT GROUP_CONCAT({$meta_table_name}.value) + FROM `{$meta_table_name}` + WHERE {$meta_table_name}.submission_id = t_submissions.id + GROUP BY {$meta_table_name}.submission_id + ) {$search} + )"; + } + } + + /** + * Filter bu element_id and post_id + * + * @param $filters + * @param $target_where_query + */ + private function filter_by_form( $filters, &$target_where_query ) { + if ( ! isset( $filters['form']['value'] ) ) { + return; + } + + $this->handle_and_for_where_query( $target_where_query ); + + list( $post_id, $form_id ) = explode( '_', $filters['form']['value'] ); + + $target_where_query .= $this->wpdb->prepare( + 'post_id = %d AND element_id = %s', + $post_id, + $form_id + ); + } + + /** + * @param $filters + * @param $target_where_query + */ + private function filter_by_referer( $filters, &$target_where_query ) { + if ( ! isset( $filters['referer']['value'] ) ) { + return; + } + + $this->handle_and_for_where_query( $target_where_query ); + + $target_where_query .= $this->wpdb->prepare( + 'referer = %s', + $filters['referer']['value'] + ); + } + + private function handle_order( $order, &$target_order_query ) { + if ( ! empty( $order ) ) { + $order['by'] = esc_sql( $order['by'] ); + $order['order'] = strtoupper( $order['order'] ); + + if ( ! in_array( $order['order'], [ 'ASC', 'DESC' ], true ) ) { + $order['order'] = 'ASC'; + } + + $target_order_query = 'ORDER BY ' . $order['by'] . ' ' . $order['order']; + } + } + + /** + * @param \stdClass $submission + * @param bool $with_form_fields + * + * @return array + */ + private function get_submission_body( $submission, $with_form_fields = false ) { + $id = (int) $submission->id; + + $result = [ + 'id' => $id, + ]; + + $result['post'] = $this->extract( $submission, [ 'post_id' ], [ 'id:int' ] ); + $result['form'] = $this->extract( $submission, [ 'form_name', 'element_id' ], [ 'name' ] ); + + $edit_post_id = $submission->post_id; + + // TODO: Should be removed if there is an ability to edit "global widgets" + $meta = json_decode( $submission->meta ?? '', true ); + + if ( isset( $meta['edit_post_id'] ) ) { + $edit_post_id = $meta['edit_post_id']; + } + + $document = Plugin::elementor()->documents->get( $edit_post_id ); + + if ( $document ) { + $result['post']['edit_url'] = $document->get_edit_url(); + } + + if ( $with_form_fields ) { + $form = Form_Snapshot_Repository::instance() + ->find( $submission->post_id, $submission->element_id ); + + if ( $form ) { + $result['form']['fields'] = $form->fields; + } + } + + $user = get_user_by( 'id', $submission->user_id ); + + $result['actions_count'] = (int) $submission->actions_count; + $result['actions_succeeded_count'] = (int) $submission->actions_succeeded_count; + $result['referer'] = $submission->referer; + $result['referer_title'] = $submission->referer_title; + $result['element_id'] = $submission->element_id; + $result['main_meta_id'] = $submission->main_meta_id; + $result['user_id'] = $submission->user_id; + $result['user_agent'] = $submission->user_agent; + $result['user_ip'] = $submission->user_ip; + $result['user_name'] = $user ? $user->display_name : null; + + $result['created_at_gmt'] = $submission->created_at_gmt; + $result['updated_at_gmt'] = $submission->updated_at_gmt; + + // Return the the dates according to WP current selected timezone. + $result['created_at'] = get_date_from_gmt( $submission->created_at_gmt ); + $result['updated_at'] = get_date_from_gmt( $submission->updated_at_gmt ); + + $result['status'] = $submission->status; + $result['is_read'] = (bool) $submission->is_read; + + return $result; + } + + private function get_new_submission_initial_data( array $submission_data ) { + $current_datetime_gmt = current_time( 'mysql', true ); + $current_datetime = get_date_from_gmt( $current_datetime_gmt ); + + $submission_data['hash_id'] = wp_generate_uuid4(); + + $submission_data = array_merge( [ + 'created_at_gmt' => $current_datetime_gmt, + 'updated_at_gmt' => $current_datetime_gmt, + 'created_at' => $current_datetime, + 'updated_at' => $current_datetime, + 'type' => self::TYPE_SUBMISSION, + 'status' => self::STATUS_NEW, + ], $submission_data ); + + return $submission_data; + } + + private function apply_filter( array $filter ) { + $where_sql = ''; + + $this->filter_after_and_before( $filter, $where_sql ); + $this->filter_status( $filter, $where_sql ); + $this->filter_search( $filter, $where_sql ); + $this->filter_by_form( $filter, $where_sql ); + $this->filter_ids( $filter, $where_sql ); + $this->filter_by_referer( $filter, $where_sql ); + + if ( ! $where_sql ) { + return ''; + } + + return 'WHERE ' . $where_sql; + } + + public function __construct() { + global $wpdb; + + $this->wpdb = $wpdb; + + $this->table_submissions = $wpdb->prefix . self::E_SUBMISSIONS; + $this->table_submissions_values = $wpdb->prefix . self::E_SUBMISSIONS_VALUES; + $this->table_submissions_actions_log = $wpdb->prefix . self::E_SUBMISSIONS_ACTIONS_LOG; + } +} diff --git a/modules/forms/submissions/database/repositories/form-snapshot-repository.php b/modules/forms/submissions/database/repositories/form-snapshot-repository.php new file mode 100644 index 0000000..5a0a93a --- /dev/null +++ b/modules/forms/submissions/database/repositories/form-snapshot-repository.php @@ -0,0 +1,168 @@ +cache->get( $key, false ) ) { + return $this->cache->get( $key, false ); + } + + return $this->save_in_cache( + $this->get_post_forms( $post_id ) + )->get( $key ); + } + + /** + * Get all the forms. + * + * @return Collection + */ + public function all() { + global $wpdb; + + $result = $wpdb->get_results( + $wpdb->prepare( + "SELECT pm.meta_value, pm.post_id FROM {$wpdb->postmeta} pm WHERE pm.meta_key = %s", + static::POST_META_KEY + ) + ); + + if ( ! $result ) { + return new Collection( [] ); + } + + foreach ( $result as $post_forms ) { + $this->save_in_cache( + $this->parse_meta( $post_forms->meta_value, $post_forms->post_id ) + ); + } + + return $this->cache; + } + + /** + * @param $post_id + * @param $form_id + * @param $data + * + * @return Form_Snapshot + */ + public function create_or_update( $post_id, $form_id, $data ) { + $forms = $this->get_post_forms( $post_id ) + ->filter( function ( Form_Snapshot $form ) use ( $form_id ) { + return $form->id !== $form_id; + } ); + + $form = new Form_Snapshot( $post_id, $data + [ 'id' => $form_id ] ); + $forms[] = $form; + + update_post_meta( + $post_id, + self::POST_META_KEY, + // Use `wp_slash` in order to avoid the unslashing during the `update_post_meta` + wp_slash( wp_json_encode( $forms->values() ) ) + ); + + $this->save_in_cache( $forms ); + + return $form; + } + + public function clear_cache() { + $this->cache = new Collection( [] ); + } + + /** + * @param $post_id + * + * @return Collection + */ + private function get_post_forms( $post_id ) { + $meta_value = get_post_meta( $post_id, self::POST_META_KEY, true ); + + if ( ! $meta_value ) { + return new Collection( [] ); + } + + return $this->parse_meta( $meta_value, $post_id ); + } + + /** + * Receive a meta value and transform it to an array of Form objects. + * + * @param $meta_value + * @param $post_id + * + * @return Collection + */ + private function parse_meta( $meta_value, $post_id ) { + return ( new Collection( json_decode( $meta_value, true ) ) ) + ->map( function ( $item ) use ( $post_id ) { + return new Form_Snapshot( $post_id, $item ); + } ); + } + + /** + * @param $forms + * + * @return Collection + */ + private function save_in_cache( Collection $forms ) { + /** @var Form_Snapshot $form */ + foreach ( $forms as $form ) { + $this->cache[ $form->get_key() ] = $form; + } + + return $this->cache; + } + + /** + * Forms_Repository constructor. + */ + public function __construct() { + $this->cache = new Collection( [] ); + } +} diff --git a/modules/forms/submissions/export/csv-export.php b/modules/forms/submissions/export/csv-export.php new file mode 100644 index 0000000..bb281c5 --- /dev/null +++ b/modules/forms/submissions/export/csv-export.php @@ -0,0 +1,186 @@ +submissions = $submissions; + + $first_submission = $this->submissions->first(); + + $this->values_keys = new Collection( [] ); + $this->post_id = $first_submission['post']['id']; + $this->element_id = $first_submission['element_id']; + + $this->form = Form_Snapshot_Repository::instance()->find( + $this->post_id, + $this->element_id + ); + } + + /** + * @return array + */ + public function prepare_for_json_response() { + $this->values_keys = Query::get_instance()->get_submissions_value_keys( + $this->post_id, + $this->element_id + ); + + $headers = $this->get_headers(); + $rows = $this->get_rows(); + + return [ + 'id' => $this->element_id, + 'content' => array_merge( $headers, $rows ), + 'mimetype' => 'text/csv;charset=UTF-8', + 'extension' => 'csv', + 'form_label' => $this->form ? $this->form->get_label() : "({$this->element_id})", + ]; + } + + /** + * @return array + */ + private function get_headers() { + $base_headers = [ + '1_form_name' => esc_html__( 'Form Name (ID)', 'elementor-pro' ), + '2_id' => esc_html__( 'Submission ID', 'elementor-pro' ), + '3_created_at' => esc_html__( 'Created At', 'elementor-pro' ), + '4_user_id' => esc_html__( 'User ID', 'elementor-pro' ), + '5_user_agent' => esc_html__( 'User Agent', 'elementor-pro' ), + '6_user_ip' => esc_html__( 'User IP', 'elementor-pro' ), + '7_referrer' => esc_html__( 'Referrer', 'elementor-pro' ), + ]; + + $labels_dictionary = $this->get_form_labels_dictionary(); + + $headers = $this->values_keys + ->map_with_keys( function ( $key ) use ( $labels_dictionary ) { + return [ + // JSON_UNESCAPED_UNICODE - for supporting non english chars. + $key => wp_json_encode( isset( $labels_dictionary[ $key ] ) ? $labels_dictionary[ $key ] : $key, JSON_UNESCAPED_UNICODE ), + ]; + } ) + ->merge( $base_headers ) + ->all(); + + return [ implode( ',', $headers ) ]; + } + + /** + * @return array + */ + private function get_rows() { + return $this->submissions->map( function ( $submission ) { + $base_values = [ + '1_form_name' => wp_json_encode( + $this->form ? $this->form->get_label() : "({$this->element_id})" + ), + '2_id' => wp_json_encode( $submission['id'] ), + '3_created_at' => wp_json_encode( $submission['created_at'] ), + '4_user_id' => wp_json_encode( $submission['user_id'] ), + // JSON_UNESCAPED_SLASHES - Should not escape the user agent e.g: 'Mozilla/5.0 ...' + '5_user_agent' => wp_json_encode( $submission['user_agent'], JSON_UNESCAPED_SLASHES ), + '6_user_ip' => wp_json_encode( $submission['user_ip'] ), + // JSON_UNESCAPED_SLASHES - should not escape the url slashes e.g: 'https://local.test/' + '7_referrer' => wp_json_encode( $submission['referer'], JSON_UNESCAPED_SLASHES ), + ]; + + $values_dictionary = $this->get_values_dictionary( $submission['values'] ); + + $row = $this->values_keys + ->map_with_keys( function ( $key ) use ( $values_dictionary ) { + return [ + // JSON_UNESCAPED_UNICODE - for supporting non english chars. + $key => wp_json_encode( isset( $values_dictionary[ $key ] ) ? $values_dictionary[ $key ] : '', JSON_UNESCAPED_UNICODE ), + ]; + } ) + ->merge( $base_values ) + ->all(); + + return implode( ',', $row ); + } )->all(); + } + + /** + * Create a dictionary from the field id and label. + * + * @return array + */ + private function get_form_labels_dictionary() { + if ( ! $this->form ) { + return []; + } + + $dictionary = []; + + foreach ( $this->form->fields as $field ) { + $dictionary[ $field['id'] ] = $field['label']; + } + + return $dictionary; + } + + /** + * Create a dictionary from the value record key and value. + * + * @param array $values + * + * @return array + */ + private function get_values_dictionary( $values ) { + if ( ! $values ) { + return []; + } + + $dictionary = []; + + foreach ( $values as $value ) { + $dictionary[ $value['key'] ] = $value['value']; + } + + return $dictionary; + } +} diff --git a/modules/forms/submissions/personal-data.php b/modules/forms/submissions/personal-data.php new file mode 100644 index 0000000..aacad23 --- /dev/null +++ b/modules/forms/submissions/personal-data.php @@ -0,0 +1,179 @@ +get_submissions_by_email( $email, true ) + ->map(function ( $submission ) { + $submission_data = ( new Collection( $submission->values ) ) + ->map( function ( $value ) { + return [ + 'name' => $value->key, + 'value' => $value->value, + ]; + } ) + ->merge([ + [ + 'name' => esc_html__( 'User IP', 'elementor-pro' ), + 'value' => $submission->user_ip, + ], + [ + 'name' => esc_html__( 'Referer', 'elementor-pro' ), + 'value' => $submission->referer, + ], + [ + 'name' => esc_html__( 'User Agent', 'elementor-pro' ), + 'value' => $submission->user_agent, + ], + [ + 'name' => esc_html__( 'Created At', 'elementor-pro' ), + 'value' => $submission->created_at, + ], + [ + 'name' => esc_html__( 'Created At GMT', 'elementor-pro' ), + 'value' => $submission->created_at_gmt, + ], + [ + 'name' => esc_html__( 'Updated At', 'elementor-pro' ), + 'value' => $submission->updated_at, + ], + [ + 'name' => esc_html__( 'Updated At GMT', 'elementor-pro' ), + 'value' => $submission->updated_at_gmt, + ], + ]) + ->all(); + + return [ + 'group_id' => $this->get_key(), + 'group_label' => $this->get_title(), + 'item_id' => "{$this->get_key()}-{$submission->id}", + 'data' => $submission_data, + ]; + }) + ->all(); + + return [ + 'data' => $data, + 'done' => true, + ]; + } + + /** + * Erase all the submissions related to specific email. + * + * WordPress send always an email even if the user choose to erase by username. + * + * @param $email + * + * @return array + */ + private function erase_data( $email ) { + $query = Query::get_instance(); + + $submissions = $query->get_submissions_by_email( $email, true ); + + $affected = 0; + $failed = 0; + + foreach ( $submissions as $submission ) { + $affected_rows = $query->delete_submission( $submission->id ); + + if ( false === $affected_rows ) { + $failed++; + } else { + $affected += $affected_rows; + } + } + + return [ + 'items_removed' => count( $submissions ) === $affected, + 'items_retained' => $failed > 0, + 'messages' => [], + 'done' => true, + ]; + } + + /** + * Add exporter to the list of exporters + * + * @param $exporters + * + * @return mixed + */ + private function add_exporter( $exporters ) { + $exporters[ $this->get_key() ] = [ + 'exporter_friendly_name' => $this->get_title(), + 'callback' => function ( $email ) { + return $this->export_data( $email ); + }, + ]; + + return $exporters; + } + + /** + * Add eraser to the list of erasers. + * + * @param $erasers + * + * @return array[] + */ + private function add_eraser( $erasers ) { + return $erasers + [ + $this->get_key() => [ + 'eraser_friendly_name' => $this->get_title(), + 'callback' => function ( $email ) { + return $this->erase_data( $email ); + }, + ], + ]; + } + + /** + * Personal_Data constructor. + */ + public function __construct() { + add_filter( 'wp_privacy_personal_data_exporters', function ( $exporters ) { + return $this->add_exporter( $exporters ); + } ); + + add_filter( 'wp_privacy_personal_data_erasers', function ( $exporters ) { + return $this->add_eraser( $exporters ); + } ); + } +} diff --git a/modules/forms/widgets/form.php b/modules/forms/widgets/form.php new file mode 100644 index 0000000..eab6ff8 --- /dev/null +++ b/modules/forms/widgets/form.php @@ -0,0 +1,2641 @@ + esc_html__( 'Text', 'elementor-pro' ), + 'email' => esc_html__( 'Email', 'elementor-pro' ), + 'textarea' => esc_html__( 'Textarea', 'elementor-pro' ), + 'url' => esc_html__( 'URL', 'elementor-pro' ), + 'tel' => esc_html__( 'Tel', 'elementor-pro' ), + 'radio' => esc_html__( 'Radio', 'elementor-pro' ), + 'select' => esc_html__( 'Select', 'elementor-pro' ), + 'checkbox' => esc_html__( 'Checkbox', 'elementor-pro' ), + 'acceptance' => esc_html__( 'Acceptance', 'elementor-pro' ), + 'number' => esc_html__( 'Number', 'elementor-pro' ), + 'date' => esc_html__( 'Date', 'elementor-pro' ), + 'time' => esc_html__( 'Time', 'elementor-pro' ), + 'upload' => esc_html__( 'File Upload', 'elementor-pro' ), + 'password' => esc_html__( 'Password', 'elementor-pro' ), + 'html' => esc_html__( 'HTML', 'elementor-pro' ), + 'hidden' => esc_html__( 'Hidden', 'elementor-pro' ), + ]; + + /** + * Forms field types. + * + * Filters the list of field types displayed in the form `field_type` control. + * + * This hook allows developers to alter the list of displayed field types. For + * example, removing the 'upload' field type from the list of fields types will + * prevent uploading files using Elementor forms. + * + * @since 1.0.0 + * + * @param array $field_types Field types. + */ + $field_types = apply_filters( 'elementor_pro/forms/field_types', $field_types ); + + $repeater->start_controls_tabs( 'form_fields_tabs' ); + + $repeater->start_controls_tab( 'form_fields_content_tab', [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + ] ); + + $repeater->add_control( + 'field_type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $field_types, + 'default' => 'text', + ] + ); + + $repeater->add_control( + 'field_label', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'placeholder', + [ + 'label' => esc_html__( 'Placeholder', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'operator' => 'in', + 'value' => [ + 'tel', + 'text', + 'email', + 'textarea', + 'number', + 'url', + 'password', + ], + ], + ], + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'required', + [ + 'label' => esc_html__( 'Required', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'return_value' => 'true', + 'default' => '', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'operator' => '!in', + 'value' => [ + 'checkbox', + 'recaptcha', + 'recaptcha_v3', + 'hidden', + 'html', + 'step', + ], + ], + ], + ], + ] + ); + + $repeater->add_control( + 'field_options', + [ + 'label' => esc_html__( 'Options', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => '', + 'description' => esc_html__( 'Enter each option in a separate line. To differentiate between label and value, separate them with a pipe char ("|"). For example: First Name|f_name', 'elementor-pro' ), + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'operator' => 'in', + 'value' => [ + 'select', + 'checkbox', + 'radio', + ], + ], + ], + ], + ] + ); + + $repeater->add_control( + 'allow_multiple', + [ + 'label' => esc_html__( 'Multiple Selection', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'return_value' => 'true', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'value' => 'select', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'select_size', + [ + 'label' => esc_html__( 'Rows', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'min' => 2, + 'step' => 1, + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'value' => 'select', + ], + [ + 'name' => 'allow_multiple', + 'value' => 'true', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'inline_list', + [ + 'label' => esc_html__( 'Inline List', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'return_value' => 'elementor-subgroup-inline', + 'default' => '', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'operator' => 'in', + 'value' => [ + 'checkbox', + 'radio', + ], + ], + ], + ], + ] + ); + + $repeater->add_control( + 'field_html', + [ + 'label' => esc_html__( 'HTML', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'dynamic' => [ + 'active' => true, + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'value' => 'html', + ], + ], + ], + ] + ); + + $repeater->add_responsive_control( + 'width', + [ + 'label' => esc_html__( 'Column Width', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Default', 'elementor-pro' ), + '100' => '100%', + '80' => '80%', + '75' => '75%', + '70' => '70%', + '66' => '66%', + '60' => '60%', + '50' => '50%', + '40' => '40%', + '33' => '33%', + '30' => '30%', + '25' => '25%', + '20' => '20%', + ], + 'default' => '100', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'operator' => '!in', + 'value' => [ + 'hidden', + 'recaptcha', + 'recaptcha_v3', + 'step', + ], + ], + ], + ], + ] + ); + + $repeater->add_control( + 'rows', + [ + 'label' => esc_html__( 'Rows', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 4, + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'value' => 'textarea', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'recaptcha_size', [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'normal', + 'options' => [ + 'normal' => esc_html__( 'Normal', 'elementor-pro' ), + 'compact' => esc_html__( 'Compact', 'elementor-pro' ), + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'value' => 'recaptcha', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'recaptcha_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'light', + 'options' => [ + 'light' => esc_html__( 'Light', 'elementor-pro' ), + 'dark' => esc_html__( 'Dark', 'elementor-pro' ), + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'value' => 'recaptcha', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'recaptcha_badge', [ + 'label' => esc_html__( 'Badge', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'bottomright', + 'options' => [ + 'bottomright' => esc_html__( 'Bottom Right', 'elementor-pro' ), + 'bottomleft' => esc_html__( 'Bottom Left', 'elementor-pro' ), + 'inline' => esc_html__( 'Inline', 'elementor-pro' ), + ], + 'description' => esc_html__( 'To view the validation badge, switch to preview mode', 'elementor-pro' ), + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'value' => 'recaptcha_v3', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'css_classes', + [ + 'label' => esc_html__( 'CSS Classes', 'elementor-pro' ), + 'type' => Controls_Manager::HIDDEN, + 'default' => '', + 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor-pro' ), + ] + ); + + $repeater->end_controls_tab(); + + $repeater->start_controls_tab( + 'form_fields_advanced_tab', + [ + 'label' => esc_html__( 'Advanced', 'elementor-pro' ), + 'condition' => [ + 'field_type!' => 'html', + ], + ] + ); + + $repeater->add_control( + 'field_value', + [ + 'label' => esc_html__( 'Default Value', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'field_type', + 'operator' => 'in', + 'value' => [ + 'text', + 'email', + 'textarea', + 'url', + 'tel', + 'radio', + 'select', + 'number', + 'date', + 'time', + 'hidden', + ], + ], + ], + ], + ] + ); + + $repeater->add_control( + 'custom_id', + [ + 'label' => esc_html__( 'ID', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'description' => esc_html__( 'Please make sure the ID is unique and not used elsewhere in this form. This field allows `A-z 0-9` & underscore chars without spaces.', 'elementor-pro' ), + 'render_type' => 'none', + 'required' => true, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $shortcode_template = '{{ view.container.settings.get( \'custom_id\' ) }}'; + $repeater->add_control( + 'shortcode', + [ + 'label' => esc_html__( 'Shortcode', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'classes' => 'forms-field-shortcode', + 'raw' => '', + ] + ); + + $repeater->end_controls_tab(); + + $repeater->end_controls_tabs(); + + $this->start_controls_section( + 'section_form_fields', + [ + 'label' => esc_html__( 'Form Fields', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'form_name', + [ + 'label' => esc_html__( 'Form Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'New Form', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Form Name', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'form_fields', + [ + 'type' => Fields_Repeater::CONTROL_TYPE, + 'fields' => $repeater->get_controls(), + 'default' => [ + [ + 'custom_id' => 'name', + 'field_type' => 'text', + 'field_label' => esc_html__( 'Name', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Name', 'elementor-pro' ), + 'width' => '100', + 'dynamic' => [ + 'active' => true, + ], + ], + [ + 'custom_id' => 'email', + 'field_type' => 'email', + 'required' => 'true', + 'field_label' => esc_html__( 'Email', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Email', 'elementor-pro' ), + 'width' => '100', + ], + [ + 'custom_id' => 'message', + 'field_type' => 'textarea', + 'field_label' => esc_html__( 'Message', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Message', 'elementor-pro' ), + 'width' => '100', + ], + ], + 'title_field' => '{{{ field_label }}}', + ] + ); + + $this->add_control( + 'input_size', + [ + 'label' => esc_html__( 'Input Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'xs' => esc_html__( 'Extra Small', 'elementor-pro' ), + 'sm' => esc_html__( 'Small', 'elementor-pro' ), + 'md' => esc_html__( 'Medium', 'elementor-pro' ), + 'lg' => esc_html__( 'Large', 'elementor-pro' ), + 'xl' => esc_html__( 'Extra Large', 'elementor-pro' ), + ], + 'default' => 'sm', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'show_labels', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'return_value' => 'true', + 'default' => 'true', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'mark_required', + [ + 'label' => esc_html__( 'Required Mark', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => '', + 'condition' => [ + 'show_labels!' => '', + ], + ] + ); + + $this->add_control( + 'label_position', + [ + 'label' => esc_html__( 'Label Position', 'elementor-pro' ), + 'type' => Controls_Manager::HIDDEN, + 'options' => [ + 'above' => esc_html__( 'Above', 'elementor-pro' ), + 'inline' => esc_html__( 'Inline', 'elementor-pro' ), + ], + 'default' => 'above', + 'condition' => [ + 'show_labels!' => '', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_buttons', + [ + 'label' => esc_html__( 'Buttons', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'sm', + 'options' => self::get_button_sizes(), + ] + ); + + $this->add_responsive_control( + 'button_width', + [ + 'label' => esc_html__( 'Column Width', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Default', 'elementor-pro' ), + '100' => '100%', + '80' => '80%', + '75' => '75%', + '70' => '70%', + '66' => '66%', + '60' => '60%', + '50' => '50%', + '40' => '40%', + '33' => '33%', + '30' => '30%', + '25' => '25%', + '20' => '20%', + ], + 'default' => '100', + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( + 'button_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'stretch' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'default' => 'stretch', + 'prefix_class' => 'elementor%s-button-align-', + ] + ); + + $this->add_control( + 'heading_steps_buttons', + [ + 'label' => esc_html__( 'Step Buttons', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'step_next_label', + [ + 'label' => esc_html__( 'Next', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'frontend_available' => true, + 'render_type' => 'none', + 'default' => esc_html__( 'Next', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Next', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'step_previous_label', + [ + 'label' => esc_html__( 'Previous', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'frontend_available' => true, + 'render_type' => 'none', + 'default' => esc_html__( 'Previous', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Previous', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'heading_submit_button', + [ + 'label' => esc_html__( 'Submit Button', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'button_text', + [ + 'label' => esc_html__( 'Submit', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Send', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Send', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'selected_button_icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'skin' => 'inline', + 'label_block' => false, + ] + ); + + $this->add_control( + 'button_icon_align', + [ + 'label' => esc_html__( 'Icon Position', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'left', + 'options' => [ + 'left' => esc_html__( 'Before', 'elementor-pro' ), + 'right' => esc_html__( 'After', 'elementor-pro' ), + ], + 'condition' => [ + 'selected_button_icon[value]!' => '', + ], + ] + ); + + $this->add_control( + 'button_icon_indent', + [ + 'label' => esc_html__( 'Icon Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'condition' => [ + 'selected_button_icon[value]!' => '', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-button .elementor-align-icon-right' => 'margin-left: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .elementor-button .elementor-align-icon-left' => 'margin-right: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'button_css_id', + [ + 'label' => esc_html__( 'Button ID', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'ai' => [ + 'active' => false, + ], + 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor-pro' ), + 'description' => esc_html__( 'Please make sure the ID is unique and not used elsewhere on the page this form is displayed. This field allows `A-z 0-9` & underscore chars without spaces.', 'elementor-pro' ), + 'separator' => 'before', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_integration', + [ + 'label' => esc_html__( 'Actions After Submit', 'elementor-pro' ), + ] + ); + + $actions = Module::instance()->actions_registrar->get(); + + $actions_options = []; + + foreach ( $actions as $action ) { + $actions_options[ $action->get_name() ] = $action->get_label(); + } + + $default_submit_actions = [ 'email' ]; + + /** + * Default submit actions. + * + * Filters the list of submit actions pre deffined by Elementor forms. + * + * By default, only one submit action is set by Elementor forms, an 'email' + * action. This hook allows developers to alter those submit action. + * + * @param array $default_submit_actions A list of default submit actions. + */ + $default_submit_actions = apply_filters( 'elementor_pro/forms/default_submit_actions', $default_submit_actions ); + + $this->add_control( + 'submit_actions', + [ + 'label' => esc_html__( 'Add Action', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'options' => $actions_options, + 'render_type' => 'none', + 'label_block' => true, + 'default' => $default_submit_actions, + 'description' => esc_html__( 'Add actions that will be performed after a visitor submits the form (e.g. send an email notification). Choosing an action will add its setting below.', 'elementor-pro' ), + ] + ); + + $this->end_controls_section(); + + foreach ( $actions as $action ) { + $action->register_settings_section( $this ); + } + + // Steps settings. + $this->start_controls_section( + 'section_steps_settings', + [ + 'label' => esc_html__( 'Steps Settings', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_control( + 'step_type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'frontend_available' => true, + 'render_type' => 'none', + 'options' => [ + 'none' => 'None', + 'text' => 'Text', + 'icon' => 'Icon', + 'number' => 'Number', + 'progress_bar' => 'Progress Bar', + 'number_text' => 'Number & Text', + 'icon_text' => 'Icon & Text', + ], + 'default' => 'number_text', + ] + ); + + $this->add_control( + 'step_icon_shape', + [ + 'label' => esc_html__( 'Shape', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'frontend_available' => true, + 'render_type' => 'none', + 'options' => [ + 'circle' => 'Circle', + 'square' => 'Square', + 'rounded' => 'Rounded', + 'none' => 'None', + ], + 'default' => 'circle', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'step_type', + 'operator' => '!in', + 'value' => [ + 'progress_bar', + 'text', + ], + ], + ], + ], + ] + ); + + $repeater->add_control( + 'display_percentage', + [ + 'label' => esc_html__( 'Display Percentage', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'frontend_available' => true, + 'render_type' => 'none', + 'return_value' => 'true', + 'default' => '', + 'condition' => [ + 'step_type' => 'progress_bar', + ], + ] + ); + + // End of steps settings. + $this->end_controls_section(); + + $this->start_controls_section( + 'section_form_options', + [ + 'label' => esc_html__( 'Additional Options', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_control( + 'form_id', + [ + 'label' => esc_html__( 'Form ID', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'placeholder' => 'new_form_id', + 'description' => esc_html__( 'Please make sure the ID is unique and not used elsewhere on the page this form is displayed. This field allows `A-z 0-9` & underscore chars without spaces.', 'elementor-pro' ), + 'separator' => 'after', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'form_validation', + [ + 'label' => esc_html__( 'Form Validation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Browser Default', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => '', + ] + ); + + $this->add_control( + 'custom_messages', + [ + 'label' => esc_html__( 'Custom Messages', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'separator' => 'before', + 'render_type' => 'none', + ] + ); + + $default_messages = Ajax_Handler::get_default_messages(); + + $this->add_control( + 'success_message', + [ + 'label' => esc_html__( 'Success Message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => $default_messages[ Ajax_Handler::SUCCESS ], + 'placeholder' => $default_messages[ Ajax_Handler::SUCCESS ], + 'label_block' => true, + 'condition' => [ + 'custom_messages!' => '', + ], + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'error_message', + [ + 'label' => esc_html__( 'Form Error', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => $default_messages[ Ajax_Handler::ERROR ], + 'placeholder' => $default_messages[ Ajax_Handler::ERROR ], + 'label_block' => true, + 'condition' => [ + 'custom_messages!' => '', + ], + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'server_message', + [ + 'label' => esc_html__( 'Server Error', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => $default_messages[ Ajax_Handler::SERVER_ERROR ], + 'placeholder' => $default_messages[ Ajax_Handler::SERVER_ERROR ], + 'label_block' => true, + 'condition' => [ + 'custom_messages!' => '', + ], + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'invalid_message', + [ + 'label' => esc_html__( 'Invalid Form', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => $default_messages[ Ajax_Handler::INVALID_FORM ], + 'placeholder' => $default_messages[ Ajax_Handler::INVALID_FORM ], + 'label_block' => true, + 'condition' => [ + 'custom_messages!' => '', + ], + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'required_field_message', + [ + 'label' => esc_html__( 'Required Field', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => $default_messages[ Ajax_Handler::FIELD_REQUIRED ], + 'placeholder' => $default_messages[ Ajax_Handler::FIELD_REQUIRED ], + 'label_block' => true, + 'condition' => [ + 'custom_messages!' => '', + 'form_validation' => 'custom', + ], + 'render_type' => 'none', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_form_style', + [ + 'label' => esc_html__( 'Form', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'column_gap', + [ + 'label' => esc_html__( 'Columns Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 10, + ], + 'range' => [ + 'px' => [ + 'max' => 60, + ], + 'em' => [ + 'max' => 6, + ], + 'rem' => [ + 'max' => 6, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group' => 'padding-right: calc( {{SIZE}}{{UNIT}}/2 ); padding-left: calc( {{SIZE}}{{UNIT}}/2 );', + '{{WRAPPER}} .elementor-form-fields-wrapper' => 'margin-left: calc( -{{SIZE}}{{UNIT}}/2 ); margin-right: calc( -{{SIZE}}{{UNIT}}/2 );', + ], + ] + ); + + $this->add_control( + 'row_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 10, + ], + 'range' => [ + 'px' => [ + 'max' => 60, + ], + 'em' => [ + 'max' => 6, + ], + 'rem' => [ + 'max' => 6, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group' => 'margin-bottom: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .elementor-field-group.recaptcha_v3-bottomleft, {{WRAPPER}} .elementor-field-group.recaptcha_v3-bottomright' => 'margin-bottom: 0;', + '{{WRAPPER}} .elementor-form-fields-wrapper' => 'margin-bottom: -{{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'heading_label', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'label_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 0, + ], + 'range' => [ + 'px' => [ + 'max' => 60, + ], + 'em' => [ + 'max' => 6, + ], + 'rem' => [ + 'max' => 6, + ], + ], + 'selectors' => [ + 'body.rtl {{WRAPPER}} .elementor-labels-inline .elementor-field-group > label' => 'padding-left: {{SIZE}}{{UNIT}};', + // for the label position = inline option + 'body:not(.rtl) {{WRAPPER}} .elementor-labels-inline .elementor-field-group > label' => 'padding-right: {{SIZE}}{{UNIT}};', + // for the label position = inline option + 'body {{WRAPPER}} .elementor-labels-above .elementor-field-group > label' => 'padding-bottom: {{SIZE}}{{UNIT}};', + // for the label position = above option + ], + ] + ); + + $this->add_control( + 'label_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group > label, {{WRAPPER}} .elementor-field-subgroup label' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_control( + 'mark_required_color', + [ + 'label' => esc_html__( 'Mark Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-mark-required .elementor-field-label:after' => 'color: {{COLOR}};', + ], + 'condition' => [ + 'mark_required' => 'yes', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'label_typography', + 'selector' => '{{WRAPPER}} .elementor-field-group > label', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_control( + 'heading_html', + [ + 'label' => esc_html__( 'HTML Field', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'html_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 0, + ], + 'range' => [ + 'px' => [ + 'max' => 60, + ], + 'em' => [ + 'max' => 6, + ], + 'rem' => [ + 'max' => 6, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-field-type-html' => 'padding-bottom: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'html_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-field-type-html' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'html_typography', + 'selector' => '{{WRAPPER}} .elementor-field-type-html', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_field_style', + [ + 'label' => esc_html__( 'Field', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'field_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group .elementor-field' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'field_typography', + 'selector' => '{{WRAPPER}} .elementor-field-group .elementor-field, {{WRAPPER}} .elementor-field-subgroup label', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_control( + 'field_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group:not(.elementor-field-type-upload) .elementor-field:not(.elementor-select-wrapper)' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .elementor-field-group .elementor-select-wrapper select' => 'background-color: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'field_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group:not(.elementor-field-type-upload) .elementor-field:not(.elementor-select-wrapper)' => 'border-color: {{VALUE}};', + '{{WRAPPER}} .elementor-field-group .elementor-select-wrapper select' => 'border-color: {{VALUE}};', + '{{WRAPPER}} .elementor-field-group .elementor-select-wrapper::before' => 'color: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'field_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'placeholder' => '1', + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group:not(.elementor-field-type-upload) .elementor-field:not(.elementor-select-wrapper)' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + '{{WRAPPER}} .elementor-field-group .elementor-select-wrapper select' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'field_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group:not(.elementor-field-type-upload) .elementor-field:not(.elementor-select-wrapper)' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + '{{WRAPPER}} .elementor-field-group .elementor-select-wrapper select' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_button_style', + [ + 'label' => esc_html__( 'Buttons', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'button_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}} .elementor-button', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), [ + 'name' => 'button_border', + 'selector' => '{{WRAPPER}} .elementor-button', + 'exclude' => [ + 'color', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_button_style' ); + + $this->start_controls_tab( + 'tab_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'heading_next_submit_button', + [ + 'label' => esc_html__( 'Next & Submit Button', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-next' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .elementor-button[type="submit"]' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-next' => 'color: {{VALUE}};', + '{{WRAPPER}} .elementor-button[type="submit"]' => 'color: {{VALUE}};', + '{{WRAPPER}} .elementor-button[type="submit"] svg *' => 'fill: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-next' => 'border-color: {{VALUE}};', + '{{WRAPPER}} .elementor-button[type="submit"]' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + 'button_border_border!' => '', + ], + ] + ); + + $this->add_control( + 'heading_previous_button', + [ + 'label' => esc_html__( 'Previous Button', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'previous_button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-previous' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'previous_button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-previous' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'previous_button_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-previous' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + 'button_border_border!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'heading_next_submit_button_hover', + [ + 'label' => esc_html__( 'Next & Submit Button', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'button_background_hover_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-next:hover' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .elementor-button[type="submit"]:hover' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_hover_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-next:hover' => 'color: {{VALUE}};', + '{{WRAPPER}} .elementor-button[type="submit"]:hover' => 'color: {{VALUE}};', + '{{WRAPPER}} .elementor-button[type="submit"]:hover svg *' => 'fill: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-next:hover' => 'border-color: {{VALUE}};', + '{{WRAPPER}} .elementor-button[type="submit"]:hover' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + 'button_border_border!' => '', + ], + ] + ); + + $this->add_control( + 'heading_previous_button_hover', + [ + 'label' => esc_html__( 'Previous Button', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'previous_button_background_color_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-previous:hover' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'previous_button_text_color_hover', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-previous:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'previous_button_border_color_hover', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-previous:hover' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + 'button_border_border!' => '', + ], + ] + ); + + $this->add_control( + 'hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} .e-form__buttons__wrapper__button-previous' => 'transition-duration: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .e-form__buttons__wrapper__button-next' => 'transition-duration: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .elementor-button[type="submit"] svg *' => 'transition-duration: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .elementor-button[type="submit"]' => 'transition-duration: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'button_hover_animation', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-button' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'button_text_padding', + [ + 'label' => esc_html__( 'Text Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_messages_style', + [ + 'label' => esc_html__( 'Messages', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'message_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-message', + ] + ); + + $this->add_control( + 'success_message_color', + [ + 'label' => esc_html__( 'Success Message Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-message.elementor-message-success' => 'color: {{COLOR}};', + ], + ] + ); + + $this->add_control( + 'error_message_color', + [ + 'label' => esc_html__( 'Error Message Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-message.elementor-message-danger' => 'color: {{COLOR}};', + ], + ] + ); + + $this->add_control( + 'inline_message_color', + [ + 'label' => esc_html__( 'Inline Message Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-message.elementor-help-inline' => 'color: {{COLOR}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_steps_style', + [ + 'label' => esc_html__( 'Steps', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'steps_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}} .e-form__indicators__indicator, {{WRAPPER}} .e-form__indicators__indicator__label', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'step_type', + 'operator' => '!in', + 'value' => [ + 'icon', + 'progress_bar', + ], + ], + ], + ], + ] + ); + + $this->add_responsive_control( + 'steps_gap', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 20, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicators-spacing: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'steps_icon_size', + [ + 'label' => esc_html__( 'Icon Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 15, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'step_type', + 'operator' => 'in', + 'value' => [ + 'icon', + 'icon_text', + ], + ], + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-icon-size: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'steps_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'size' => 30, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-padding: {{SIZE}}{{UNIT}}', + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'step_type', + 'operator' => '!in', + 'value' => [ + 'text', + 'progress_bar', + ], + ], + ], + ], + ] + ); + + $this->start_controls_tabs( 'steps_state', [ + 'condition' => [ + 'step_type!' => 'progress_bar', + ], + ] ); + + $this->start_controls_tab( + 'tab_steps_state_inactive', + [ + 'label' => esc_html__( 'Inactive', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'step_inactive_primary_color', + [ + 'label' => esc_html__( 'Primary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-inactive-primary-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'step_inactive_secondary_color', + [ + 'label' => esc_html__( 'Secondary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-inactive-secondary-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_steps_state_active', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'step_active_primary_color', + [ + 'label' => esc_html__( 'Primary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-active-primary-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'step_active_secondary_color', + [ + 'label' => esc_html__( 'Secondary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-active-secondary-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_steps_state_completed', + [ + 'label' => esc_html__( 'Completed', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'step_completed_primary_color', + [ + 'label' => esc_html__( 'Primary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-completed-primary-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'step_completed_secondary_color', + [ + 'label' => esc_html__( 'Secondary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'condition' => [ + 'step_icon_shape!' => 'none', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-completed-secondary-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_responsive_control( + 'step_divider_width', + [ + 'label' => esc_html__( 'Divider Width', 'elementor-pro' ), + 'separator' => 'before', + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'size' => 1, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'condition' => [ + 'step_type!' => 'progress_bar', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-divider-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'step_divider_gap', + [ + 'label' => esc_html__( 'Divider Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 10, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'condition' => [ + 'step_type!' => 'progress_bar', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-divider-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'step_progress_bar_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'condition' => [ + 'step_type' => 'progress_bar', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-progress-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'step_progress_bar_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'condition' => [ + 'step_type' => 'progress_bar', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-progress-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'step_progress_bar_height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'default' => [ + 'size' => 20, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'condition' => [ + 'step_type' => 'progress_bar', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-progress-height: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'step_progress_bar_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 0, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'condition' => [ + 'step_type' => 'progress_bar', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-progress-border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'step_progress_bar_percentage_heading', + [ + 'label' => esc_html__( 'Percentage', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'step_type' => 'progress_bar', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'step_progress_bar_percentage__typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}} .e-form__indicators__indicator__progress__meter', + 'condition' => [ + 'step_type' => 'progress_bar', + ], + ] + ); + + $this->add_control( + 'step_progress_bar_percentage_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'condition' => [ + 'step_type' => 'progress_bar', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-form-steps-indicator-progress-meter-color: {{VALUE}};', + ], + ] + ); + + // End of steps style. + $this->end_controls_section(); + + } + + private function render_icon_with_fallback( $settings ) { + $migrated = isset( $settings['__fa4_migrated']['selected_button_icon'] ); + $is_new = empty( $settings['button_icon'] ) && Icons_Manager::is_migration_allowed(); + + if ( $is_new || $migrated ) { + Icons_Manager::render_icon( $settings['selected_button_icon'], [ 'aria-hidden' => 'true' ] ); + } else { + ?>get_settings_for_display(); + + if ( ! Plugin::elementor()->editor->is_edit_mode() ) { + /** + * Elementor form pre render. + * + * Fires before the from is rendered in the frontend. This hook allows + * developers to add functionality before the from is rendered. + * + * @since 2.4.0 + * + * @param array $instance Current form settings. + * @param Form $this An instance of the form. + */ + do_action( 'elementor-pro/forms/pre_render', $instance, $this ); + } + + $this->add_render_attribute( + [ + 'wrapper' => [ + 'class' => [ + 'elementor-form-fields-wrapper', + 'elementor-labels-' . $instance['label_position'], + ], + ], + 'submit-group' => [ + 'class' => [ + 'elementor-field-group', + 'elementor-column', + 'elementor-field-type-submit', + ], + ], + 'button' => [ + 'class' => 'elementor-button', + ], + 'icon-align' => [ + 'class' => [ + empty( $instance['button_icon_align'] ) ? '' : + 'elementor-align-icon-' . $instance['button_icon_align'], + 'elementor-button-icon', + ], + ], + ] + ); + + if ( empty( $instance['button_width'] ) ) { + $instance['button_width'] = '100'; + } + + $this->add_render_attribute( 'submit-group', 'class', 'elementor-col-' . $instance['button_width'] . ' e-form__buttons' ); + + if ( ! empty( $instance['button_width_tablet'] ) ) { + $this->add_render_attribute( 'submit-group', 'class', 'elementor-md-' . $instance['button_width_tablet'] ); + } + + if ( ! empty( $instance['button_width_mobile'] ) ) { + $this->add_render_attribute( 'submit-group', 'class', 'elementor-sm-' . $instance['button_width_mobile'] ); + } + + if ( ! empty( $instance['button_size'] ) ) { + $this->add_render_attribute( 'button', 'class', 'elementor-size-' . $instance['button_size'] ); + } + + if ( ! empty( $instance['button_type'] ) ) { + $this->add_render_attribute( 'button', 'class', 'elementor-button-' . $instance['button_type'] ); + } + + if ( $instance['button_hover_animation'] ) { + $this->add_render_attribute( 'button', 'class', 'elementor-animation-' . $instance['button_hover_animation'] ); + } + + if ( ! empty( $instance['form_id'] ) ) { + $this->add_render_attribute( 'form', 'id', $instance['form_id'] ); + } + + if ( ! empty( $instance['form_name'] ) ) { + $this->add_render_attribute( 'form', 'name', $instance['form_name'] ); + } + + if ( 'custom' === $instance['form_validation'] ) { + $this->add_render_attribute( 'form', 'novalidate' ); + } + + if ( ! empty( $instance['button_css_id'] ) ) { + $this->add_render_attribute( 'button', 'id', $instance['button_css_id'] ); + } + + $referer_title = trim( wp_title( '', false ) ); + + if ( ! $referer_title && is_home() ) { + $referer_title = get_option( 'blogname' ); + } + + ?> +
    print_render_attribute_string( 'form' ); ?>> + + + + + + + + +
    print_render_attribute_string( 'wrapper' ); ?>> + $item ) : + $item['input_size'] = $instance['input_size']; + $this->form_fields_render_attributes( $item_index, $instance, $item ); + + $field_type = $item['field_type']; + + /** + * Render form field. + * + * Filters the field rendered by Elementor forms. + * + * @since 1.0.0 + * + * @param array $item The field value. + * @param int $item_index The field index. + * @param Form $this An instance of the form. + */ + $item = apply_filters( 'elementor_pro/forms/render/item', $item, $item_index, $this ); + + /** + * Render form field. + * + * Filters the field rendered by Elementor forms. + * + * The dynamic portion of the hook name, `$field_type`, refers to the field type. + * + * @since 1.0.0 + * + * @param array $item The field value. + * @param int $item_index The field index. + * @param Form $this An instance of the form. + */ + $item = apply_filters( "elementor_pro/forms/render/item/{$field_type}", $item, $item_index, $this ); + + $print_label = ! in_array( $item['field_type'], [ 'hidden', 'html', 'step' ], true ); + ?> +
    print_render_attribute_string( 'field-group' . $item_index ); ?>> + + + make_textarea_field( $item, $item_index ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + break; + + case 'select': + // PHPCS - the method make_select_field is safe. + echo $this->make_select_field( $item, $item_index ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + break; + + case 'radio': + case 'checkbox': + // PHPCS - the method make_radio_checkbox_field is safe. + echo $this->make_radio_checkbox_field( $item, $item_index, $item['field_type'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + break; + case 'text': + case 'email': + case 'url': + case 'password': + case 'hidden': + case 'search': + $this->add_render_attribute( 'input' . $item_index, 'class', 'elementor-field-textual' ); + ?> + print_render_attribute_string( 'input' . $item_index ); ?>> + +
    + +
    print_render_attribute_string( 'submit-group' ); ?>> + +
    +
    +
    + + <# + view.addRenderAttribute( + 'form', + { + 'id': settings.form_id, + 'name': settings.form_name, + } + ); + if ( 'custom' === settings.form_validation ) { + view.addRenderAttribute( 'form', 'novalidate' ); + } + #> +
    +
    + <# + for ( var i in settings.form_fields ) { + var item = settings.form_fields[ i ]; + item = elementor.hooks.applyFilters( 'elementor_pro/forms/content_template/item', item, i, settings ); + + item.field_type = _.escape( item.field_type ); + item.field_value = _.escape( item.field_value ); + + var options = item.field_options ? item.field_options.split( '\n' ) : [], + itemClasses = _.escape( item.css_classes ), + labelVisibility = '', + placeholder = '', + required = '', + inputField = '', + multiple = '', + fieldGroupClasses = 'elementor-field-group elementor-column elementor-field-type-' + item.field_type, + printLabel = settings.show_labels && ! [ 'hidden', 'html', 'step' ].includes( item.field_type ); + + fieldGroupClasses += ' elementor-col-' + ( ( '' !== item.width ) ? item.width : '100' ); + + if ( item.width_tablet ) { + fieldGroupClasses += ' elementor-md-' + item.width_tablet; + } + + if ( item.width_mobile ) { + fieldGroupClasses += ' elementor-sm-' + item.width_mobile; + } + + if ( item.required ) { + required = 'required'; + fieldGroupClasses += ' elementor-field-required'; + + if ( settings.mark_required ) { + fieldGroupClasses += ' elementor-mark-required'; + } + } + + if ( item.placeholder ) { + placeholder = 'placeholder="' + _.escape( item.placeholder ) + '"'; + } + + if ( item.allow_multiple ) { + multiple = ' multiple'; + fieldGroupClasses += ' elementor-field-type-' + item.field_type + '-multiple'; + } + + switch ( item.field_type ) { + case 'step': + inputField = `
    `; + break; + case 'html': + inputField = item.field_html; + break; + + case 'textarea': + inputField = ''; + break; + + case 'select': + if ( options ) { + var size = ''; + if ( item.allow_multiple && item.select_size ) { + size = ' size="' + item.select_size + '"'; + } + inputField = '
    '; + inputField += '
    '; + } + break; + + case 'radio': + case 'checkbox': + if ( options ) { + var multiple = ''; + + if ( 'checkbox' === item.field_type && options.length > 1 ) { + multiple = '[]'; + } + + inputField = '
    '; + + for ( var x in options ) { + var option_value = options[ x ]; + var option_label = options[ x ]; + var option_id = 'form_field_' + item.field_type + i + x; + if ( options[x].indexOf( '|' ) > -1 ) { + var label_value = options[x].split( '|' ); + option_label = label_value[0]; + option_value = label_value[1]; + } + + view.addRenderAttribute( option_id, { + value: option_value, + type: item.field_type, + id: 'form_field_' + i + '-' + x, + name: 'form_field_' + i + multiple + } ); + + if ( option_value === item.field_value ) { + view.addRenderAttribute( option_id, 'checked', 'checked' ); + } + + inputField += ' '; + inputField += ''; + + } + + inputField += '
    '; + } + break; + + case 'text': + case 'email': + case 'url': + case 'password': + case 'number': + case 'search': + itemClasses = 'elementor-field-textual ' + itemClasses; + inputField = ''; + break; + default: + item.placeholder = _.escape( item.placeholder ); + inputField = elementor.hooks.applyFilters( 'elementor_pro/forms/content_template/field/' + item.field_type, '', item, i, settings ); + } + + if ( inputField ) { + #> +
    + + <# if ( printLabel && item.field_label ) { #> + + <# } #> + + {{{ inputField }}} +
    + <# + } + } + + + var buttonClasses = 'elementor-field-group elementor-column elementor-field-type-submit e-form__buttons'; + + buttonClasses += ' elementor-col-' + ( ( '' !== settings.button_width ) ? settings.button_width : '100' ); + + if ( settings.button_width_tablet ) { + buttonClasses += ' elementor-md-' + settings.button_width_tablet; + } + + if ( settings.button_width_mobile ) { + buttonClasses += ' elementor-sm-' + settings.button_width_mobile; + } + + var iconHTML = elementor.helpers.renderIcon( view, settings.selected_button_icon, { 'aria-hidden': true }, 'i' , 'object' ), + migrated = elementor.helpers.isIconMigrated( settings, 'selected_button_icon' ); + + #> + +
    + +
    +
    +
    + start_controls_section( + 'section_fields_content', + [ + 'label' => esc_html__( 'Form Fields', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'show_labels', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'input_size', + [ + 'label' => esc_html__( 'Input Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'xs' => esc_html__( 'Extra Small', 'elementor-pro' ), + 'sm' => esc_html__( 'Small', 'elementor-pro' ), + 'md' => esc_html__( 'Medium', 'elementor-pro' ), + 'lg' => esc_html__( 'Large', 'elementor-pro' ), + 'xl' => esc_html__( 'Extra Large', 'elementor-pro' ), + ], + 'default' => 'sm', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_button_content', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_text', + [ + 'label' => esc_html__( 'Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Log In', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'xs' => esc_html__( 'Extra Small', 'elementor-pro' ), + 'sm' => esc_html__( 'Small', 'elementor-pro' ), + 'md' => esc_html__( 'Medium', 'elementor-pro' ), + 'lg' => esc_html__( 'Large', 'elementor-pro' ), + 'xl' => esc_html__( 'Extra Large', 'elementor-pro' ), + ], + 'default' => 'sm', + ] + ); + + $this->add_responsive_control( + 'align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'stretch' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'prefix_class' => 'elementor%s-button-align-', + 'default' => '', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_login_content', + [ + 'label' => esc_html__( 'Additional Options', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'redirect_after_login', + [ + 'label' => esc_html__( 'Redirect After Login', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'redirect_url', + [ + 'type' => Controls_Manager::URL, + 'show_label' => false, + 'options' => false, + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'description' => esc_html__( 'Note: Because of security reasons, you can ONLY use your current domain here.', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'redirect_after_login' => 'yes', + ], + ] + ); + + $this->add_control( + 'redirect_after_logout', + [ + 'label' => esc_html__( 'Redirect After Logout', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'redirect_logout_url', + [ + 'type' => Controls_Manager::URL, + 'show_label' => false, + 'options' => false, + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'description' => esc_html__( 'Note: Because of security reasons, you can ONLY use your current domain here.', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'redirect_after_logout' => 'yes', + ], + ] + ); + + $this->add_control( + 'show_lost_password', + [ + 'label' => esc_html__( 'Lost your password?', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + ] + ); + + if ( get_option( 'users_can_register' ) ) { + $this->add_control( + 'show_register', + [ + 'label' => esc_html__( 'Register', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + ] + ); + } + + $this->add_control( + 'show_remember_me', + [ + 'label' => esc_html__( 'Remember Me', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'show_logged_in_message', + [ + 'label' => esc_html__( 'Logged in Message', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'custom_labels', + [ + 'label' => esc_html__( 'Custom Label', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + ] + ); + + $this->add_control( + 'user_label', + [ + 'label' => esc_html__( 'Username Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Username or Email Address', 'elementor-pro' ), + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'show_labels', + 'operator' => '===', + 'value' => 'yes', + ], + [ + 'name' => 'custom_labels', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + ] + ); + + $this->add_control( + 'user_placeholder', + [ + 'label' => esc_html__( 'Username Placeholder', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Username or Email Address', 'elementor-pro' ), + 'condition' => [ + 'custom_labels' => 'yes', + ], + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'password_label', + [ + 'label' => esc_html__( 'Password Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Password', 'elementor-pro' ), + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'show_labels', + 'operator' => '===', + 'value' => 'yes', + ], + [ + 'name' => 'custom_labels', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + ] + ); + + $this->add_control( + 'password_placeholder', + [ + 'label' => esc_html__( 'Password Placeholder', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Password', 'elementor-pro' ), + 'condition' => [ + 'custom_labels' => 'yes', + ], + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style', + [ + 'label' => esc_html__( 'Form', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'row_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 10, + ], + 'range' => [ + 'px' => [ + 'max' => 60, + ], + 'em' => [ + 'max' => 6, + ], + 'rem' => [ + 'max' => 6, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group' => 'margin-bottom: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .elementor-form-fields-wrapper' => 'margin-bottom: -{{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'links_color', + [ + 'label' => esc_html__( 'Links Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group > a' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_control( + 'links_hover_color', + [ + 'label' => esc_html__( 'Links Hover Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group > a:hover' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_labels', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'show_labels!' => '', + ], + ] + ); + + $this->add_control( + 'label_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 0, + ], + 'range' => [ + 'px' => [ + 'max' => 60, + ], + 'em' => [ + 'max' => 6, + ], + 'rem' => [ + 'max' => 6, + ], + ], + 'selectors' => [ + 'body {{WRAPPER}} .elementor-field-group > label' => 'padding-bottom: {{SIZE}}{{UNIT}};', + // for the label position = above option + ], + ] + ); + + $this->add_control( + 'label_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-form-fields-wrapper label' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'label_typography', + 'selector' => '{{WRAPPER}} .elementor-form-fields-wrapper label', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_field_style', + [ + 'label' => esc_html__( 'Fields', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'field_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group .elementor-field' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'field_typography', + 'selector' => '{{WRAPPER}} .elementor-field-group .elementor-field, {{WRAPPER}} .elementor-field-subgroup label', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_control( + 'field_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group .elementor-field:not(.elementor-select-wrapper)' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .elementor-field-group .elementor-select-wrapper select' => 'background-color: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'field_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group .elementor-field:not(.elementor-select-wrapper)' => 'border-color: {{VALUE}};', + '{{WRAPPER}} .elementor-field-group .elementor-select-wrapper select' => 'border-color: {{VALUE}};', + '{{WRAPPER}} .elementor-field-group .elementor-select-wrapper::before' => 'color: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'field_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'placeholder' => '1', + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group .elementor-field:not(.elementor-select-wrapper)' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + '{{WRAPPER}} .elementor-field-group .elementor-select-wrapper select' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'field_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-field-group .elementor-field:not(.elementor-select-wrapper)' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + '{{WRAPPER}} .elementor-field-group .elementor-select-wrapper select' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_button_style', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->start_controls_tabs( 'tabs_button_style' ); + + $this->start_controls_tab( + 'tab_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-button' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'button_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}} .elementor-button', + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'button_background', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-button', + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + 'color' => [ + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), [ + 'name' => 'button_border', + 'selector' => '{{WRAPPER}} .elementor-button', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-button' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'button_text_padding', + [ + 'label' => esc_html__( 'Text Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_hover_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-button:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'button_background_hover', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-button:hover', + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + ], + ] + ); + + $this->add_control( + 'button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-button:hover' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + 'button_border_border!' => '', + ], + ] + ); + + $this->add_control( + 'button_hover_animation', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + ] + ); + + $this->add_control( + 'button_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-button' => 'transition-duration: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_message', + [ + 'label' => esc_html__( 'Logged in Message', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'show_logged_in_message' => 'yes', + ], + ] + ); + + $this->add_control( + 'message_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-widget-container .elementor-login__logged-in-message' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'condition' => [ + 'show_logged_in_message' => 'yes', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'message_typography', + 'selector' => '{{WRAPPER}} .elementor-widget-container .elementor-login__logged-in-message', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'condition' => [ + 'show_logged_in_message' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + + } + + private function form_fields_render_attributes() { + $settings = $this->get_settings_for_display(); + + if ( ! empty( $settings['button_size'] ) ) { + $this->add_render_attribute( 'button', 'class', 'elementor-size-' . $settings['button_size'] ); + } + + if ( $settings['button_hover_animation'] ) { + $this->add_render_attribute( 'button', 'class', 'elementor-animation-' . $settings['button_hover_animation'] ); + } + + $this->add_render_attribute( + [ + 'wrapper' => [ + 'class' => [ + 'elementor-form-fields-wrapper', + ], + ], + 'field-group' => [ + 'class' => [ + 'elementor-field-type-text', + 'elementor-field-group', + 'elementor-column', + 'elementor-col-100', + ], + ], + 'submit-group' => [ + 'class' => [ + 'elementor-field-group', + 'elementor-column', + 'elementor-field-type-submit', + 'elementor-col-100', + ], + ], + 'button' => [ + 'class' => [ + 'elementor-button', + ], + 'name' => 'wp-submit', + ], + 'user_label' => [ + 'for' => 'user-' . $this->get_id(), + 'class' => 'elementor-field-label', + ], + 'user_input' => [ + 'size' => '1', + 'type' => 'text', + 'name' => 'log', + 'id' => 'user-' . $this->get_id(), + 'placeholder' => $settings['user_placeholder'], + 'class' => [ + 'elementor-field', + 'elementor-field-textual', + 'elementor-size-' . $settings['input_size'], + ], + ], + 'password_label' => [ + 'for' => 'password-' . $this->get_id(), + 'class' => 'elementor-field-label', + ], + 'password_input' => [ + 'size' => '1', + 'type' => 'password', + 'name' => 'pwd', + 'id' => 'password-' . $this->get_id(), + 'placeholder' => $settings['password_placeholder'], + 'class' => [ + 'elementor-field', + 'elementor-field-textual', + 'elementor-size-' . $settings['input_size'], + ], + ], + ] + ); + + if ( ! $settings['show_labels'] ) { + $this->add_render_attribute( 'user_label', 'class', 'elementor-screen-only' ); + $this->add_render_attribute( 'password_label', 'class', 'elementor-screen-only' ); + } + + $this->add_render_attribute( 'field-group', 'class', 'elementor-field-required' ) + ->add_render_attribute( 'input', 'required', true ) + ->add_render_attribute( 'input', 'aria-required', 'true' ); + + } + + protected function render() { + $settings = $this->get_settings_for_display(); + $current_url = remove_query_arg( 'fake_arg' ); + $logout_redirect = $current_url; + + if ( 'yes' === $settings['redirect_after_login'] && ! empty( $settings['redirect_url']['url'] ) ) { + $redirect_url = $settings['redirect_url']['url']; + } else { + $redirect_url = $current_url; + } + + if ( 'yes' === $settings['redirect_after_logout'] && ! empty( $settings['redirect_logout_url']['url'] ) ) { + $logout_redirect = $settings['redirect_logout_url']['url']; + } + + if ( is_user_logged_in() && ! Plugin::elementor()->editor->is_edit_mode() ) { + if ( 'yes' === $settings['show_logged_in_message'] ) { + $current_user = wp_get_current_user(); + + // PHPCS - `sprintf` is safe. + echo ''; + } + + return; + } + + $this->form_fields_render_attributes(); + ?> + + + + get_settings_for_display( 'gallery_type' ) ) { + return [ 'nav-menu' ]; + } + + return []; + } + + protected function register_controls() { + $this->start_controls_section( 'settings', [ 'label' => esc_html__( 'Settings', 'elementor-pro' ) ] ); + + $this->add_control( + 'gallery_type', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'default' => 'single', + 'options' => [ + 'single' => esc_html__( 'Single', 'elementor-pro' ), + 'multiple' => esc_html__( 'Multiple', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'gallery', + [ + 'type' => Controls_Manager::GALLERY, + 'condition' => [ + 'gallery_type' => 'single', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater = new Repeater(); + + $repeater->add_control( + 'gallery_title', + [ + 'type' => Controls_Manager::TEXT, + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'default' => esc_html__( 'New Gallery', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'multiple_gallery', + [ + 'type' => Controls_Manager::GALLERY, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'galleries', + [ + 'type' => Controls_Manager::REPEATER, + 'label' => esc_html__( 'Galleries', 'elementor-pro' ), + 'fields' => $repeater->get_controls(), + 'title_field' => '{{{ gallery_title }}}', + 'default' => [ + [ + 'gallery_title' => esc_html__( 'New Gallery', 'elementor-pro' ), + ], + ], + 'condition' => [ + 'gallery_type' => 'multiple', + ], + ] + ); + + $this->add_control( + 'order_by', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Order By', 'elementor-pro' ), + 'options' => [ + '' => esc_html__( 'Default', 'elementor-pro' ), + 'random' => esc_html__( 'Random', 'elementor-pro' ), + ], + 'default' => '', + ] + ); + + $this->add_control( + 'lazyload', + [ + 'type' => Controls_Manager::SWITCHER, + 'label' => esc_html__( 'Lazy Load', 'elementor-pro' ), + 'return_value' => 'yes', + 'default' => 'yes', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'gallery_layout', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'default' => 'grid', + 'options' => [ + 'grid' => esc_html__( 'Grid', 'elementor-pro' ), + 'justified' => esc_html__( 'Justified', 'elementor-pro' ), + 'masonry' => esc_html__( 'Masonry', 'elementor-pro' ), + ], + 'separator' => 'before', + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( + 'columns', + [ + 'label' => esc_html__( 'Columns', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 4, + 'tablet_default' => 2, + 'mobile_default' => 1, + 'min' => 1, + 'max' => 24, + 'condition' => [ + 'gallery_layout!' => 'justified', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $active_breakpoints = Plugin::elementor()->breakpoints->get_active_breakpoints(); + $ideal_row_height_device_args = []; + $gap_device_args = []; + + // Add default values for all active breakpoints. + foreach ( $active_breakpoints as $breakpoint_name => $breakpoint_instance ) { + if ( 'widescreen' !== $breakpoint_name ) { + $ideal_row_height_device_args[ $breakpoint_name ] = [ + 'default' => [ + 'size' => 150, + ], + ]; + + $gap_device_args[ $breakpoint_name ] = [ + 'default' => [ + 'size' => 10, + ], + ]; + } + } + + $this->add_responsive_control( + 'ideal_row_height', + [ + 'label' => esc_html__( 'Row Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 50, + 'max' => 500, + ], + ], + 'default' => [ + 'size' => 200, + ], + 'device_args' => $ideal_row_height_device_args, + 'condition' => [ + 'gallery_layout' => 'justified', + ], + 'required' => true, + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( + 'gap', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 10, + ], + 'device_args' => $gap_device_args, + 'required' => true, + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'link_to', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'file', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'file' => esc_html__( 'Media File', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom URL', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'url', + [ + 'label' => esc_html__( 'URL', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'condition' => [ + 'link_to' => 'custom', + ], + 'frontend_available' => true, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'open_lightbox', + [ + 'label' => esc_html__( 'Lightbox', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'description' => sprintf( + /* translators: 1: Link open tag, 2: Link close tag. */ + esc_html__( 'Manage your site’s lightbox settings in the %1$sLightbox panel%2$s.', 'elementor-pro' ), + '', + '' + ), + 'default' => 'default', + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'yes' => esc_html__( 'Yes', 'elementor-pro' ), + 'no' => esc_html__( 'No', 'elementor-pro' ), + ], + 'condition' => [ + 'link_to' => 'file', + ], + ] + ); + + $this->add_control( + 'aspect_ratio', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Aspect Ratio', 'elementor-pro' ), + 'default' => '3:2', + 'options' => [ + '1:1' => '1:1', + '3:2' => '3:2', + '4:3' => '4:3', + '9:16' => '9:16', + '16:9' => '16:9', + '21:9' => '21:9', + ], + 'condition' => [ + 'gallery_layout' => 'grid', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->add_group_control( + Group_Control_Image_Size::get_type(), + [ + 'name' => 'thumbnail_image', + 'default' => 'medium', + ] + ); + + $this->end_controls_section(); // settings + + $this->start_controls_section( + 'section_filter_bar_content', + [ + 'label' => esc_html__( 'Filter Bar', 'elementor-pro' ), + 'condition' => [ + 'gallery_type' => 'multiple', + ], + ] + ); + + $this->add_control( + 'show_all_galleries', + [ + 'type' => Controls_Manager::SWITCHER, + 'label' => esc_html__( '"All" Filter', 'elementor-pro' ), + 'default' => 'yes', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'show_all_galleries_label', + [ + 'type' => Controls_Manager::TEXT, + 'label' => esc_html__( '"All" Filter Label', 'elementor-pro' ), + 'default' => esc_html__( 'All', 'elementor-pro' ), + 'condition' => [ + 'show_all_galleries' => 'yes', + ], + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'pointer', + [ + 'label' => esc_html__( 'Pointer', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'underline', + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'underline' => esc_html__( 'Underline', 'elementor-pro' ), + 'overline' => esc_html__( 'Overline', 'elementor-pro' ), + 'double-line' => esc_html__( 'Double Line', 'elementor-pro' ), + 'framed' => esc_html__( 'Framed', 'elementor-pro' ), + 'background' => esc_html__( 'Background', 'elementor-pro' ), + 'text' => esc_html__( 'Text', 'elementor-pro' ), + ], + 'style_transfer' => true, + ] + ); + + $this->add_control( + 'animation_line', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'fade', + 'options' => [ + 'fade' => 'Fade', + 'slide' => 'Slide', + 'grow' => 'Grow', + 'drop-in' => 'Drop In', + 'drop-out' => 'Drop Out', + 'none' => 'None', + ], + 'condition' => [ + 'pointer' => [ 'underline', 'overline', 'double-line' ], + ], + ] + ); + + $this->add_control( + 'animation_framed', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'fade', + 'options' => [ + 'fade' => 'Fade', + 'grow' => 'Grow', + 'shrink' => 'Shrink', + 'draw' => 'Draw', + 'corners' => 'Corners', + 'none' => 'None', + ], + 'condition' => [ + 'pointer' => 'framed', + ], + ] + ); + + $this->add_control( + 'animation_background', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'fade', + 'options' => [ + 'fade' => 'Fade', + 'grow' => 'Grow', + 'shrink' => 'Shrink', + 'sweep-left' => 'Sweep Left', + 'sweep-right' => 'Sweep Right', + 'sweep-up' => 'Sweep Up', + 'sweep-down' => 'Sweep Down', + 'shutter-in-vertical' => 'Shutter In Vertical', + 'shutter-out-vertical' => 'Shutter Out Vertical', + 'shutter-in-horizontal' => 'Shutter In Horizontal', + 'shutter-out-horizontal' => 'Shutter Out Horizontal', + 'none' => 'None', + ], + 'condition' => [ + 'pointer' => 'background', + ], + ] + ); + + $this->add_control( + 'animation_text', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'grow', + 'options' => [ + 'grow' => 'Grow', + 'shrink' => 'Shrink', + 'sink' => 'Sink', + 'float' => 'Float', + 'skew' => 'Skew', + 'rotate' => 'Rotate', + 'none' => 'None', + ], + 'condition' => [ + 'pointer' => 'text', + ], + ] + ); + + $this->end_controls_section(); // settings + + $this->start_controls_section( 'overlay', [ 'label' => esc_html__( 'Overlay', 'elementor-pro' ) ] ); + + $this->add_control( + 'overlay_background', + [ + 'label' => esc_html__( 'Background', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'overlay_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'title' => esc_html__( 'Title', 'elementor-pro' ), + 'caption' => esc_html__( 'Caption', 'elementor-pro' ), + 'alt' => esc_html__( 'Alt', 'elementor-pro' ), + 'description' => esc_html__( 'Description', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'overlay_description', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'title' => esc_html__( 'Title', 'elementor-pro' ), + 'caption' => esc_html__( 'Caption', 'elementor-pro' ), + 'alt' => esc_html__( 'Alt', 'elementor-pro' ), + 'description' => esc_html__( 'Description', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->end_controls_section(); // overlay + + $this->start_controls_section( + 'image_style', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->start_controls_tabs( 'image_tabs' ); + + $this->start_controls_tab( + 'image_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'image_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--image-border-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'image_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'em' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--image-border-width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'image_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--image-border-radius: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Css_Filter::get_type(), + [ + 'name' => 'image_css_filters', + 'selector' => '{{WRAPPER}} .e-gallery-image', + ] + ); + + $this->end_controls_tab(); // overlay_background normal + + $this->start_controls_tab( + 'image_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'image_border_color_hover', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-gallery-item:hover' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'image_border_radius_hover', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-gallery-item:hover' => 'border-radius: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Css_Filter::get_type(), + [ + 'name' => 'image_css_filters_hover', + 'selector' => '{{WRAPPER}} .e-gallery-item:hover .e-gallery-image', + ] + ); + + $this->end_controls_tab(); // overlay_background normal + + $this->end_controls_tabs();// overlay_background tabs + + $this->add_control( + 'image_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => 'None', + 'grow' => 'Zoom In', + 'shrink-contained' => 'Zoom Out', + 'move-contained-left' => 'Move Left', + 'move-contained-right' => 'Move Right', + 'move-contained-top' => 'Move Up', + 'move-contained-bottom' => 'Move Down', + ], + 'separator' => 'before', + 'default' => '', + 'frontend_available' => true, + 'render_type' => 'ui', + ] + ); + + $this->add_control( + 'image_animation_duration', + [ + 'label' => esc_html__( 'Animation Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 800, + ], + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--image-transition-duration: {{SIZE}}ms', + ], + ] + ); + + $this->end_controls_section(); // overlay_background + + $this->start_controls_section( + 'overlay_style', + [ + 'label' => esc_html__( 'Overlay', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'overlay_background' => 'yes', + ], + ] + ); + + $this->start_controls_tabs( 'overlay_background_tabs' ); + + $this->start_controls_tab( + 'overlay_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'overlay_background', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-gallery-item__overlay', + 'fields_options' => [ + 'background' => [ + 'label' => esc_html__( 'Overlay', 'elementor-pro' ), + ], + ], + ] + ); + + $this->end_controls_tab(); // overlay_background normal + + $this->start_controls_tab( + 'overlay_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'overlay_background_hover', + 'types' => [ 'classic', 'gradient' ], + 'selector' => '{{WRAPPER}} .e-gallery-item:hover .elementor-gallery-item__overlay, {{WRAPPER}} .e-gallery-item:focus .elementor-gallery-item__overlay', + 'exclude' => [ 'image' ], + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + 'color' => [ + 'default' => 'rgba(0,0,0,0.5)', + ], + ], + ] + ); + + $this->end_controls_tab(); // overlay_background normal + + $this->end_controls_tabs();// overlay_background tabs + + $this->add_control( + 'image_blend_mode', + [ + 'label' => esc_html__( 'Blend Mode', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'Normal', 'elementor-pro' ), + 'multiply' => 'Multiply', + 'screen' => 'Screen', + 'overlay' => 'Overlay', + 'darken' => 'Darken', + 'lighten' => 'Lighten', + 'color-dodge' => 'Color Dodge', + 'color-burn' => 'Color Burn', + 'hue' => 'Hue', + 'saturation' => 'Saturation', + 'color' => 'Color', + 'exclusion' => 'Exclusion', + 'luminosity' => 'Luminosity', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--overlay-mix-blend-mode: {{VALUE}}', + ], + 'separator' => 'before', + 'render_type' => 'ui', + ] + ); + + $this->add_control( + 'background_overlay_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => [ + [ + 'label' => esc_html__( 'None', 'elementor-pro' ), + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + ], + ], + [ + 'label' => esc_html__( 'Entrance', 'elementor-pro' ), + 'options' => [ + 'enter-from-right' => 'Slide In Right', + 'enter-from-left' => 'Slide In Left', + 'enter-from-top' => 'Slide In Up', + 'enter-from-bottom' => 'Slide In Down', + 'enter-zoom-in' => 'Zoom In', + 'enter-zoom-out' => 'Zoom Out', + 'fade-in' => 'Fade In', + ], + ], + [ + 'label' => esc_html__( 'Exit', 'elementor-pro' ), + 'options' => [ + 'exit-to-right' => 'Slide Out Right', + 'exit-to-left' => 'Slide Out Left', + 'exit-to-top' => 'Slide Out Up', + 'exit-to-bottom' => 'Slide Out Down', + 'exit-zoom-in' => 'Zoom In', + 'exit-zoom-out' => 'Zoom Out', + 'fade-out' => 'Fade Out', + ], + ], + ], + 'separator' => 'before', + 'default' => '', + 'frontend_available' => true, + 'render_type' => 'ui', + ] + ); + + $this->add_control( + 'background_overlay_animation_duration', + [ + 'label' => esc_html__( 'Animation Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 800, + ], + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--overlay-transition-duration: {{SIZE}}ms', + ], + ] + ); + + $this->end_controls_section(); // overlay_background + + $this->start_controls_section( + 'overlay_content_style', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + //TODO: add conditions for this section + ] + ); + + $this->add_control( + 'content_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'default' => 'center', + 'selectors' => [ + '{{WRAPPER}}' => '--content-text-align: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'content_vertical_position', + [ + 'label' => esc_html__( 'Vertical Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'middle' => [ + 'title' => esc_html__( 'Middle', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'selectors_dictionary' => [ + 'top' => 'flex-start', + 'middle' => 'center', + 'bottom' => 'flex-end', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--content-justify-content: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'content_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'size' => 20, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--content-padding: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'heading_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'overlay_title!' => '', + ], + ] + ); + + $this->add_control( + 'title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--title-text-color: {{VALUE}}', + ], + 'condition' => [ + 'overlay_title!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'title_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-gallery-item__title', + 'condition' => [ + 'overlay_title!' => '', + ], + ] + ); + + $this->add_control( + 'title_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--description-margin-top: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'overlay_title!' => '', + ], + ] + ); + + $this->add_control( + 'heading_description', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'overlay_description!' => '', + ], + ] + ); + + $this->add_control( + 'description_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--description-text-color: {{VALUE}}', + ], + 'condition' => [ + 'overlay_description!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'description_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-gallery-item__description', + 'condition' => [ + 'overlay_description!' => '', + ], + ] + ); + + $this->add_control( + 'content_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'groups' => [ + [ + 'label' => esc_html__( 'None', 'elementor-pro' ), + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + ], + ], + [ + 'label' => esc_html__( 'Entrance', 'elementor-pro' ), + 'options' => [ + 'enter-from-right' => 'Slide In Right', + 'enter-from-left' => 'Slide In Left', + 'enter-from-top' => 'Slide In Up', + 'enter-from-bottom' => 'Slide In Down', + 'enter-zoom-in' => 'Zoom In', + 'enter-zoom-out' => 'Zoom Out', + 'fade-in' => 'Fade In', + ], + ], + [ + 'label' => esc_html__( 'Reaction', 'elementor-pro' ), + 'options' => [ + 'grow' => 'Grow', + 'shrink' => 'Shrink', + 'move-right' => 'Move Right', + 'move-left' => 'Move Left', + 'move-up' => 'Move Up', + 'move-down' => 'Move Down', + ], + ], + [ + 'label' => esc_html__( 'Exit', 'elementor-pro' ), + 'options' => [ + 'exit-to-right' => 'Slide Out Right', + 'exit-to-left' => 'Slide Out Left', + 'exit-to-top' => 'Slide Out Up', + 'exit-to-bottom' => 'Slide Out Down', + 'exit-zoom-in' => 'Zoom In', + 'exit-zoom-out' => 'Zoom Out', + 'fade-out' => 'Fade Out', + ], + ], + ], + 'default' => 'fade-in', + 'separator' => 'before', + 'render_type' => 'ui', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'content_animation_duration', + [ + 'label' => esc_html__( 'Animation Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 800, + ], + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--content-transition-duration: {{SIZE}}ms; --content-transition-delay: {{SIZE}}ms;', + ], + 'condition' => [ + 'content_hover_animation!' => '', + ], + ] + ); + + $this->add_control( + 'content_sequenced_animation', + [ + 'label' => esc_html__( 'Sequenced Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'content_hover_animation!' => '', + ], + 'frontend_available' => true, + 'render_type' => 'ui', + ] + ); + + $this->end_controls_section(); // overlay_content + + $this->start_controls_section( + 'filter_bar_style', + [ + 'label' => esc_html__( 'Filter Bar', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'gallery_type' => 'multiple', + ], + ] + ); + + $this->add_control( + 'align_filter_bar_items', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'prefix_class' => 'elementor-gallery--filter-align-', + 'selectors_dictionary' => [ + 'left' => 'flex-start', + 'right' => 'flex-end', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--titles-container-justify-content: {{VALUE}}', + ], + ] + ); + + $this->start_controls_tabs( 'filter_bar_colors' ); + + $this->start_controls_tab( 'filter_bar_colors_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'galleries_title_color_normal', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--galleries-title-color-normal: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'galleries_titles_typography', + 'selector' => '{{WRAPPER}} .elementor-gallery-title', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + ] + ); + + $this->end_controls_tab();// filter_bar_colors_normal + + $this->start_controls_tab( 'filter_bar_colors_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'galleries_title_color_hover', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--galleries-title-color-hover: {{VALUE}}', + ], + 'condition' => [ + 'pointer!' => 'background', + ], + ] + ); + + /* + When the pointer style = background, users could need a different text color. + The control handles the title color in hover state, only when the pointer style is background. + */ + $this->add_control( + 'galleries_title_color_hover_pointer_bg', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#fff', + 'selectors' => [ + '{{WRAPPER}}' => '--galleries-title-color-hover: {{VALUE}}', + ], + 'condition' => [ + 'pointer' => 'background', + ], + ] + ); + + $this->add_control( + 'galleries_pointer_color_hover', + [ + 'label' => esc_html__( 'Pointer Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--galleries-pointer-bg-color-hover: {{VALUE}}', + ], + 'condition' => [ + 'pointer!' => [ 'none', 'text' ], + ], + ] + ); + + $this->end_controls_tab();// filter_bar_colors_hover + + $this->start_controls_tab( 'filter_bar_colors_active', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'galleries_title_color_active', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--gallery-title-color-active: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'galleries_pointer_color_active', + [ + 'label' => esc_html__( 'Pointer Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--galleries-pointer-bg-color-active: {{VALUE}}', + ], + 'condition' => [ + 'pointer!' => [ 'none', 'text' ], + ], + + ] + ); + + $this->end_controls_tab();// filter_bar_colors_active + + $this->end_controls_tabs(); // filter_bar_colors + + $this->add_control( + 'pointer_width', + [ + 'label' => esc_html__( 'Pointer Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'devices' => [ Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP, Breakpoints_Manager::BREAKPOINT_KEY_TABLET ], + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 30, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--galleries-pointer-border-width: {{SIZE}}{{UNIT}}', + ], + 'separator' => 'before', + 'condition' => [ + 'pointer' => [ 'underline', 'overline', 'double-line', 'framed' ], + ], + ] + ); + + $this->add_control( + 'galleries_titles_space_between', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-gallery-title' => '--space-between: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'galleries_titles_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-gallery__titles-container' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); // filter_bar_style + } + + protected function render_static() { + $settings = $this->get_settings_for_display(); + + $is_multiple = 'multiple' === $settings['gallery_type'] && ! empty( $settings['galleries'] ); + + $is_single = 'single' === $settings['gallery_type'] && ! empty( $settings['gallery'] ); + + $gap = $settings['gap']['size'] . $settings['gap']['unit']; + $ratio_percentage = '75'; + $columns = 4; + + if ( $settings['columns'] ) { + $columns = $settings['columns']; + } + + if ( $settings['aspect_ratio'] ) { + $ratio_array = explode( ':', $settings['aspect_ratio'] ); + + $ratio_percentage = ( $ratio_array[1] / $ratio_array[0] ) * 100; + } + + $this->add_render_attribute( + 'gallery_container', + [ + 'style' => "--columns: {$columns}; --aspect-ratio: {$ratio_percentage}%; --hgap: {$gap}; --vgap: {$gap};", + 'class' => 'e-gallery-grid', + ] + ); + + $galleries = []; + + if ( $is_multiple ) { + foreach ( array_values( $settings['galleries'] ) as $multi_gallery ) { + if ( ! $multi_gallery['multiple_gallery'] ) { + continue; + } + + $galleries[] = $multi_gallery['multiple_gallery']; + } + } elseif ( $is_single ) { + $galleries[0] = $settings['gallery']; + } + + foreach ( $galleries as $gallery ) { + foreach ( $gallery as $item ) { + $image_src = wp_get_attachment_image_src( $item['id'] ); + + $this->add_render_attribute( 'gallery_item_image_' . $item['id'], [ + 'style' => "background-image: url('{$image_src[0]}');", + ] ); + } + } + + $this->render(); + } + + /** + * + */ + protected function render() { + $settings = $this->get_settings_for_display(); + + $is_multiple = 'multiple' === $settings['gallery_type'] && ! empty( $settings['galleries'] ); + + $is_single = 'single' === $settings['gallery_type'] && ! empty( $settings['gallery'] ); + + $has_description = ! empty( $settings['overlay_description'] ); + + $has_title = ! empty( $settings['overlay_title'] ); + + $has_animation = ! empty( $settings['image_hover_animation'] ) || ! empty( $settings['content_hover_animation'] ) || ! empty( $settings['background_overlay_hover_animation'] ); + + $gallery_item_tag = ! empty( $settings['link_to'] ) ? 'a' : 'div'; + + $galleries = []; + + if ( $is_multiple ) { + $this->add_render_attribute( + 'titles-container', + [ + 'class' => 'elementor-gallery__titles-container', + 'aria-label' => esc_html__( 'Gallery filter', 'elementor-pro' ), + ] + ); + + if ( $settings['pointer'] ) { + $this->add_render_attribute( 'titles-container', 'class', 'e--pointer-' . $settings['pointer'] ); + + foreach ( $settings as $key => $value ) { + if ( 0 === strpos( $key, 'animation' ) && $value ) { + $this->add_render_attribute( 'titles-container', 'class', 'e--animation-' . $value ); + break; + } + } + } ?> +
    print_render_attribute_string( 'titles-container' ); ?>> + + + print_unescaped_setting( 'show_all_galleries_label' ); ?> + + + + $gallery ) : + if ( ! $gallery['multiple_gallery'] ) { + continue; + } + + $galleries[ $index ] = $gallery['multiple_gallery']; + ?> + + print_unescaped_setting( 'gallery_title', 'galleries', $index ); ?> + + +
    + editor->is_edit_mode() ) { ?> + + add_render_attribute( 'gallery_container', 'class', 'elementor-gallery__container' ); + + if ( $has_title || $has_description ) { + $this->add_render_attribute( 'gallery_item_content', 'class', 'elementor-gallery-item__content' ); + + if ( $has_title ) { + $this->add_render_attribute( 'gallery_item_title', 'class', 'elementor-gallery-item__title' ); + } + + if ( $has_description ) { + $this->add_render_attribute( 'gallery_item_description', 'class', 'elementor-gallery-item__description' ); + } + } + + $this->add_render_attribute( 'gallery_item_background_overlay', [ 'class' => 'elementor-gallery-item__overlay' ] ); + + $gallery_items = []; + $thumbnail_size = $settings['thumbnail_image_size']; + foreach ( $galleries as $gallery_index => $gallery ) { + foreach ( $gallery as $index => $item ) { + if ( in_array( $item['id'], array_keys( $gallery_items ), true ) ) { + $gallery_items[ $item['id'] ][] = $gallery_index; + } else { + $gallery_items[ $item['id'] ] = [ $gallery_index ]; + } + } + } + + if ( 'random' === $settings['order_by'] ) { + $shuffled_items = []; + $keys = array_keys( $gallery_items ); + shuffle( $keys ); + foreach ( $keys as $key ) { + $shuffled_items[ $key ] = $gallery_items[ $key ]; + } + $gallery_items = $shuffled_items; + } + + if ( ! empty( $galleries ) ) { ?> +
    print_render_attribute_string( 'gallery_container' ); ?>> + $tags ) : + $unique_index = $id; //$gallery_index . '_' . $index; + $image_src = wp_get_attachment_image_src( $id, $thumbnail_size ); + if ( ! $image_src ) { + continue; + } + $attachment = get_post( $id ); + $image_data = [ + 'alt' => get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true ), + 'media' => wp_get_attachment_image_src( $id, 'full' )['0'], + 'src' => $image_src['0'], + 'width' => $image_src['1'], + 'height' => $image_src['2'], + 'caption' => $attachment->post_excerpt, + 'description' => $attachment->post_content, + 'title' => $attachment->post_title, + ]; + + $this->add_render_attribute( 'gallery_item_' . $unique_index, [ + 'class' => [ + 'e-gallery-item', + 'elementor-gallery-item', + ], + ] ); + + if ( $has_animation ) { + $this->add_render_attribute( 'gallery_item_' . $unique_index, [ 'class' => 'elementor-animated-content' ] ); + } + + if ( $is_multiple ) { + $this->add_render_attribute( 'gallery_item_' . $unique_index, [ 'data-e-gallery-tags' => implode( ',', $tags ) ] ); + } + + if ( $has_title && 'div' === $gallery_item_tag ) { + $this->add_render_attribute( 'gallery_item_' . $unique_index, [ 'tabindex' => '0' ] ); + } + + if ( 'a' === $gallery_item_tag ) { + if ( 'file' === $settings['link_to'] ) { + $href = $image_data['media']; + + $this->add_render_attribute( 'gallery_item_' . $unique_index, [ + 'href' => esc_url( $href ), + ] ); + + if ( Plugin::elementor()->editor->is_edit_mode() ) { + $this->add_render_attribute( 'gallery_item_' . $unique_index, 'class', 'elementor-clickable' ); + } + + $this->add_lightbox_data_attributes( 'gallery_item_' . $unique_index, $id, $settings['open_lightbox'], $this->get_id() ); + } elseif ( 'custom' === $settings['link_to'] ) { + $this->add_link_attributes( 'gallery_item_' . $unique_index, $settings['url'] ); + } + } + + $this->add_render_attribute( 'gallery_item_image_' . $unique_index, + [ + 'class' => [ + 'e-gallery-image', + 'elementor-gallery-item__image', + ], + 'data-thumbnail' => $image_data['src'], + 'data-width' => $image_data['width'], + 'data-height' => $image_data['height'], + 'aria-label' => $image_data['alt'], + 'role' => 'img', + ] + );?> + < print_render_attribute_string( 'gallery_item_' . $unique_index ); ?>> +
    print_render_attribute_string( 'gallery_item_image_' . $unique_index ); ?> >
    + +
    print_render_attribute_string( 'gallery_item_background_overlay' ); ?>>
    + + +
    print_render_attribute_string( 'gallery_item_content' ); ?>> + +
    print_render_attribute_string( 'gallery_item_title' ); ?>> + + +
    + +
    print_render_attribute_string( 'gallery_item_description' ); ?>> + + +
    + +
    + + > + +
    + get_param( 'ids' ); + + if ( ! empty( $ids ) ) { + // TODO: This logic should be handled at REST API. + $ids = explode( ',', $ids ); + + foreach ( $ids as $template_id ) { + $template_data = Plugin::elementor()->templates_manager->get_template_data( [ + 'source' => 'local', + 'template_id' => $template_id, + ] ); + + if ( ! empty( $template_data ) ) { + $result[ $template_id ] = $template_data['content'][0]; + } + } + } + + return $result; + } + + public function get_permission_callback( $request ) { + return current_user_can( 'edit_posts' ); + } +} diff --git a/modules/global-widget/documents/widget.php b/modules/global-widget/documents/widget.php new file mode 100644 index 0000000..e568e05 --- /dev/null +++ b/modules/global-widget/documents/widget.php @@ -0,0 +1,63 @@ + static::get_type(), + ] ); + } + + public function is_editable_by_current_user() { + return User::is_current_user_can_edit( $this->get_main_id() ); + } + + public function import( array $data ) { + parent::import( $data ); + + $this->update_main_meta( Module::WIDGET_TYPE_META_KEY, $data['content'][0]['widgetType'] ); + } + + public function save( $data ) { + // Since the method of 'modules/usage::before_document_save' will remove from global if new_status is the same as old. + $data['settings'] = [ 'post_status' => Document::STATUS_PUBLISH ]; + + return parent::save( $data ); + } +} diff --git a/modules/global-widget/module.php b/modules/global-widget/module.php new file mode 100644 index 0000000..80eac04 --- /dev/null +++ b/modules/global-widget/module.php @@ -0,0 +1,266 @@ + 'Global_Widget', + ]; + + const LICENSE_FEATURE_NAME = 'global-widget'; + + public function __construct() { + parent::__construct(); + + $this->add_hooks(); + + Plugin::elementor()->data_manager->register_controller_instance( new Data\Controller() ); + } + + public function get_widgets() { + return API::filter_active_features( static::WIDGET_NAME_CLASS_NAME_MAP ); + } + + public function get_name() { + return 'global-widget'; + } + + public function add_templates_localize_data( $settings ) { + $elementor = Plugin::elementor(); + + $templates_manager = $elementor->templates_manager; + + $widgets_types = $elementor->widgets_manager->get_widget_types(); + + $widget_templates = array_filter( $templates_manager->get_source( 'local' )->get_items( [ 'type' => self::TEMPLATE_TYPE ] ), function( $template ) use ( $widgets_types ) { + if ( empty( $template['widgetType'] ) || empty( $widgets_types[ $template['widgetType'] ] ) ) { + return false; + } + + // Open the stack in order to include the widget controls in initial editor config + $widgets_types[ $template['widgetType'] ]->get_stack( false ); + + return true; + } ); + + $widget_templates_content = []; + + foreach ( $widget_templates as $widget_template ) { + $widget_templates_content[ $widget_template['template_id'] ] = [ + 'elType' => 'widget', + 'title' => $widget_template['title'], + 'widgetType' => $widget_template['widgetType'], + ]; + } + + $settings = array_replace_recursive( $settings, [ + 'widget_templates' => $widget_templates_content, + 'should_show_promotion' => ! API::is_licence_has_feature( static::LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ), + ] ); + + return $settings; + } + + public function set_template_widget_type_meta( $post_id, $template_data ) { + if ( self::TEMPLATE_TYPE === $template_data['type'] ) { + update_post_meta( $post_id, self::WIDGET_TYPE_META_KEY, $template_data['content'][0]['widgetType'] ); + } + } + + public function on_template_update( $template_id, $template_data ) { + if ( self::TEMPLATE_TYPE !== $template_data['type'] ) { + return; + } + + $this->delete_included_posts_css( $template_id ); + } + + public function filter_template_data( $data ) { + if ( self::TEMPLATE_TYPE === $data['type'] ) { + $data['widgetType'] = get_post_meta( $data['template_id'], self::WIDGET_TYPE_META_KEY, true ); + } + + return $data; + } + + public function get_element_child_type( Element_Base $default_child_type, array $element_data ) { + if ( isset( $element_data['templateID'] ) ) { + $template_post = get_post( $element_data['templateID'] ); + + if ( ! $template_post || 'trash' === $template_post->post_status ) { + return false; + } + } + + return $default_child_type; + } + + public function is_post_type_support_elementor( $is_supported, $post_id, $post_type ) { + if ( ! $is_supported || Source_Local::CPT !== $post_type ) { + return $is_supported; + } + + $is_widget_template = $this->is_widget_template( $post_id ); + + // FIX ME: Change `get_current_screen()` condition to better way. + if ( $is_widget_template && function_exists( 'get_current_screen' ) ) { + $screen = get_current_screen(); + + if ( ! empty( $screen->id ) && in_array( $screen->id, [ 'elementor_library', 'edit-elementor_library' ] ) ) { + $is_supported = false; + } + } + + return $is_supported; + } + + public function is_template_supports_export( $default_value, $template_id ) { + return $default_value && ! $this->is_widget_template( $template_id ); + } + + /** + * Remove user edit capabilities. + * + * Filters the user capabilities to disable editing in admin. + * + * @param array $allcaps An array of all the user's capabilities. + * @param array $caps Actual capabilities for meta capability. + * @param array $args Optional parameters passed to has_cap(), typically object ID. + * + * @return array + * @deprecated 3.1.0 Use `Plugin::elementor()->documents->remove_user_edit_cap()` instead. + */ + public function remove_user_edit_cap( $allcaps, $caps, $args ) { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0', 'Plugin::elementor()->documents->remove_user_edit_cap()' ); + + return Plugin::elementor()->documents->remove_user_edit_cap( $allcaps, $caps, $args ); + } + + public function is_widget_template( $template_id ) { + $template_type = Source_Local::get_template_type( $template_id ); + + return self::TEMPLATE_TYPE === $template_type; + } + + public function set_global_widget_included_posts_list( $post_id, $editor_data ) { + $global_widget_ids = []; + + Plugin::elementor()->db->iterate_data( $editor_data, function( $element_data ) use ( &$global_widget_ids ) { + if ( isset( $element_data['templateID'] ) ) { + $global_widget_ids[] = $element_data['templateID']; + } + } ); + + foreach ( $global_widget_ids as $widget_id ) { + $included_posts = get_post_meta( $widget_id, self::INCLUDED_POSTS_LIST_META_KEY, true ); + + if ( ! is_array( $included_posts ) ) { + $included_posts = []; + } + + $included_posts[ $post_id ] = true; + + update_post_meta( $widget_id, self::INCLUDED_POSTS_LIST_META_KEY, $included_posts ); + } + } + + private function delete_included_posts_css( $template_id ) { + $including_post_ids = (array) get_post_meta( $template_id, self::INCLUDED_POSTS_LIST_META_KEY, true ); + + if ( empty( $including_post_ids ) ) { + return; + } + + foreach ( array_keys( $including_post_ids ) as $post_id ) { + delete_post_meta( $post_id, '_elementor_css' ); + } + } + + /** + * @param Documents_Manager $documents_manager + */ + public function register_documents( $documents_manager ) { + $documents_manager->register_document_type( self::TEMPLATE_TYPE, Documents\Widget::get_class_full_name() ); + } + + public function on_elementor_editor_init() { + if ( ! API::is_licence_has_feature( static::LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $promotion = Tiers::get_promotion_template( [ + 'title' => esc_html__( 'Meet Our Global Widget', 'elementor-pro' ), + 'messages' => [ + esc_html__( 'Create Global Widgets. Modify the content, style and setting of any widget and reuse it across your website to accelerate your workflow and stay consistent.', 'elementor-pro' ), + ], + 'link' => 'https://go.elementor.com/go-pro-advanced-global-widget/', + ], true ); + + Plugin::elementor()->common->add_template( $promotion, 'text' ); + + return; + } + + Plugin::elementor()->common->add_template( __DIR__ . '/views/panel-template.php' ); + } + + /** + * Get document data. + * + * Used to manipulate data of global widgets. + * + * @param $data + * @param $document + * + * @return array + */ + private function get_document_data( $data, $document ) { + // If not a global widget template document or does not have elements. + if ( ! ( $document instanceof Widget ) && ! empty( $data['elements'] ) ) { + $data['elements'] = Plugin::elementor()->db->iterate_data( $data['elements'], function( $element ) { + if ( ! empty( $element['templateID'] ) ) { + $element['originalWidgetType'] = $element['widgetType']; + $element['widgetType'] = 'global'; + } + + return $element; + } ); + } + + return $data; + } + + private function add_hooks() { + add_action( 'elementor/documents/register', [ $this, 'register_documents' ] ); + add_action( 'elementor/template-library/after_save_template', [ $this, 'set_template_widget_type_meta' ], 10, 2 ); + add_action( 'elementor/template-library/after_update_template', [ $this, 'on_template_update' ], 10, 2 ); + add_action( 'elementor/editor/init', [ $this, 'on_elementor_editor_init' ] ); + add_action( 'elementor/editor/after_save', [ $this, 'set_global_widget_included_posts_list' ], 10, 2 ); + + add_filter( 'elementor_pro/editor/localize_settings', [ $this, 'add_templates_localize_data' ] ); + add_filter( 'elementor/template-library/get_template', [ $this, 'filter_template_data' ] ); + add_filter( 'elementor/element/get_child_type', [ $this, 'get_element_child_type' ], 10, 2 ); + add_filter( 'elementor/utils/is_post_support', [ $this, 'is_post_type_support_elementor' ], 10, 3 ); + + add_filter( 'elementor/template_library/is_template_supports_export', [ $this, 'is_template_supports_export' ], 10, 2 ); + add_filter( 'elementor/document/save/data', function ( $data, $document ) { + return $this->get_document_data( $data, $document ); + }, 10, 2 ); + } +} diff --git a/modules/global-widget/views/panel-template.php b/modules/global-widget/views/panel-template.php new file mode 100644 index 0000000..3305e6c --- /dev/null +++ b/modules/global-widget/views/panel-template.php @@ -0,0 +1,33 @@ + + + + + diff --git a/modules/global-widget/widgets/global-widget.php b/modules/global-widget/widgets/global-widget.php new file mode 100644 index 0000000..1495ef0 --- /dev/null +++ b/modules/global-widget/widgets/global-widget.php @@ -0,0 +1,244 @@ +templates_manager->get_template_data( [ + 'source' => 'local', + 'template_id' => $data['templateID'], + ] ); + + if ( is_wp_error( $template_data ) ) { + throw new \Exception( $template_data->get_error_message() ); + } + + if ( empty( $template_data['content'] ) ) { + throw new \Exception( 'Template content not found.' ); + } + + $this->set_template_data( $template_data ); + + $template_widget_type = $this->get_template_widget_type(); + $original_widget_type = Plugin::elementor()->widgets_manager->get_widget_types( + $template_widget_type + ); + + if ( ! $original_widget_type ) { + throw new \Exception( 'Original widget type not found.' ); + } + + // If it saved as draft it already have the recent settings. + if ( empty( $data['draft'] ) ) { + if ( empty( $data['originalWidgetType'] ) ) { + // If: `$data['originalWidgetType']` exists it means that the data was manipulated in saving process, from the backend. + // so `widgetType` is 'global' and have to be changed. + $data['widgetType'] = $template_widget_type; + } + + if ( ! $this->is_draft_or_autosave_process() ) { + // If its not 'draft saving process' then settings should be according the template. + // Since draft saving process, already have the recent settings to save. + $data['settings'] = $this->get_template_settings(); + } + } + + $this->original_widget_type = $original_widget_type; + $this->data = $data; + } + + parent::__construct( $data, $args ); + } + + public function show_in_panel() { + return false; + } + + public function get_raw_data( $with_html_content = false ) { + $raw_data = parent::get_raw_data( $with_html_content ); + + // Save 'templateID' in all situations. + $raw_data['templateID'] = $this->get_data( 'templateID' ); + + if ( $this->is_draft_or_autosave_process() ) { + $raw_data['draft'] = true; + + // Keep the current snapshot, just mark it as a draft. + return $raw_data; + } + + if ( $this->is_saved_as_draft() ) { + // If: Item saved as draft + // Then: the the `$raw_data` hold recently saved draft template, with original widget type. + $raw_data['widgetType'] = $this->get_template_widget_type(); + + return $raw_data; + } + + return $raw_data; + } + + public function render_content() { + $this->get_original_element_instance()->render_content(); + } + + public function get_unique_selector() { + return '.elementor-global-' . $this->get_data( 'templateID' ); + } + + public function get_name() { + return 'global'; + } + + public function get_title() { + return esc_html__( 'Global', 'elementor-pro' ); + } + + public function get_script_depends() { + if ( $this->is_type_instance() ) { + return []; + } + + return $this->get_original_element_instance()->get_script_depends(); + } + + public function get_style_depends() { + if ( $this->is_type_instance() ) { + return []; + } + + return $this->get_original_element_instance()->get_style_depends(); + } + + public function get_controls( $control_id = null ) { + if ( $this->is_type_instance() ) { + return []; + } + + return $this->get_original_element_instance()->get_controls(); + } + + public function get_original_element_instance() { + if ( ! $this->original_element_instance ) { + $this->init_original_element_instance(); + } + + return $this->original_element_instance; + } + + public function on_export() { + return $this->get_template_content(); + } + + public function render_plain_content() { + $this->get_original_element_instance()->render_plain_content(); + } + + protected function add_render_attributes() { + // Never called from editor, this method is used only for frontend/preview. + parent::add_render_attributes(); + + $skin_type = $this->get_settings( '_skin' ); + + $original_widget_type = $this->get_original_element_instance()->get_data( 'widgetType' ); + + $this->set_render_attribute( '_wrapper', 'data-widget_type', $original_widget_type . '.' . ( $skin_type ? $skin_type : 'default' ) ); + + $this->add_render_attribute( '_wrapper', [ + 'class' => [ + 'elementor-global-' . $this->get_data( 'templateID' ), + 'elementor-widget-' . $original_widget_type, + ], + ] ); + } + + private function init_original_element_instance() { + $widget_class = $this->original_widget_type->get_class_name(); + + $template_content = $this->get_template_or_draft_content(); + $template_content['id'] = $this->get_id(); + + $this->original_element_instance = new $widget_class( + $template_content, + $this->original_widget_type->get_default_args() + ); + } + + private function is_draft_or_autosave_process() { + /** + * `Plugin::elementor()->common` is not available for guest/logged out users. + */ + if ( ! Plugin::elementor()->common ) { + return false; + } + + $ajax = Plugin::elementor()->common->get_component( 'ajax' ); + $ajax_data = $ajax->get_current_action_data(); + + // Is draft or autosave? + return $ajax_data && 'save_builder' === $ajax_data['action'] && in_array( $ajax_data['data']['status'], [ + Document::STATUS_DRAFT, + Document::STATUS_AUTOSAVE, + ], true ); + } + + private function is_saved_as_draft() { + return $this->get_data( 'draft' ); + } + + private function set_template_data( $template_data ) { + $this->template_data = $template_data; + } + + private function get_template_widget_type() { + return $this->template_data['content'][0]['widgetType']; + } + + private function get_template_settings() { + return $this->template_data['content'][0]['settings']; + } + + private function get_template_content() { + return $this->template_data['content'][0]; + } + + private function get_template_or_draft_content() { + if ( $this->is_saved_as_draft() ) { + $draft_data = $this->data; + $draft_data['widgetType'] = $this->get_template_widget_type(); + + return $draft_data; + } + + return $this->get_template_content(); + } +} diff --git a/modules/hotspot/module.php b/modules/hotspot/module.php new file mode 100644 index 0000000..ca1b926 --- /dev/null +++ b/modules/hotspot/module.php @@ -0,0 +1,21 @@ +remove_control( 'caption_source' ); + $this->remove_control( 'caption' ); + $this->remove_control( 'link_to' ); + $this->remove_control( 'link' ); + $this->remove_control( 'open_lightbox' ); + + /** + * Section Hotspot + */ + $this->start_controls_section( + 'hotspot_section', + [ + 'label' => esc_html__( 'Hotspot', 'elementor-pro' ), + ] + ); + + $repeater = new Repeater(); + + $repeater->start_controls_tabs( 'hotspot_repeater' ); + + $repeater->start_controls_tab( + 'hotspot_content_tab', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + ] + ); + + $repeater->add_control( + 'hotspot_label', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'label_block' => true, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'hotspot_link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + ] + ); + + $repeater->add_control( + 'hotspot_icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'skin' => 'inline', + 'label_block' => false, + ] + ); + + $repeater->add_control( + 'hotspot_icon_position', + [ + 'label' => esc_html__( 'Icon Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Icon Start', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'end' => [ + 'title' => esc_html__( 'Icon End', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'selectors_dictionary' => [ + 'start' => 'grid-column: 1;', + 'end' => 'grid-column: 2;', + ], + 'condition' => [ + 'hotspot_icon[value]!' => '', + 'hotspot_label[value]!' => '', + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .e-hotspot__icon' => '{{VALUE}}', + ], + 'default' => 'start', + ] + ); + + $repeater->add_control( + 'hotspot_icon_spacing', + [ + 'label' => esc_html__( 'Icon Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'default' => [ + 'size' => 5, + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .e-hotspot__button' => + 'grid-gap: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'hotspot_icon[value]!' => '', + 'hotspot_label[value]!' => '', + ], + ] + ); + + $repeater->add_control( + 'hotspot_custom_size', + [ + 'label' => esc_html__( 'Custom Hotspot Size', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'default' => 'no', + 'description' => esc_html__( 'Set custom Hotspot size that will only affect this specific hotspot.', 'elementor-pro' ), + ] + ); + + $repeater->add_control('hotspot_width', + [ + 'label' => esc_html__( 'Min Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 1000, + ], + 'em' => [ + 'max' => 100, + ], + 'rem' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}}' => '--hotspot-min-width: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'hotspot_custom_size' => 'yes', + ], + ] + ); + + $repeater->add_control( + 'hotspot_height', + [ + 'label' => esc_html__( 'Min Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 1000, + ], + 'em' => [ + 'max' => 100, + ], + 'rem' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}}' => '--hotspot-min-height: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'hotspot_custom_size' => 'yes', + ], + ] + ); + + $repeater->add_control( + 'hotspot_tooltip_content', + [ + 'render_type' => 'template', + 'label' => esc_html__( 'Tooltip Content', 'elementor-pro' ), + 'type' => Controls_Manager::WYSIWYG, + 'default' => esc_html__( 'Add Your Tooltip Text Here', 'elementor-pro' ), + ] + ); + + $repeater->end_controls_tab(); + + $repeater->start_controls_tab( + 'hotspot_position_tab', + [ + 'label' => esc_html__( 'POSITION', 'elementor-pro' ), + ] + ); + + $repeater->add_control( + 'hotspot_horizontal', + [ + 'label' => esc_html__( 'Horizontal Orientation', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => is_rtl() ? 'right' : 'left', + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'toggle' => false, + ] + ); + + $repeater->add_responsive_control( + 'hotspot_offset_x', + [ + 'label' => esc_html__( 'Offset', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ '%' ], + 'default' => [ + 'unit' => '%', + 'size' => 50, + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}}' => + '{{hotspot_horizontal.VALUE}}: {{SIZE}}%; --hotspot-translate-x: {{SIZE}}%;', + ], + ] + ); + + $repeater->add_control( + 'hotspot_vertical', + [ + 'label' => esc_html__( 'Vertical Orientation', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'default' => 'top', + 'toggle' => false, + ] + ); + + $repeater->add_responsive_control( + 'hotspot_offset_y', + [ + 'label' => esc_html__( 'Offset', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ '%' ], + 'default' => [ + 'unit' => '%', + 'size' => 50, + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}}' => + '{{hotspot_vertical.VALUE}}: {{SIZE}}%; --hotspot-translate-y: {{SIZE}}%;', + ], + ] + ); + + $repeater->add_control( + 'hotspot_tooltip_position', + [ + 'label' => esc_html__( 'Custom Tooltip Properties', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'default' => 'no', + 'description' => esc_html__( 'Set custom Tooltip opening that will only affect this specific hotspot.', 'elementor-pro' ), + ] + ); + + $repeater->add_control( + 'hotspot_heading', + [ + 'label' => esc_html__( 'Box', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'condition' => [ + 'hotspot_tooltip_position' => 'yes', + ], + ] + ); + + $repeater->add_responsive_control( + 'hotspot_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'right' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'bottom' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'left' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + 'top' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .e-hotspot--tooltip-position' => 'right: initial;bottom: initial;left: initial;top: initial;{{VALUE}}: calc(100% + 5px );', + ], + 'condition' => [ + 'hotspot_tooltip_position' => 'yes', + ], + 'render_type' => 'template', + ] + ); + + $repeater->add_responsive_control( + 'hotspot_tooltip_width', + [ + 'label' => esc_html__( 'Min Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 2000, + ], + 'em' => [ + 'max' => 200, + ], + 'rem' => [ + 'max' => 200, + ], + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .e-hotspot__tooltip' => 'min-width: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'hotspot_tooltip_position' => 'yes', + ], + ] + ); + + $repeater->add_control( + 'hotspot_tooltip_text_wrap', + [ + 'label' => esc_html__( 'Text Wrap', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}}' => '--white-space: normal', + ], + 'condition' => [ + 'hotspot_tooltip_position' => 'yes', + ], + ] + ); + + $repeater->end_controls_tab(); + + $repeater->end_controls_tabs(); + + $this->add_control( + 'hotspot', + [ + 'label' => esc_html__( 'Hotspot', 'elementor-pro' ), + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'title_field' => '{{{ hotspot_label }}}', + 'default' => [ + [ + // Default #1 circle + ], + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'hotspot_animation', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'e-hotspot--soft-beat' => esc_html__( 'Soft Beat', 'elementor-pro' ), + 'e-hotspot--expand' => esc_html__( 'Expand', 'elementor-pro' ), + 'e-hotspot--overlay' => esc_html__( 'Overlay', 'elementor-pro' ), + '' => esc_html__( 'None', 'elementor-pro' ), + ], + 'default' => 'e-hotspot--expand', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'hotspot_sequenced_animation', + [ + 'label' => esc_html__( 'Sequenced Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'default' => 'no', + 'frontend_available' => true, + 'render_type' => 'none', + ] + ); + + $this->add_control( + 'hotspot_sequenced_animation_duration', + [ + 'label' => esc_html__( 'Sequence Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 20000, + 'step' => 100, + ], + ], + 'condition' => [ + 'hotspot_sequenced_animation' => 'yes', + ], + 'frontend_available' => true, + 'render_type' => 'ui', + ] + ); + + $this->end_controls_section(); + + /** + * Tooltip Section + */ + $this->start_controls_section( + 'tooltip_section', + [ + 'label' => esc_html__( 'Tooltip', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'tooltip_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'top', + 'toggle' => false, + 'options' => [ + 'right' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'bottom' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'left' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + 'top' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .e-hotspot--tooltip-position' => 'right: initial;bottom: initial;left: initial;top: initial;{{VALUE}}: calc(100% + 5px );', + ], + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( + 'tooltip_trigger', + [ + 'label' => esc_html__( 'Trigger', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'mouseenter' => esc_html__( 'Hover', 'elementor-pro' ), + 'click' => esc_html__( 'Click', 'elementor-pro' ), + 'none' => esc_html__( 'None', 'elementor-pro' ), + ], + 'default' => 'click', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'tooltip_animation', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'e-hotspot--fade-in-out' => esc_html__( 'Fade In/Out', 'elementor-pro' ), + 'e-hotspot--fade-grow' => esc_html__( 'Fade Grow', 'elementor-pro' ), + 'e-hotspot--fade-direction' => esc_html__( 'Fade By Direction', 'elementor-pro' ), + 'e-hotspot--slide-direction' => esc_html__( 'Slide By Direction', 'elementor-pro' ), + ], + 'default' => 'e-hotspot--fade-in-out', + 'placeholder' => esc_html__( 'Enter your image caption', 'elementor-pro' ), + 'condition' => [ + 'tooltip_trigger!' => 'none', + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'tooltip_animation_duration', + [ + 'label' => esc_html__( 'Animation Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 10000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--tooltip-transition-duration: {{SIZE}}ms;', + ], + 'condition' => [ + 'tooltip_trigger!' => 'none', + ], + ] + ); + + $this->end_controls_section(); + + /************* + * Style Tab + ************/ + /** + * Section Style Image + */ + + $this->remove_control( 'section_style_caption' ); + + $this->remove_control( 'caption_align' ); + + $this->remove_control( 'text_color' ); + + $this->remove_control( 'caption_background_color' ); + + $this->remove_control( 'caption_typography' ); + + $this->remove_control( 'caption_text_shadow' ); + + $this->remove_control( 'caption_space' ); + + $this->update_control( 'align', [ + 'options' => [ + 'flex-start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'flex-end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--background-align: {{VALUE}};', + ], + ] ); + + $this->update_control( + 'width', + [ + 'selectors' => [ + '{{WRAPPER}}' => '--container-width: {{SIZE}}{{UNIT}}; --image-width: 100%;', + ], + ] + ); + + $this->update_control( + 'space', + [ + 'selectors' => [ + '{{WRAPPER}}' => '--container-max-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->update_control( + 'height', + [ + 'selectors' => [ + '{{WRAPPER}}' => '--container-height: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->remove_control( 'hover_animation' ); + + $this->update_control( + 'opacity', + [ + 'selectors' => [ + '{{WRAPPER}}' => '--opacity: {{SIZE}};', + ], + ] + ); + + $this->update_control( + 'opacity_hover', + [ + 'selectors' => [ + '{{WRAPPER}} .elementor-widget-container>img:hover' => '--opacity: {{SIZE}};', + ], + ] + ); + + /** + * Section Style Hotspot + */ + $this->start_controls_section( + 'section_style_hotspot', + [ + 'label' => esc_html__( 'Hotspot', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'style_hotspot_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--hotspot-color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + ] + ); + + $this->add_responsive_control( + 'style_hotspot_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 300, + ], + 'em' => [ + 'max' => 30, + ], + 'rem' => [ + 'max' => 30, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--hotspot-size: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'style_typography', + 'selector' => '{{WRAPPER}} .e-hotspot__label', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + ] + ); + + $this->add_responsive_control( + 'style_hotspot_width', + [ + 'label' => esc_html__( 'Min Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 1000, + ], + 'em' => [ + 'max' => 100, + ], + 'rem' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--hotspot-min-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'style_hotspot_height', + [ + 'label' => esc_html__( 'Min Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 1000, + ], + 'em' => [ + 'max' => 100, + ], + 'rem' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--hotspot-min-height: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'style_hotspot_box_color', + [ + 'label' => esc_html__( 'Box Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--hotspot-box-color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + ] + ); + + $this->add_responsive_control( + 'style_hotspot_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--hotspot-padding: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'style_hotspot_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--hotspot-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'style_hotspot_box_shadow', + 'selector' => ' + {{WRAPPER}} .e-hotspot:not(.e-hotspot--circle) .e-hotspot__button, + {{WRAPPER}} .e-hotspot.e-hotspot--circle .e-hotspot__button .e-hotspot__outer-circle + ', + ] + ); + + $this->end_controls_section(); + + /** + * Section Style Tooltip + */ + $this->start_controls_section( + 'section_style_tooltip', + [ + 'label' => esc_html__( 'Tooltip', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'style_tooltip_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tooltip-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'style_tooltip_typography', + 'selector' => '{{WRAPPER}} .e-hotspot__tooltip', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + ] + ); + + $this->add_responsive_control( + 'style_tooltip_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--tooltip-align: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'style_tooltip_heading', + [ + 'label' => esc_html__( 'Box', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'style_tooltip_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 2000, + ], + 'em' => [ + 'max' => 200, + ], + 'rem' => [ + 'max' => 200, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--tooltip-min-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'style_tooltip_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--tooltip-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'style_tooltip_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tooltip-color: {{VALUE}}', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + ] + ); + + $this->add_control( + 'style_tooltip_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--tooltip-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'style_tooltip_box_shadow', + 'selector' => '{{WRAPPER}} .e-hotspot__tooltip', + ] + ); + + $this->end_controls_section(); + } + + + protected function render() { + $settings = $this->get_settings_for_display(); + + if ( empty( $settings['image']['url'] ) ) { + return; + } + + $is_tooltip_direction_animation = 'e-hotspot--slide-direction' === $settings['tooltip_animation'] || 'e-hotspot--fade-direction' === $settings['tooltip_animation']; + $show_tooltip = 'none' === $settings['tooltip_trigger']; + $sequenced_animation_class = 'yes' === $settings['hotspot_sequenced_animation'] ? 'e-hotspot--sequenced' : ''; + + // Main Image + Group_Control_Image_Size::print_attachment_image_html( $settings, 'image', 'image' ); + + // Hotspot + foreach ( $settings['hotspot'] as $key => $hotspot ) : + $is_circle = ! $hotspot['hotspot_label'] && ! $hotspot['hotspot_icon']['value']; + $is_only_icon = ! $hotspot['hotspot_label'] && $hotspot['hotspot_icon']['value']; + $hotspot_position_x = '%' === $hotspot['hotspot_offset_x']['unit'] ? 'e-hotspot--position-' . $hotspot['hotspot_horizontal'] : ''; + $hotspot_position_y = '%' === $hotspot['hotspot_offset_y']['unit'] ? 'e-hotspot--position-' . $hotspot['hotspot_vertical'] : ''; + $is_hotspot_link = ! empty( $hotspot['hotspot_link']['url'] ); + $hotspot_element_tag = $is_hotspot_link ? 'a' : 'div'; + + // hotspot attributes + $hotspot_repeater_setting_key = $this->get_repeater_setting_key( 'hotspot', 'hotspots', $key ); + $this->add_render_attribute( + $hotspot_repeater_setting_key, [ + 'class' => [ + 'e-hotspot', + 'elementor-repeater-item-' . $hotspot['_id'], + $sequenced_animation_class, + $hotspot_position_x, + $hotspot_position_y, + $is_hotspot_link ? 'e-hotspot--link' : '', + ( 'click' === $settings['tooltip_trigger'] && $is_hotspot_link ) ? 'e-hotspot--no-tooltip' : '', + ], + ] + ); + if ( $is_circle ) { + $this->add_render_attribute( $hotspot_repeater_setting_key, 'class', 'e-hotspot--circle' ); + } + if ( $is_only_icon ) { + $this->add_render_attribute( $hotspot_repeater_setting_key, 'class', 'e-hotspot--icon' ); + } + + if ( $is_hotspot_link ) { + $this->add_link_attributes( $hotspot_repeater_setting_key, $hotspot['hotspot_link'] ); + } + + // hotspot trigger attributes + $trigger_repeater_setting_key = $this->get_repeater_setting_key( 'trigger', 'hotspots', $key ); + $this->add_render_attribute( + $trigger_repeater_setting_key, [ + 'class' => [ + 'e-hotspot__button', + $settings['hotspot_animation'], + ], + ] + ); + + //direction mask attributes + $direction_mask_repeater_setting_key = $this->get_repeater_setting_key( 'e-hotspot__direction-mask', 'hotspots', $key ); + $this->add_render_attribute( + $direction_mask_repeater_setting_key, [ + 'class' => [ + 'e-hotspot__direction-mask', + ( $is_tooltip_direction_animation ) ? 'e-hotspot--tooltip-position' : '', + ], + ] + ); + + //tooltip attributes + $tooltip_custom_position = ( $is_tooltip_direction_animation && $hotspot['hotspot_tooltip_position'] && $hotspot['hotspot_position'] ) ? 'e-hotspot--override-tooltip-animation-from-' . $hotspot['hotspot_position'] : ''; + $tooltip_repeater_setting_key = $this->get_repeater_setting_key( 'tooltip', 'hotspots', $key ); + $this->add_render_attribute( + $tooltip_repeater_setting_key, [ + 'class' => [ + 'e-hotspot__tooltip', + ( $show_tooltip ) ? 'e-hotspot--show-tooltip' : '', + ( ! $is_tooltip_direction_animation ) ? 'e-hotspot--tooltip-position' : '', + ( ! $show_tooltip ) ? $settings['tooltip_animation'] : '', + $tooltip_custom_position, + ], + ] + ); ?> + + + < print_render_attribute_string( $hotspot_repeater_setting_key ); ?>> + + +
    print_render_attribute_string( $trigger_repeater_setting_key ); ?>> + +
    +
    + + +
    + + +
    + + +
    + + + + +
    print_render_attribute_string( $direction_mask_repeater_setting_key ); ?>> + +
    print_render_attribute_string( $tooltip_repeater_setting_key ); ?> > + +
    + +
    + + + + > + + + + + <# + const image = { + id: settings.image.id, + url: settings.image.url, + size: settings.image_size, + dimension: settings.image_custom_dimension, + model: view.getEditModel() + }; + + const imageUrl = elementor.imagesManager.getImageUrl( image ); + + if ( ! imageUrl ) { + return; + } + #> + + <# + const isTooltipDirectionAnimation = (settings.tooltip_animation==='e-hotspot--slide-direction' || settings.tooltip_animation==='e-hotspot--fade-direction' ) ? true : false; + const showTooltip = ( settings.tooltip_trigger === 'none' ); + + _.each( settings.hotspot, ( hotspot, index ) => { + const iconHTML = elementor.helpers.renderIcon( view, hotspot.hotspot_icon, {}, 'i' , 'object' ); + + const isCircle = !hotspot.hotspot_label && !hotspot.hotspot_icon.value; + const isOnlyIcon = !hotspot.hotspot_label && hotspot.hotspot_icon.value; + const hotspotPositionX = '%' === hotspot.hotspot_offset_x.unit ? 'e-hotspot--position-' + hotspot.hotspot_horizontal : ''; + const hotspotPositionY = '%' === hotspot.hotspot_offset_y.unit ? 'e-hotspot--position-' + hotspot.hotspot_vertical : ''; + const hotspotLink = hotspot.hotspot_link.url; + const hotspotElementTag = hotspotLink ? 'a': 'div'; + + // hotspot attributes + const hotspotRepeaterSettingKey = view.getRepeaterSettingKey( 'hotspot', 'hotspots', index ); + view.addRenderAttribute( hotspotRepeaterSettingKey, { + 'class' : [ + 'e-hotspot', + 'elementor-repeater-item-' + hotspot._id, + hotspotPositionX, + hotspotPositionY, + hotspotLink ? 'e-hotspot--link' : '',, + ] + }); + + if ( isCircle ) { + view.addRenderAttribute( hotspotRepeaterSettingKey, 'class', 'e-hotspot--circle' ); + } + + if ( isOnlyIcon ) { + view.addRenderAttribute( hotspotRepeaterSettingKey, 'class', 'e-hotspot--icon' ); + } + + // hotspot trigger attributes + const triggerRepeaterSettingKey = view.getRepeaterSettingKey( 'trigger', 'hotspots', index ); + view.addRenderAttribute(triggerRepeaterSettingKey, { + 'class' : [ + 'e-hotspot__button', + settings.hotspot_animation, + //'hotspot-trigger-' + hotspot.hotspot_icon_position + ] + }); + + //direction mask attributes + const directionMaskRepeaterSettingKey = view.getRepeaterSettingKey( 'e-hotspot__direction-mask', 'hotspots', index ); + view.addRenderAttribute(directionMaskRepeaterSettingKey, { + 'class' : [ + 'e-hotspot__direction-mask', + ( isTooltipDirectionAnimation ) ? 'e-hotspot--tooltip-position' : '' + ] + }); + + //tooltip attributes + const tooltipCustomPosition = ( isTooltipDirectionAnimation && hotspot.hotspot_tooltip_position && hotspot.hotspot_position ) ? 'e-hotspot--override-tooltip-animation-from-' + hotspot.hotspot_position : ''; + const tooltipRepeaterSettingKey = view.getRepeaterSettingKey('tooltip', 'hotspots', index); + view.addRenderAttribute( tooltipRepeaterSettingKey, { + 'class': [ + 'e-hotspot__tooltip', + ( showTooltip ) ? 'e-hotspot--show-tooltip' : '', + ( !isTooltipDirectionAnimation ) ? 'e-hotspot--tooltip-position' : '', + ( !showTooltip ) ? settings.tooltip_animation : '', + tooltipCustomPosition + ], + }); + + #> + <{{{ hotspotElementTag }}} {{{ view.getRenderAttributeString( hotspotRepeaterSettingKey ) }}}> + + +
    + <# if ( isCircle ) { #> +
    +
    + <# } else { #> + <# if (hotspot.hotspot_icon.value){ #> +
    {{{ iconHTML.value }}}
    + <# } #> + <# if ( hotspot.hotspot_label ){ #> +
    {{{ hotspot.hotspot_label }}}
    + <# } #> + <# } #> +
    + + + <# if( hotspot.hotspot_tooltip_content && ! ( 'click' === settings.tooltip_trigger && hotspotLink ) ){ #> + <# if( isTooltipDirectionAnimation ){ #> +
    + <# } #> +
    + {{{ hotspot.hotspot_tooltip_content }}} +
    + <# if( isTooltipDirectionAnimation ){ #> +
    + <# } #> + <# } #> + + + <# }); #> + add_actions(); + } + + public function admin_columns_headers( $defaults ) { + $defaults['shortcode'] = esc_html__( 'Shortcode', 'elementor-pro' ); + + return $defaults; + } + + public function admin_columns_content( $column_name, $post_id ) { + if ( 'shortcode' !== $column_name ) { + return; + } + + printf( + '', + sprintf( + /* translators: %s: Template ID. */ + esc_attr( 'elementor-templte-%s-shortcode' ), + $post_id // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ), + sprintf( + /* translators: %s: Template ID. */ + esc_html__( 'Elementor template shortcode for template %s', 'elementor-pro' ), + $post_id // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ), + sprintf( + /* translators: %s: Shortcode, %d: Template ID. */ + esc_attr( '[%s id="%d"]' ), + self::SHORTCODE, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + $post_id // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ) + ); + } + + public function shortcode( $attributes = [] ) { + if ( empty( $attributes['id'] ) ) { + return ''; + } + + $include_css = false; + + if ( isset( $attributes['css'] ) && 'false' !== $attributes['css'] ) { + $include_css = (bool) $attributes['css']; + } + + return Plugin::elementor()->frontend->get_builder_content_for_display( $attributes['id'], $include_css ); + } + + private function add_actions() { + if ( is_admin() ) { + add_action( 'manage_' . Source_Local::CPT . '_posts_columns', [ $this, 'admin_columns_headers' ] ); + add_action( 'manage_' . Source_Local::CPT . '_posts_custom_column', [ $this, 'admin_columns_content' ], 10, 2 ); + } + + add_shortcode( self::SHORTCODE, [ $this, 'shortcode' ] ); + } +} diff --git a/modules/library/module.php b/modules/library/module.php new file mode 100644 index 0000000..630aafb --- /dev/null +++ b/modules/library/module.php @@ -0,0 +1,158 @@ +add_filters(); + $this->add_actions(); + + new Shortcode(); + } + + public function get_name() { + return 'library'; + } + + public function register_wp_widgets() { + register_widget( 'ElementorPro\Modules\Library\WP_Widgets\Elementor_Library' ); + } + + /** + * @deprecated 3.1.0 + */ + public function localize_settings() { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); + + return []; + } + + public function add_to_results_for_library_widget_templates( $val, $post, $request ) { + $document = Plugin::elementor()->documents->get( $post->ID ); + if ( $document ) { + return true; + } + + return false; + } + + public function format_post_title_for_library_widget_templates( $post_title, $post_id, $request ) { + $document = Plugin::elementor()->documents->get( $post_id ); + return $post_title . ' (' . $document->get_post_type_title() . ')'; + } + + public function add_actions() { + add_action( 'widgets_init', [ $this, 'register_wp_widgets' ] ); + } + + /** + * @deprecated 2.6.0 No longer used by internal code. See Autocomplete documentation in Query-Control Module. + * @param array $results + * @param array $data + * + * @return array + */ + public function get_autocomplete_for_library_widget_templates( array $results, array $data ) { + $document_types = Plugin::elementor()->documents->get_document_types( [ + 'show_in_library' => true, + ] ); + + $query_params = [ + 's' => $data['q'], + 'post_type' => Source_Local::CPT, + 'posts_per_page' => -1, + 'orderby' => 'meta_value', + 'order' => 'ASC', + 'meta_query' => [ + [ + 'key' => Document::TYPE_META_KEY, + 'value' => array_keys( $document_types ), + 'compare' => 'IN', + ], + ], + ]; + + $query = new \WP_Query( $query_params ); + + $results = []; + + foreach ( $query->posts as $post ) { + $document = Plugin::elementor()->documents->get( $post->ID ); + if ( $document ) { + $results[] = [ + 'id' => $post->ID, + 'text' => esc_html( $post->post_title ) . ' (' . $document->get_post_type_title() . ')', + ]; + } + } + + return $results; + } + + /** + * @deprecated 2.6.0 No longer used by internal code. See Autocomplete documentation in Query-Control Module. + * @param $results + * @param $request + * + * @return mixed + */ + public function get_value_title_for_library_widget_templates( $results, $request ) { + $ids = (array) $request['id']; + + $query = new \WP_Query( + [ + 'post_type' => Source_Local::CPT, + 'post__in' => $ids, + 'posts_per_page' => -1, + ] + ); + + foreach ( $query->posts as $post ) { + $document = Plugin::elementor()->documents->get( $post->ID ); + if ( $document ) { + $results[ $post->ID ] = esc_html( $post->post_title ) . ' (' . $document->get_post_type_title() . ')'; + } + } + + return $results; + } + + public function add_filters() { + add_filter( 'elementor/widgets/black_list', function( $black_list ) { + $black_list[] = 'ElementorPro\Modules\Library\WP_Widgets\Elementor_Library'; + + return $black_list; + } ); + } + + public static function get_templates() { + return Plugin::elementor()->templates_manager->get_source( 'local' )->get_items(); + } + + public static function empty_templates_message() { + return '
    +
    +
    ' . esc_html__( 'You Haven’t Saved Templates Yet.', 'elementor-pro' ) . '
    + +
    '; + } +} diff --git a/modules/library/widgets/template.php b/modules/library/widgets/template.php new file mode 100644 index 0000000..b2a9225 --- /dev/null +++ b/modules/library/widgets/template.php @@ -0,0 +1,108 @@ +start_controls_section( + 'section_template', + [ + 'label' => esc_html__( 'Template', 'elementor-pro' ), + ] + ); + + $document_types = Plugin::elementor()->documents->get_document_types( [ + 'show_in_library' => true, + ] ); + + $this->add_control( + 'template_id', + [ + 'label' => esc_html__( 'Choose Template', 'elementor-pro' ), + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_LIBRARY_TEMPLATE, + 'query' => [ + 'meta_query' => [ + [ + 'key' => Document::TYPE_META_KEY, + 'value' => array_keys( $document_types ), + 'compare' => 'IN', + ], + ], + ], + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + $template_id = $this->get_settings( 'template_id' ); + + if ( empty( $template_id ) ) { + return; + } + + if ( 'publish' !== get_post_status( $template_id ) ) { + return; + } + + ?> +
    + frontend->get_builder_content_for_display( $template_id ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> +
    + esc_html__( 'Embed your saved elements.', 'elementor-pro' ), + ] + ); + } + + /** + * @param array $args + * @param array $instance + */ + public function widget( $args, $instance ) { + // PHPCS - Theme arg + echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + + if ( ! empty( $instance['title'] ) ) { + /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ + $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ); + // PHPCS - Theme arg + echo $args['before_title'] . esc_html( $title ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + if ( ! empty( $instance['template_id'] ) && 'publish' === get_post_status( $instance['template_id'] ) ) { + $this->sidebar_id = $args['id']; + + add_filter( 'elementor/frontend/builder_content_data', [ $this, 'filter_content_data' ] ); + + // PHPCS - the main content + echo Plugin::elementor()->frontend->get_builder_content_for_display( $instance['template_id'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + + remove_filter( 'elementor/frontend/builder_content_data', [ $this, 'filter_content_data' ] ); + + unset( $this->sidebar_id ); + } + + // PHPCS - Theme arg + echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + /** + * Avoid nesting a sidebar within a template that will appear in the sidebar itself + * + * @param array $data + * + * @return mixed + */ + public function filter_content_data( $data ) { + if ( ! empty( $data ) ) { + $data = Plugin::elementor()->db->iterate_data( $data, function( $element ) { + if ( 'widget' === $element['elType'] && 'sidebar' === $element['widgetType'] && $this->sidebar_id === $element['settings']['sidebar'] ) { + $element['settings']['sidebar'] = null; + } + + return $element; + } ); + } + + return $data; + } + + /** + * @param array $instance + * + * @return void + */ + public function form( $instance ) { + $default = [ + 'title' => '', + 'template_id' => '', + ]; + + $instance = array_merge( $default, $instance ); + + $templates = Module::get_templates(); + + if ( ! $templates ) { + // PHPCS - the method empty_templates_message is safe. + echo Module::empty_templates_message(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + + return; + } + ?> +

    + + +

    + +

    + + + + + href="" + > + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/loop-builder/documents/loop.php b/modules/loop-builder/documents/loop.php new file mode 100644 index 0000000..4a4d8d8 --- /dev/null +++ b/modules/loop-builder/documents/loop.php @@ -0,0 +1,535 @@ + esc_html__( 'What is a loop?', 'elementor-pro' ), + 'content' => esc_html__( 'A Loop is a layout you can customize to display recurring dynamic content - like listings, posts, portfolios, products, , etc.', 'elementor-pro' ), + 'tip' => esc_html__( 'Start by creating a master item. All the other instances in the grid will match this design. Then go back to the widget in the editor panel and assign both a template and a source of content. Your grid should populate automatically.', 'elementor-pro' ), + 'docs' => 'https://go.elementor.com/app-theme-builder-loop', + 'video_url' => 'https://www.youtube.com/embed/zMvY9XaE1YY', + ]; + } + + protected static function get_site_editor_thumbnail_url() { + return ELEMENTOR_PRO_MODULES_URL . 'loop-builder/assets/images/loop-item.svg'; + } + + public static function get_properties() { + $properties = parent::get_properties(); + + $properties['support_conditions'] = false; + + return $properties; + } + + public function save( $data ) { + if ( isset( $data['settings']['source'] ) ) { + update_post_meta( $this->get_main_id(), '_elementor_source', $data['settings']['source'] ); + } + + parent::save( $data ); + } + + private function get_data_id() { + if ( Taxonomy_Loop_Provider::is_loop_taxonomy() ) { + return $this->get_data_id_from_taxonomy_loop_query(); + } + + return get_the_ID(); + } + + public function get_container_attributes() { + $attributes = Document::get_container_attributes(); + $post_id = $this->get_data_id(); + + $attributes['class'] .= ' e-loop-item'; + $attributes['class'] .= ' e-loop-item-' . $post_id; + $attributes['class'] .= ' ' . esc_attr( implode( ' ', get_post_class( [], $post_id ) ) ); + + $attributes['data-custom-edit-handle'] = true; + + return $attributes; + } + + public function get_initial_config() { + $config = parent::get_initial_config(); + $loop_builder_module = new LoopBuilderModule(); + + if ( 'post' === $loop_builder_module->get_source_type_from_post_meta( $this->get_main_id() ) ) { + foreach ( static::RECOMMENDED_POSTS_WIDGET_NAMES as $recommended_posts_widget_name ) { + $config['panel']['widgets_settings'][ $recommended_posts_widget_name ] = [ + 'categories' => [ 'recommended' ], + 'show_in_panel' => true, + ]; + } + } + + $config['panel']['widgets_settings']['container'] = [ + 'categories' => [ 'layout' ], + ]; + + foreach ( static::WIDGETS_TO_HIDE as $widget_to_hide ) { + $config['panel']['widgets_settings'][ $widget_to_hide ] = [ + 'show_in_panel' => false, + ]; + } + + $config['container_attributes'] = $this->get_container_attributes(); + + return $config; + } + + public static function get_site_editor_config() { + $config = parent::get_site_editor_config(); + + $config['show_instances'] = false; + + return $config; + } + + public function get_location_label() { + return ''; + } + + public function get_css_wrapper_selector() { + return '.e-loop-item-' . $this->get_main_id(); + } + + public static function get_preview_as_options() { + $post_types = Utils::get_public_post_types(); + + $post_types_options = []; + + foreach ( $post_types as $post_type => $label ) { + $post_types_options[ self::SINGLE_PREFIX . $post_type ] = get_post_type_object( $post_type )->labels->singular_name; + } + + return [ + 'single' => [ + 'label' => esc_html__( 'Single', 'elementor-pro' ), + 'options' => $post_types_options, + ], + ]; + } + + protected function get_remote_library_config() { + $config = parent::get_remote_library_config(); + + $config['type'] = self::DOCUMENT_TYPE; + $config['default_route'] = 'templates/loop-items'; + + return $config; + } + + /** + * Get Edit Url + * + * Disable the Library modal for non-container (section) users. + * + * @return string + */ + public function get_edit_url() { + $url = parent::get_edit_url(); + + if ( ! Plugin::elementor()->experiments->is_feature_active( 'container' ) ) { + $url = str_replace( '#library', '', $url ); + } + + return $url; + } + + protected static function get_editor_panel_categories() { + $new_categories = [ + 'recommended' => [ + 'title' => esc_html__( 'Recommended', 'elementor-pro' ), + ], + 'layout' => [ + 'title' => esc_html__( 'Layout', 'elementor-pro' ), + 'hideIfEmpty' => true, + ], + ]; + return static::insert_categories_after_favorites( $new_categories ); + } + + protected function register_controls() { + parent::register_controls(); + + $this->remove_control( 'content_wrapper_html_tag' ); + + $this->update_preview_control(); + + $this->inject_width_control(); + + $this->add_query_section(); + + Plugin::elementor()->controls_manager->add_custom_css_controls( $this ); + + } + + /** + * Get Wrapper Tags + * + * We remove the `content_wrapper_html_tag` control in this document and default to using a `div`. + * The setting no longer exists when printing the document element, so we need to override this method so that + * the extended document class defaults to using a `div` when printing the element. + * + * @since 3.8.0 + * + * @return false + */ + public function get_wrapper_tags() { + return false; + } + + /** + * Print elements with wrapper. + * + * Overwrite method from theme-document.php to render some custom markup if a variable + * $elements_data['empty_loop_template'] is set. This variable is set via a filter hook + * 'elementor/frontend/builder_content_data' in the loop builder module. + * + * @since 3.8.0 + * + * @param $elements_data + * + * @return void + */ + public function print_elements_with_wrapper( $elements_data = null ) { + if ( isset( $elements_data['empty_loop_template'] ) ) { + $this->print_empty_loop_template_markup( $elements_data['empty_loop_template_id'] ); + } else { + parent::print_elements_with_wrapper( $elements_data ); + } + } + + private function enqueue_loop_css() { + if ( $this->is_autosave() ) { + $css_file = Loop_Preview::create( $this->post->ID ); + } else { + $css_file = Loop_CSS::create( $this->post->ID ); + } + + $css_file->print_all_css( $this->post->ID ); + } + + /** + * Get content. + * + * Override the parent method to retrieve the content with CSS in the Editor. + * + * @since 3.8.0 + */ + public function get_content( $with_css = false ) { + $edit_mode = Plugin::elementor()->editor->is_edit_mode(); + + add_filter( 'elementor/frontend/builder_content/before_print_css', [ $this, 'prevent_inline_css_printing' ] ); + + $this->enqueue_loop_css(); + + Plugin::elementor()->editor->set_edit_mode( false ); + + $content = parent::get_content(); + + remove_filter( 'elementor/frontend/builder_content/before_print_css', [ $this, 'prevent_inline_css_printing' ] ); + + Plugin::elementor()->editor->set_edit_mode( $edit_mode ); + + return $content; + } + + /** + * Runs on the 'elementor/frontend/builder_content/before_print_css' hook. + * + * @return false + */ + public function prevent_inline_css_printing() { + return false; + } + + /** + * Print empty loop template markup. + * + * This function is used to render markup in the editor when a loop template is empty/blank. + * Currently, nothing will be rendered in the editor if the template is empty. + * This markup is needed in the DOM for us to be able to switch to this document in place. + * + * @since 3.8.0 + * + * @param int $post_id The post ID of the document. + * + * @return void + */ + protected function print_empty_loop_template_markup( $post_id ) { + ?> +
    +
    +
    + start_controls_section( + '_section_query', + [ + 'label' => esc_html__( 'Query', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_SETTINGS, + ] + ); + + $loop_builder_module = new LoopBuilderModule(); + $source_type = $loop_builder_module->get_source_type_from_post_meta( $this->get_main_id() ); + + $this->add_control( + 'source', + [ + 'label' => esc_html__( 'Source Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'post' => esc_html__( 'Posts', 'elementor-pro' ), + ], + 'default' => $source_type, + 'prefix_class' => 'elementor-view-', + ] + ); + + do_action( 'elementor-pro/modules/loop-builder/documents/loop/query_settings', $this ); + + $this->add_control( + 'apply_query_source', + [ + 'type' => Controls_Manager::BUTTON, + 'label' => esc_html__( 'Apply', 'elementor-pro' ), + 'label_block' => true, + 'show_label' => false, + 'text' => esc_html__( 'Apply', 'elementor-pro' ), + 'event' => 'elementorLoopBuilder:ApplySourceChange', + ] + ); + + $this->add_control( + 'query_source_description', + [ + 'raw' => esc_html__( 'This affects the types of widgets and templates you can use for your master item.', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + ] + ); + + $this->end_controls_section(); + } + + /** + * @return void + */ + protected function inject_width_control() { + $this->start_injection([ + 'type' => 'section', + 'at' => 'start', + 'of' => 'preview_settings', + ]); + + $this->add_responsive_control( + 'preview_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 200, + 'max' => 1140, + ], + 'em' => [ + 'min' => 20, + 'max' => 100, + ], + 'rem' => [ + 'min' => 20, + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--preview-width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_injection(); + } + + /** + * @return void + */ + protected function update_preview_control() { + $loop_builder_module = new LoopBuilderModule(); + $source_type = $loop_builder_module->get_source_type_from_post_meta( $this->get_main_id() ); + + if ( Taxonomy_Loop_Provider::is_source_type_taxonomy( $source_type ) ) { + $this->update_taxonomy_settings_controls( $source_type ); + } else { + $this->update_post_settings_controls( $source_type ); + } + } + + /** + * @param array $new_categories + * @return array + */ + private static function insert_categories_after_favorites( array $new_categories ) { + $existing_categories = parent::get_editor_panel_categories(); + $category_keys = array_keys( $existing_categories ); + $index = array_search( 'favorites', $category_keys, true ); + return array_splice( $existing_categories, 0, $index + 1 ) + $new_categories + array_splice( $existing_categories, $index + 1 ); + } + + private function update_post_settings_controls( $source_type ) { + $this->update_control( + self::PREVIEW_TYPE, + [ + 'default' => self::SINGLE_PREFIX . $source_type, + 'label' => esc_html__( 'Preview a specific post or item', 'elementor-pro' ), + ] + ); + + $latest_posts = get_posts( [ + 'posts_per_page' => 1, + 'post_type' => $source_type, + ] ); + + if ( ! empty( $latest_posts ) ) { + $this->update_control( + self::PREVIEW_ID, + [ + 'default' => $latest_posts[0]->ID, + ] + ); + } + } + + private function update_taxonomy_settings_controls( $source_type ) { + $this->update_taxonomy_preview_type_control( $source_type ); + $this->update_taxonomy_preview_id_control( $source_type ); + } + + private function update_taxonomy_preview_type_control( $source_type ) { + $post_type_slug = $this->extract_taxonomy_type( $source_type ); + $allowed_post_types = Taxonomy_Loop_Provider::get_supported_cpts( $post_type_slug ); + $options_prefix = $source_type . '/'; + $taxonomy_options = $this->get_taxonomy_options( $allowed_post_types, $options_prefix ); + + $this->update_control( + self::PREVIEW_TYPE, + [ + 'default' => Taxonomy_Loop_Provider::get_default_source_type( $source_type, $options_prefix ), + 'groups' => [ + 'archive' => [ + 'label' => esc_html__( 'Archive', 'elementor-pro' ), + 'options' => $taxonomy_options, + ], + ], + 'label' => esc_html__( 'Preview a specific post or item', 'elementor-pro' ), + ] + ); + } + + private function update_taxonomy_preview_id_control( $source_type ) { + $term_id = $this->get_settings_for_display( self::PREVIEW_ID ) ?? null; + + $filter_args = [ + 'taxonomy' => Taxonomy_Loop_Provider::get_default_source_type( $source_type ), + 'show_empty_items' => 'yes', + 'show_child_taxonomy' => 'yes', + ]; + + if ( $term_id ) { + $filter_args['term_taxonomy_id'] = $term_id; + } + + $latest_terms = $this->get_filtered_taxonomies( $filter_args, [] ); + + if ( ! empty( $latest_terms ) ) { + $term = array_values( $latest_terms )[0]; + $this->update_control( + self::PREVIEW_ID, + [ + 'default' => $term->term_id, + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_TAX, + ], + ], + ); + } + } + + private function extract_taxonomy_type( $source_type ) { + return str_replace( '_taxonomy', '', $source_type ); + } +} diff --git a/modules/loop-builder/files/css/loop-css-trait.php b/modules/loop-builder/files/css/loop-css-trait.php new file mode 100644 index 0000000..5b15d3e --- /dev/null +++ b/modules/loop-builder/files/css/loop-css-trait.php @@ -0,0 +1,149 @@ +editor->is_edit_mode() ) { + return true; + } + + return 'internal' !== get_option( 'elementor_css_print_method' ); + } + + /** + * @param array $fonts + * @return void + */ + private function enqueue_fonts( array $fonts ) { + foreach ( $fonts as $font ) { + Plugin::elementor()->frontend->enqueue_font( $font ); + } + } + + /** + * @param $icon_fonts + * @return void + */ + private function enqueue_icon_fonts( $icon_fonts ) { + $icons_types = Icons_Manager::get_icon_manager_tabs(); + + foreach ( $icon_fonts as $icon_font ) { + if ( ! isset( $icons_types[ $icon_font ] ) ) { + continue; + } + Plugin::elementor()->frontend->enqueue_font( $icon_font ); + } + } + + private function enqueue_font_links() { + $meta = $this->get_meta(); + + if ( Base::CSS_STATUS_EMPTY === $meta['status'] ) { + return; + } + + // First time after clear cache etc. + if ( '' === $meta['status'] || $this->is_update_required() ) { + $this->update(); + + $meta = $this->get_meta(); + } + + // Handle fonts. + if ( ! empty( $meta['fonts'] ) ) { + $this->enqueue_fonts( $meta['fonts'] ); + } + + if ( ! empty( $meta['icons'] ) ) { + $this->enqueue_icon_fonts( $meta['icons'] ); + } + } + + /** + * @param array $early_access_google_fonts + * @return void + */ + private function print_early_access_google_font_link_tags( array $early_access_google_fonts ) { + $early_access_google_fonts_urls = Plugin::elementor()->frontend->get_early_access_google_font_urls( $early_access_google_fonts ); + + foreach ( $early_access_google_fonts_urls as $font_url ) { + echo ''; + } + } + + private function print_fonts_links() { + $google_fonts = Plugin::elementor()->frontend->get_list_of_google_fonts_by_type(); + + if ( ! empty( $google_fonts['google'] ) ) { + $stable_google_fonts_url = Plugin::elementor()->frontend->get_stable_google_fonts_url( $google_fonts['google'] ); + + echo ''; + } + + if ( ! empty( $google_fonts['early'] ) ) { + $this->print_early_access_google_font_link_tags( $google_fonts['early'] ); + } + } + + public function enqueue_and_print_font_links() { + $this->enqueue_font_links(); + + $this->print_fonts_links(); + } + + public function print_all_css( int $post_id ) { + // Avoid re-print CSS + if ( isset( self::$printed_with_css[ $this->get_file_handle_id() ] ) ) { + return; + } + + $template_custom_css = $post_id > 0 ? $this->get_custom_css( $post_id ) : ''; + + echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + if ( Plugin::elementor()->editor->is_edit_mode() && method_exists( Plugin::elementor()->frontend, 'get_list_of_google_fonts_by_type' ) ) { + $this->enqueue_and_print_font_links(); + } + + // Avoid re-print CSS + self::$printed_with_css[ $this->get_file_handle_id() ] = true; + } + + private function get_custom_css( $post_id ) { + $loop_doc = Plugin::elementor()->documents->get( $post_id ); + return $loop_doc->get_settings( 'custom_css' ); + } + + public function print_css() { + $this->print_all_css( 0 ); + } +} diff --git a/modules/loop-builder/files/css/loop-dynamic-css.php b/modules/loop-builder/files/css/loop-dynamic-css.php new file mode 100644 index 0000000..70eb2aa --- /dev/null +++ b/modules/loop-builder/files/css/loop-dynamic-css.php @@ -0,0 +1,23 @@ +post_id_for_data = $post_id_for_data; + + $post_css_file = Post::create( $post_id_for_data ); + + parent::__construct( $post_id, $post_css_file ); + } + + public function get_post_id_for_data() { + return $this->post_id_for_data; + } +} diff --git a/modules/loop-builder/files/css/loop-preview.php b/modules/loop-builder/files/css/loop-preview.php new file mode 100644 index 0000000..3ed32d4 --- /dev/null +++ b/modules/loop-builder/files/css/loop-preview.php @@ -0,0 +1,27 @@ +post_id; + } + + /** + * Loop CSS file constructor. + * + * Initializing the CSS file of the loop widget. Set the post ID and initiate the stylesheet. + * + * @since 1.2.0 + * @access public + * + * @param int $post_id Post ID. + */ + public function __construct( $loop_template_id ) { + $this->post_id = $loop_template_id; + + parent::__construct( $loop_template_id ); + } +} diff --git a/modules/loop-builder/module.php b/modules/loop-builder/module.php new file mode 100644 index 0000000..7264bd7 --- /dev/null +++ b/modules/loop-builder/module.php @@ -0,0 +1,299 @@ +register_documents( $documents_manager ); + }, 11 /* After WC documents */ ); + + add_filter( 'elementor/finder/categories', [ $this, 'add_finder_items' ] ); + add_action( 'elementor/template-library/create_new_dialog_fields', [ $this, 'add_posts_type_to_template_popup' ], 10 ); + + add_action( 'elementor/template-library/create_new_dialog_fields', [ $this, 'add_taxonomies_type_to_template_popup' ], 12 ); + add_action( 'elementor-pro/modules/loop-builder/documents/loop/query_settings', [ $this, 'add_taxonomies_type_to_loop_settings_query' ], 12 ); + + add_filter( 'elementor/frontend/builder_content_data', [ $this, 'filter_content_data' ], 10, 2 ); + + add_action( 'manage_' . Source_Local::CPT . '_posts_columns', function ( $columns ) { + return $this->manage_posts_columns( $columns ); + } ); + + add_action('elementor/editor/init', function () { + Plugin::elementor()->common->add_template( __DIR__ . '/views/cta-template.php' ); + }); + + if ( $this->is_loop_theme_builder() ) { + add_filter( 'template_include', [ $this, 'filter_template_to_canvas_view' ], 999 ); + add_filter( 'body_class', [ $this, 'filter_body_class' ] ); + } + + // Prevent enqueue default dynamic CSS for loop item templates + add_filter( 'elementor/css-file/dynamic/should_enqueue', + function ( $should_enqueue, $post_id ) { + if ( $this->is_loop_item_document_type_meta_key( $post_id ) ) { + $should_enqueue = false; + } + return $should_enqueue; + }, + 10, 2 ); + + // Prevent enqueue default Post CSS for loop item templates + add_action( + 'elementor/css-file/post/enqueue', + function( $css_file ) { + $post_id = $css_file->get_post_id(); + $file_handle = 'elementor-post-' . $post_id; + + if ( $this->is_loop_item_document_type_meta_key( $post_id ) && wp_style_is( $file_handle, 'enqueued' ) ) { + wp_dequeue_style( $file_handle ); + } + } + ); + + add_filter( 'elementor/editor/localize_settings', function ( $config ) { + $config['admin_url'] = admin_url(); + return $config; + } ); + + foreach ( static::LOOP_WIDGETS as $widget_type ) { + add_action( 'elementor/widget/' . $widget_type . '/skins_init', function ( Widget_Base $widget ) { + $widget->add_skin( new Skin_Loop_Post_Taxonomy( $widget ) ); + }, 12 ); + } + } + + public function filter_template_to_canvas_view() { + return ELEMENTOR_PATH . 'modules/page-templates/templates/canvas.php'; + } + + private function get_preview_loop_item_id() { + $post_id = false; + + // Editor preview. + $post_id = Utils::_unstable_get_super_global_value( $_GET, 'elementor-preview' ); + + if ( ! $post_id ) { + $library_type = Utils::_unstable_get_super_global_value( $_GET, 'elementor_library' ); + + if ( 'elementor-' . self::TEMPLATE_LIBRARY_TYPE_SLUG === $library_type ) { + // Frontend Loop Item template preview. + $post_id = get_the_ID(); + } + } + + return $post_id; + } + + public function filter_body_class( $classes ) { + $classes[] = 'e-loop-template-canvas'; + + $post_id = $this->get_preview_loop_item_id(); + + if ( $post_id && 'product' === $this->get_source_type_from_post_meta( $post_id ) ) { + $classes[] = 'woocommerce'; + } + + return $classes; + } + + /** + * Filter content data. + * + * Determine whether we are in the Editor and are trying to Edit an empty loop template. + * + * If this is the case, we add some elements to the $data array in order for frontend.php + * to not 'return' an empty string and reach the print_elements_with_wrapper() function. + * + * We then override print_elements_with_wrapper() in the loop document using the variables + * we added here. + * + * @since 3.8.0 + * + * @param array $data + * @param int $post_id + * + * @return mixed + */ + public function filter_content_data( $data, $post_id ) { + if ( + empty( $data ) + && LoopDocument::get_type() === get_post_meta( $post_id, Document::TYPE_META_KEY, true ) + && wp_doing_ajax() + ) { + $data['empty_loop_template'] = true; + $data['empty_loop_template_id'] = $post_id; + } + + return $data; + } + + public function add_finder_items( array $categories ) { + $categories['create']['items']['loop-template'] = [ + 'title' => esc_html__( 'Add New Loop Template', 'elementor-pro' ), + 'icon' => 'plus-circle-o', + 'url' => $this->get_admin_templates_url() . '#add_new', + 'keywords' => [ 'template', 'theme', 'new', 'create', 'loop', 'dynamic', 'listing', 'archive', 'repeater' ], + ]; + + return $categories; + } + + public function add_posts_type_to_template_popup( $form ) { + if ( empty( $form ) ) { + return; + } + $form->add_control( '_elementor_source', [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Choose source type', 'elementor-pro' ), + 'options' => $this->get_post_type_options(), + 'section' => 'main', + 'required' => true, + 'conditions' => [ + 'template-type' => self::TEMPLATE_LIBRARY_TYPE_SLUG, + ], + ] ); + } + + public function get_source_type_from_post_meta( $post_id ) { + $source_type = get_post_meta( intval( $post_id ), '_elementor_source', true ); + return empty( $source_type ) ? 'post' : $source_type; + } + + private function is_editing_existing_loop_item() { + //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required. + $elementor_library = Utils::_unstable_get_super_global_value( $_GET, 'elementor_library' ) ?? ''; + //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required. + $post_id = Utils::_unstable_get_super_global_value( $_GET, 'elementor-preview' ); + + return ! empty( $elementor_library ) && $this->is_loop_item_document_type_meta_key( $post_id ); + } + + private function is_creating_new_loop_item() { + //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required. + $post_type = Utils::_unstable_get_super_global_value( $_GET, 'post_type' ); + //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required. + $post_id = Utils::_unstable_get_super_global_value( $_GET, 'p' ); + return 'elementor_library' === $post_type && $this->is_loop_item_document_type_meta_key( $post_id ); + } + + private function is_loop_item_document_type_meta_key( $post_id ) { + return static::TEMPLATE_LIBRARY_TYPE_SLUG === get_post_meta( $post_id, Document::TYPE_META_KEY, true ); + } + + private function is_loop_theme_builder() { + return $this->is_editing_existing_loop_item() || $this->is_creating_new_loop_item(); + } + + /** + * @param Documents_Manager $documents_manager + */ + private function register_documents( $documents_manager ) { + $documents_manager->register_document_type( Documents\Loop::get_type(), Documents\Loop::get_class_full_name() ); + } + + private function get_admin_templates_url( $relative = false ) { + $base_url = Source_Local::ADMIN_MENU_SLUG; + if ( ! $relative ) { + $base_url = admin_url( $base_url ); + } + + return add_query_arg( + [ + 'tabs_group' => 'theme', + 'elementor_library_type' => self::TEMPLATE_LIBRARY_TYPE_SLUG, + ], + $base_url + ); + } + + private function manage_posts_columns( $columns ) { + //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required. + $taxonomy_type_slug = Utils::_unstable_get_super_global_value( $_REQUEST, Source_Local::TAXONOMY_TYPE_SLUG ); + if ( self::TEMPLATE_LIBRARY_TYPE_SLUG === $taxonomy_type_slug ) { + unset( $columns['instances'] ); + } + + return $columns; + } + + private function get_post_type_options() { + $options = [ self::LOOP_POST_SKIN_ID => esc_html__( 'Posts', 'elementor-pro' ) ]; + + return $options; + } + + public function add_taxonomies_type_to_template_popup( $form ) { + $this->add_taxonomies_to_options( $form, '_elementor_source' ); + } + + public function add_taxonomies_type_to_loop_settings_query( $form ) { + $this->add_taxonomies_to_options( $form, 'source' ); + } + + protected function add_taxonomies_to_options( $form, $control_name ) { + $controls = $form->get_controls( $control_name ); + + if ( ! $controls || ! isset( $controls['options'] ) ) { + return; + } + + $options = $controls['options']; + $options[ self::LOOP_POST_TAXONOMY_SKIN_ID ] = esc_html__( 'Post Taxonomy', 'elementor-pro' ); + + $form->update_control($control_name, [ + 'options' => $options, + ] ); + } +} diff --git a/modules/loop-builder/providers/taxonomy-loop-provider.php b/modules/loop-builder/providers/taxonomy-loop-provider.php new file mode 100644 index 0000000..554da27 --- /dev/null +++ b/modules/loop-builder/providers/taxonomy-loop-provider.php @@ -0,0 +1,388 @@ +skin_id = $skin_id; + $this->default_source_type = $default_source_type; + } + + public function get_query_settings( array $display_settings ): array { + $taxonomy_type = $display_settings[ $this->get_query_property_name( self::POST_TYPE ) ] ?? self::POST_CATEGORY_TAXONOMY; + $term_taxonomy_id = $display_settings[ $this->get_property_name( self::QUERY_ID ) ]; + $depth = (int) $display_settings[ $this->get_property_name( self::QUERY_DEPTH ) ]; + $hierarchical = $display_settings[ $this->get_property_name( self::HIERARCHICAL ) ]; + + $settings = [ + 'taxonomy' => empty( $taxonomy_type ) ? $this->default_source_type : $taxonomy_type, + 'show_empty_items' => 'yes' === $display_settings[ $this->get_property_name( self::HIDE_EMPTY ) ] ? 'no' : 'yes', + 'include' => $display_settings[ $this->get_property_name( self::INCLUDE ) ], + 'exclude' => $display_settings[ $this->get_property_name( self::EXCLUDE ) ], + 'term_taxonomy_id' => strlen( $term_taxonomy_id ) ? $term_taxonomy_id : null, + 'child_of' => $display_settings[ $this->get_property_name( self::PARENT ) ], + 'offset' => $display_settings[ $this->get_property_name( self::OFFSET ) ], + 'number' => $display_settings['posts_per_page'], + 'orderby' => $display_settings[ $this->get_property_name( self::ORDER_BY ) ], + 'order' => $display_settings[ $this->get_property_name( self::ORDER ) ], + 'avoid_reset_parent' => true, + 'show_child_taxonomy' => 'no', + ]; + + if ( $depth && 'yes' === $hierarchical ) { + $settings['hierarchical'] = 'yes'; + $settings['child_taxonomy_depth'] = $depth - 1; + $settings['show_child_taxonomy'] = 'yes'; + } + + return $settings; + } + + public function get_control_args( string $key, bool $is_prefixed ): array { + $tabs_wrapper = $this->get_query_property_name( self::TABS_WRAPPER ); + $include_tab = $this->get_query_property_name( self::INCLUDE_TAB ); + $exclude_tab = $this->get_query_property_name( self::EXCLUDE_TAB ); + $include_exclude_conditions = $this->get_include_exclude_conditions( $is_prefixed ); + $filter_related_conditions = $this->get_depth_related_filter_conditions( $is_prefixed, false ); + + $post_type_key = $is_prefixed ? $this->get_query_property_name( self::POST_TYPE ) : self::POST_TYPE; + $filter_by_key = $is_prefixed ? $this->get_query_property_name( self::FILTER_BY ) : self::FILTER_BY; + $hierarchical_key = $is_prefixed ? $this->get_property_name( self::HIERARCHICAL ) : self::HIERARCHICAL; + + $filter_by_parent_term = Utils::format_control_condition( $filter_by_key, '===', self::PARENT ); + $is_hierarchical_term = Utils::format_control_condition( $hierarchical_key, '===', 'yes' ); + + $control_options = [ + self::FILTER_BY => [ + 'label' => esc_html__( 'Filter By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'show_all', + 'options' => [ + self::SHOW_ALL => esc_html__( 'Show All', 'elementor-pro' ), + self::MANUAL_SELECTION => esc_html__( 'Manual Selection', 'elementor-pro' ), + ], + 'conditions' => $filter_related_conditions, + ], + self::PARENT => [ + 'label' => esc_html__( 'Parent', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ 1, 2, 3, 4 ], + 'conditions' => [ + 'relation' => 'and', + 'terms' => $this->get_related_tags_conditions( $post_type_key, '!==', [ $filter_by_parent_term ] ), + ], + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_tab, + ], + self::INCLUDE_TAB => [ + 'type' => Controls_Manager::TAB, + 'label' => esc_html__( 'Include', 'elementor-pro' ), + 'tabs_wrapper' => $tabs_wrapper, + 'conditions' => $include_exclude_conditions, + ], + self::EXCLUDE_TAB => [ + 'type' => Controls_Manager::TAB, + 'label' => esc_html__( 'Exclude', 'elementor-pro' ), + 'tabs_wrapper' => $tabs_wrapper, + 'conditions' => $include_exclude_conditions, + ], + self::INCLUDE => [ + 'label' => esc_html__( 'Include By', 'elementor-pro' ), + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'label_block' => true, + 'multiple' => true, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_TAX, + 'autocomplete' => [], + ], + 'default' => '', + 'conditions' => $include_exclude_conditions, + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_tab, + ], + self::EXCLUDE => [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'label_block' => true, + 'multiple' => true, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_TAX, + ], + 'default' => '', + 'conditions' => $include_exclude_conditions, + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $exclude_tab, + ], + self::ORDER_BY => [ + 'label' => esc_html__( 'Order By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => self::ORDER_BY_NAME, + 'options' => [ + self::ORDER_BY_NAME => esc_html__( 'Name', 'elementor-pro' ), + self::ORDER_BY_ID => esc_html__( 'ID', 'elementor-pro' ), + ], + 'separator' => 'before', + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_tab, + 'condition' => [], + ], + self::ORDER => [ + 'label' => esc_html__( 'Order', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => self::DESC_ORDER, + 'options' => [ + self::ASC_ORDER => esc_html__( 'ASC', 'elementor-pro' ), + self::DESC_ORDER => esc_html__( 'DESC', 'elementor-pro' ), + ], + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_tab, + 'condition' => [], + ], + self::AVOID_DUPLICATES => [ + 'label' => esc_html__( 'Avoid Duplicates', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'no', + 'description' => esc_html__( 'Set to Yes to avoid duplicate posts from showing up. This only effects the frontend.', 'elementor-pro' ), + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $exclude_tab, + ], + self::HIDE_EMPTY => [ + 'label' => esc_html__( 'Hide Empty', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'no', + 'separator' => 'before', + 'condition' => [], + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_tab, + ], + self::OFFSET => [ + 'label' => esc_html__( 'Skip Taxonomy', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 0, + 'description' => esc_html__( 'Start grid from chosen taxonomy', 'elementor-pro' ), + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $exclude_tab, + ], + self::HIERARCHICAL => [ + 'label' => esc_html__( 'Filter by depth', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'no', + 'conditions' => $this->get_depth_related_filter_conditions( $is_prefixed, true ), + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_tab, + ], + self::QUERY_DEPTH => [ + 'label' => esc_html__( 'Depth', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '0', + 'options' => [ + '0' => esc_html__( 'All', 'elementor-pro' ), + '1' => esc_html__( '1', 'elementor-pro' ), + '2' => esc_html__( '2', 'elementor-pro' ), + '3' => esc_html__( '3', 'elementor-pro' ), + '4' => esc_html__( '4', 'elementor-pro' ), + '5' => esc_html__( '5', 'elementor-pro' ), + '6' => esc_html__( '6', 'elementor-pro' ), + ], + 'conditions' => $this->get_depth_related_filter_conditions( $is_prefixed, true, [ $is_hierarchical_term ] ), + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_tab, + ], + self::QUERY_ID => [ + 'label' => esc_html__( 'Query ID', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'ai' => [ + 'active' => false, + ], + 'description' => esc_html__( 'Give your Query a custom unique id to allow server side filtering', 'elementor-pro' ), + 'separator' => 'before', + 'dynamic' => [ + 'active' => true, + ], + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_tab, + ], + ]; + + return $control_options[ $key ] ?? $control_options; + } + + /** + * Get settings key names. + * + * Adds prefix to the desired key. + */ + public function get_property_name( string $key ): string { + return $this->skin_id . '_' . $key; + } + + /** + * Get query settings key names. + * + * Adds prefix and '_query_' to the desired key. + */ + public function get_query_property_name( string $key ): string { + return $this->get_property_name( 'query_' . $key ); + } + + public static function is_source_type_taxonomy( $source_type ) { + $available_source_types = self::get_loop_taxonomy_types(); + + return in_array( $source_type, $available_source_types, true ); + } + + public static function get_loop_taxonomy_types() { + return [ + LoopBuilderModule::LOOP_POST_TAXONOMY_SKIN_ID, + WoocommerceModule::LOOP_PRODUCT_TAXONOMY_SKIN_ID, + ]; + } + + public static function get_default_source_type( $taxonomy_loop_type, $prefix = '' ): string { + $options = [ + WoocommerceModule::LOOP_PRODUCT_TAXONOMY_SKIN_ID => self::PRODUCT_CATEGORY_TAXONOMY, + LoopBuilderModule::LOOP_POST_TAXONOMY_SKIN_ID => self::POST_CATEGORY_TAXONOMY, + ]; + + return isset( $options[ $taxonomy_loop_type ] ) + ? $prefix . $options[ $taxonomy_loop_type ] + : ''; + } + + public static function get_supported_cpts( $taxonomy_loop_type ) { + $post_types = array_merge( [ 'post' ], self::get_post_additional_cpts() ); + $options = [ + WoocommerceModule::LOOP_PRODUCT_TAXONOMY_SKIN_ID => [ 'product' ], + 'product' => [ 'product' ], + LoopBuilderModule::LOOP_POST_TAXONOMY_SKIN_ID => $post_types, + 'post' => $post_types, + ]; + + return $options[ $taxonomy_loop_type ] ?? []; + } + + private function get_include_exclude_conditions( bool $is_prefixed = false ): array { + $post_type_name = $is_prefixed ? $this->get_query_property_name( self::POST_TYPE ) : self::POST_TYPE; + $filter_by_key = $is_prefixed ? $this->get_query_property_name( self::FILTER_BY ) : self::FILTER_BY; + + $manual_selection_terms = Utils::format_control_condition( $filter_by_key, '===', self::MANUAL_SELECTION ); + + return [ + 'relation' => 'or', + 'terms' => $this->get_related_tags_conditions( $post_type_name, '===', [ $manual_selection_terms ] ), + ]; + } + + private function get_depth_related_filter_conditions( $is_prefixed, $should_add_hierarchy_condition, $extra_terms = [] ) { + $filter_by_key = $is_prefixed ? $this->get_query_property_name( self::FILTER_BY ) : self::FILTER_BY; + $post_type_key = $is_prefixed ? $this->get_query_property_name( self::POST_TYPE ) : self::POST_TYPE; + + $conditions = [ + 'relation' => 'and', + 'terms' => $this->get_related_tags_conditions( $post_type_key, '!==' ), + ]; + + if ( $should_add_hierarchy_condition ) { + $conditions['terms'][] = Utils::format_control_condition( $filter_by_key, '!==', self::MANUAL_SELECTION ); + } + + $conditions['terms'] = array_merge( $conditions['terms'], $extra_terms ); + + return $conditions; + } + + private function get_related_tags_conditions( $name, $operator, $extra_terms = [] ) { + $tags = [ self::POST_TAG_TAXONOMY, self::PRODUCT_TAG_TAXONOMY ]; + + return $this->get_related_taxonomy_condition_terms( $tags, $name, $operator, $extra_terms ); + } + + private function get_related_taxonomy_condition_terms( $value_options, $name, $operator, $extra_terms = [] ) { + $terms = []; + + foreach ( $value_options as $value ) { + $terms[] = Utils::format_control_condition( $name, $operator, $value ); + } + + return array_merge( $terms, $extra_terms ); + } + + public static function is_loop_taxonomy(): bool { + global $wp_query; + + return $wp_query->is_loop_taxonomy ?? false; + } + + public static function is_loop_taxonomy_strict(): bool { + global $wp_query; + + return self::is_loop_taxonomy() && ( $wp_query->loop_term ?? false ); + } + + private static function get_post_additional_cpts() { + $post_types = get_post_types( [ + 'public' => true, + '_builtin' => false, + ] ); + + return array_filter( $post_types, function( $post_type ) { + return ! in_array( $post_type, [ 'product', 'e-landing-page', 'elementor_library' ] ); + } ); + } +} diff --git a/modules/loop-builder/skins/skin-loop-base.php b/modules/loop-builder/skins/skin-loop-base.php new file mode 100644 index 0000000..87271e7 --- /dev/null +++ b/modules/loop-builder/skins/skin-loop-base.php @@ -0,0 +1,259 @@ +parent = $widget; + + $this->add_group_control( + Group_Control_Related::get_type(), + [ + 'name' => Module::QUERY_ID, + 'presets' => [ 'full' ], + 'exclude' => [ + 'posts_per_page', // Use the one from Layout section + ], + ] + ); + } + + protected function maybe_add_load_more_wrapper_class() { + $settings = $this->parent->get_settings_for_display(); + /** @var Loop_Widget_Base $widget */ + $widget = $this->parent; + + if ( isset( $settings['pagination_type'] ) && 'load_more_on_click' === $settings['pagination_type'] ) { + // If Pagination is enabled with the Load More On Click option, a class is needed for targeting. + // The 'wrapper' element tag is used by the Button Widget Trait. + $widget->add_render_attribute( 'wrapper', 'class', 'e-loop__load-more' ); + } + } + + public function query_posts() { + return $this->query_posts_for_alternate_templates(); + } + + /** + * Enqueue Loop Document CSS Meta + * + * Process the template before beginning to loop through the items. This ensures that + * elements with dynamic CSS are identified before each individual item is rendered. + * + * @param int $post_id + * + * @return void + */ + protected function enqueue_loop_document_css_meta( $post_id ) { + if ( $this->post_meta_css_exists( $post_id ) ) { + return; + } + + if ( wp_is_post_autosave( $post_id ) ) { + $css_file = Loop_Preview::create( $post_id ); + } else { + $css_file = Loop_CSS::create( $post_id ); + } + + /** @var Loop|Loop_Preview $css_file */ + $css_file->update(); + } + + private function post_meta_css_exists( $post_id ) { + return ! empty( get_post_meta( $post_id, Post_CSS::META_KEY ) ); + } + + public function render() { + $template_id = $this->parent->get_settings_for_display( 'template_id' ); + $is_edit_mode = Plugin::elementor()->editor->is_edit_mode(); + /** @var Loop_Widget_Base $widget */ + $widget = $this->parent; + $current_document = Plugin::elementor()->documents->get_current(); + + if ( $template_id ) { + $this->enqueue_loop_document_css_meta( $template_id ); + $this->alternate_template_before_skin_render(); + + $this->maybe_add_load_more_wrapper_class(); + + $widget->before_skin_render(); + + parent::render(); + + $widget->after_skin_render(); + + $this->alternate_template_after_skin_render(); + } elseif ( $is_edit_mode ) { + $this->render_empty_view(); + } + + if ( $current_document ) { + Plugin::elementor()->documents->switch_to_document( $current_document ); + } + } + + protected function handle_no_posts_found() { + $settings = $this->parent->get_settings_for_display(); + + ?> +
    + + < class="e-loop-nothing-found-message__text"> + + > +
    + parent; + + $classes = $widget->get_loop_header_widget_classes(); + + $classes[] = 'elementor-loop-container'; + + return $classes; + } + + protected function _register_controls_actions() { + add_action( 'elementor/element/' . $this->parent->get_name() . '/section_query/after_section_start', [ $this, 'register_query_controls' ] ); + } + + /** + * Render Post + * + * Uses the chosen custom template to render Loop posts. + * + * @since 3.8.0 + */ + protected function render_post() { + if ( $this->has_alternate_templates() ) { + $this->render_post_if_widget_has_alternate_templates(); + } else { + $this->render_post_content( $this->parent->get_settings_for_display( 'template_id' ) ); + } + } + + private function render_post_content( $template_id ) { + $post_id = get_the_ID(); + + /** @var LoopDocument $document */ + $document = Plugin::elementor()->documents->get( $template_id ); + + if ( ! $document ) { + return; + } + + $this->print_dynamic_css( $post_id, $template_id ); + $document->print_content(); + } + + protected function print_dynamic_css( $post_id, $post_id_for_data ) { + $document = Plugin::elementor()->documents->get_doc_for_frontend( $post_id_for_data ); + + if ( ! $document ) { + return; + } + + Plugin::elementor()->documents->switch_to_document( $document ); + + $css_file = Loop_Dynamic_CSS::create( $post_id, $post_id_for_data ); + $post_css = $css_file->get_content(); + + if ( empty( $post_css ) ) { + return; + } + + $css = ''; + $css = str_replace( '.elementor-' . $post_id, '.e-loop-item-' . $post_id, $post_css ); + $css = sprintf( '', 'loop-dynamic-' . $post_id_for_data, $css ); + + echo $css; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + + Plugin::elementor()->documents->restore_document(); + } + + protected function render_loop_header() { + /** @var Loop_Widget_Base $widget */ + $widget = $this->parent; + $config = $widget->get_config(); + + if ( $config['add_parent_render_header'] ) { + parent::render_loop_header(); + } + + $widget->render_loop_header(); + } + + protected function render_loop_footer() { + /** @var Loop_Widget_Base $widget */ + $widget = $this->parent; + $config = $widget->get_config(); + + if ( $config['add_parent_render_footer'] ) { + parent::render_loop_footer(); + } + + $widget->render_loop_footer(); + } + + /** + * Render Empty View + * + * Renders the Loop widget's view if there is no default template (empty view). + * + * @since 3.8.0 + */ + protected function render_empty_view() { + ?> +
    + parent; + + $this->enqueue_loop_document_css_meta( $template_id ); + $this->maybe_add_load_more_wrapper_class(); + $widget->before_skin_render(); + $this->render_loop_header(); + } + + protected function get_default_source_option() { + return Taxonomy_Loop_Provider::POST_CATEGORY_TAXONOMY; + } +} diff --git a/modules/loop-builder/skins/skin-loop-post.php b/modules/loop-builder/skins/skin-loop-post.php new file mode 100644 index 0000000..873e80c --- /dev/null +++ b/modules/loop-builder/skins/skin-loop-post.php @@ -0,0 +1,19 @@ +parent = $widget; + } + + protected function init_provider() { + $this->provider = new Taxonomy_Loop_Provider( $this->get_id(), $this->get_default_source_option() ); + } + + public function render() { + $template_id = $this->parent->get_settings_for_display( 'template_id' ); + $is_edit_mode = Plugin::elementor()->editor->is_edit_mode(); + $current_document = Plugin::elementor()->documents->get_current(); + + if ( $template_id ) { + $this->prepare_template_loop( $this->get_terms(), $template_id ); + } elseif ( $is_edit_mode ) { + $this->render_empty_view(); + } + + if ( $current_document ) { + Plugin::elementor()->documents->switch_to_document( $current_document ); + } + } + + protected function prepare_template_loop( $terms, $template_id ) { + global $wp_query; + $wp_query->is_loop_taxonomy = ! empty( $terms ); + + $this->render_before_loop( $template_id ); + $this->render_loop_content( $terms, $template_id ); + $this->render_loop_end(); + + $wp_query->is_loop_taxonomy = false; + } + + protected function render_before_loop( $template_id ) { + $widget = $this->parent; + + $this->enqueue_loop_document_css_meta( $template_id ); + $this->maybe_add_load_more_wrapper_class(); + $widget->before_skin_render(); + $this->render_loop_header(); + } + + protected function render_loop_content( $terms, $template_id ) { + global $wp_query; + + foreach ( $terms as $term ) { + $wp_query->loop_term = $term; + $this->render_post(); + $wp_query->loop_term = null; + } + } + + /** + * Render Post + * + * Uses the chosen custom template to render Loop posts. + * + * @since 3.8.0 + */ + protected function render_post() { + $template_id = $this->parent->get_settings_for_display( 'template_id' ); + + /** @var LoopDocument $document */ + $document = Plugin::elementor()->documents->get( $template_id ); + + if ( ! $document ) { + return; + } + + $this->print_dynamic_css( $this->get_data_id_from_taxonomy_loop_query(), $template_id ); + $document->print_content(); + } + + protected function render_loop_end() { + /** @var Loop_Widget_Base $widget */ + $widget = $this->parent; + + $this->render_loop_footer(); + $widget->after_skin_render(); + } + + /** + * Register Query Controls + * + * Registers the controls for the query used by the Loop. + * + * @since 3.8.0 + */ + public function register_query_controls( Loop_Widget_Base $widget ) { + $this->init_parent( $widget ); + $this->init_provider(); + $post_type_options = $this->get_taxonomy_options( ( Taxonomy_Loop_Provider::get_supported_cpts( $this->post_type ) ) ); + $default_source_type = $this->get_default_source_option(); + + $this->add_group_control( + Group_Control_Taxonomy::get_type(), + [ + 'name' => Taxonomy_Loop_Provider::QUERY_CONTROL_GROUP_NAME, + Taxonomy_Loop_Provider::POST_TYPE => Taxonomy_Loop_Provider::get_default_source_type( $this->get_id() ), + 'presets' => [ 'include', 'exclude' ], + 'fields_options' => [ + Taxonomy_Loop_Provider::POST_TYPE => [ + 'default' => $default_source_type, + 'options' => $post_type_options, + ], + Taxonomy_Loop_Provider::FILTER_BY => $this->provider->get_control_args( Taxonomy_Loop_Provider::FILTER_BY, false ), + Taxonomy_Loop_Provider::INCLUDE_TAB => $this->provider->get_control_args( Taxonomy_Loop_Provider::INCLUDE_TAB, false ), + Taxonomy_Loop_Provider::EXCLUDE_TAB => $this->provider->get_control_args( Taxonomy_Loop_Provider::EXCLUDE_TAB, false ), + ], + 'exclude' => [ + 'include', + 'exclude', + ], + ], + ); + + // Filtering controls + $this->add_query_control( Taxonomy_Loop_Provider::PARENT, true ); + $this->add_query_control( Taxonomy_Loop_Provider::INCLUDE, true ); + $this->add_query_control( Taxonomy_Loop_Provider::EXCLUDE, true ); + + // Order controls + $this->add_query_control( Taxonomy_Loop_Provider::ORDER_BY, true ); + $this->add_query_control( Taxonomy_Loop_Provider::ORDER, true ); + + // Result exclusion controls + $this->add_query_control( Taxonomy_Loop_Provider::AVOID_DUPLICATES, true ); + $this->add_query_control( Taxonomy_Loop_Provider::HIDE_EMPTY, true ); + $this->add_query_control( Taxonomy_Loop_Provider::OFFSET, true ); + + // Hierarchical controls + $this->add_query_control( Taxonomy_Loop_Provider::HIERARCHICAL, true ); + $this->add_query_control( Taxonomy_Loop_Provider::QUERY_DEPTH, true ); + + // Result inclusion controls + $this->add_query_control( Taxonomy_Loop_Provider::QUERY_ID, true ); + } + + /** + * Returns desired taxonomy items. + * + * Uses control values from get_settings_for_display. + */ + protected function get_terms() { + $display_settings = $this->parent->get_settings_for_display(); + $settings = $this->provider->get_query_settings( $display_settings ); + + add_filter( 'elementor/loop_taxonomy/args', [ $this, 'filter_loop_taxonomy_args' ], 10, 3 ); + + $terms = $this->get_filtered_taxonomies( $settings, $settings ); + + remove_filter( 'elementor/loop_taxonomy/args', [ $this, 'filter_loop_taxonomy_args' ] ); + + Module::add_to_taxonomies_avoid_list( wp_list_pluck( $terms, 'term_id' ) ); + + return $terms; + } + + public function filter_loop_taxonomy_args( $args, $settings, $display_settings ) { + $avoid_duplicates_key = $this->provider->get_property_name( 'avoid_duplicates' ); + $current_settings = $this->parent->get_settings_for_display(); + + $should_avoid_duplicates = isset( $current_settings[ $avoid_duplicates_key ] ) && + 'yes' === $current_settings[ $avoid_duplicates_key ]; + + if ( $should_avoid_duplicates ) { + $exclude = isset( $args['exclude'] ) && is_array( $args['exclude'] ) ? $args['exclude'] : []; + $exclude = array_merge( $exclude, Module::get_taxonomies_avoid_list_ids() ); + $args['exclude'] = $exclude; + } + + return $args; + } + + private function add_query_control( $control_id, $is_prefixed ) { + $this->add_control( $control_id, $this->provider->get_control_args( $control_id, $is_prefixed ) ); + } +} diff --git a/modules/loop-builder/traits/alternate-templates-trait.php b/modules/loop-builder/traits/alternate-templates-trait.php new file mode 100644 index 0000000..ee9a4a0 --- /dev/null +++ b/modules/loop-builder/traits/alternate-templates-trait.php @@ -0,0 +1,560 @@ +found_posts` + * and `$query->max_num_pages` so the pagination, and any standard WP Query elements, display as expected. + * + * @return false|\WP_Query + */ + public function query_posts_for_alternate_templates() { + // If there are no static alternate templates, no need to modify the query. + if ( ! $this->has_static_alternate_templates() ) { + return false; + } + + $widget = $this->parent; + + /** @var QueryControlModule $elementor_query */ + $elementor_query = QueryControlModule::instance(); + + /** + * Construct the `static_alternate_template_query_data` used for the new query and when rendering each post. + */ + $query = $elementor_query->get_query( $widget, $widget->get_query_name(), [ + 'posts_per_page' => 1, + 'paged' => 1, + ] ); + $this->init_static_alternate_template_query_data( $query->found_posts ); + + if ( ! $this->query_contains_static_alternate_templates() ) { + return false; + } + + $adjusted_found_posts = $this->get_static_alternate_template_adjusted_found_posts(); + $adjusted_max_num_pages = $this->get_static_alternate_template_adjusted_max_num_pages(); + + /** + * New query using `offset` in place of `paged`. + */ + $query = $elementor_query->get_query( $widget, $widget->get_query_name(), [ + 'posts_per_page' => $widget->get_posts_per_page_value(), + 'offset' => $this->get_static_alternate_template_query_offset(), + ] ); + + /** + * Increase size of the query using the adjusted values calculated after + * constructing `static_alternate_template_query_data`. + */ + $query->found_posts = $adjusted_found_posts; + $query->max_num_pages = $adjusted_max_num_pages; + + return $query; + } + + /** + * Init Alternate Templates Settings + * + * Improve performance by storing the `alternate_templates` repeater settings, so we don't use + * `get_settings_for_display()` each time we check if a post should use an alternate template. + * + * At the same time we store `$has_alternate_templates` and `$has_static_alternate_templates` used by their + * accompanying helper functions, for efficiency, and so we don't need to loop through the `alternate_templates` + * each time we check. + * + * We also re-arrange the `alternate_templates` array for two reasons: + * (1) The last template added by the user should take preference, so we reverse the array so that when we loop + * through the repeater settings array to check if a post should use an alternate template, we find the last + * added template first. + * (2) 'Static' alternate templates should take preference over 'non-static' templates, so we group all static + * templates before first, so when we loop through the array to check if a post should use an alternate template + * we find the 'static' template first. + * + * @return void + */ + private function init_alternate_template_settings(): void { + $settings = $this->parent->get_settings_for_display(); + + if ( empty( $settings['alternate_templates'] ) || empty( $settings['template_id'] ) ) { + return; + } + + // Note that the user has added alternate templates. + $this->has_alternate_templates = true; + + // Store the alternate templates. + $alternate_templates = $settings['alternate_templates']; + + // Reverse the alternate template settings so the last template added by the user takes preference over + // the previous templates. + $alternate_templates = array_reverse( $alternate_templates ); + + // Rearrange the alternate template settings to group all static templates before the standard templates. + $static_alternate_templates = []; + $standard_alternate_templates = []; + foreach ( $alternate_templates as $alternate_template ) { + // Skip the alternate template from any calculations until a repeat template number is specified. + if ( empty( $alternate_template['repeat_template'] ) ) { + continue; + } + + if ( $this->is_alternate_template_static_position( $alternate_template ) ) { + $static_alternate_templates[ $alternate_template['_id'] ] = $alternate_template; + + // Note that the user has added 'static' alternate templates. + $this->has_static_alternate_templates = true; + } else { + $standard_alternate_templates[ $alternate_template['_id'] ] = $alternate_template; + } + } + + $this->alternate_templates = $static_alternate_templates + $standard_alternate_templates; + } + + private function alternate_template_before_skin_render(): void { + // Init Alternate Template Settings should occur as early in the load order as possible as it's responsible + // for setting flags used in helper functions, that can be called quite early in the widget + // e.g. `has_alternate_templates()` & `has_static_alternate_templates()`. + $this->init_alternate_template_settings(); + + if ( ! $this->has_alternate_templates() ) { + return; + } + + $this->maybe_add_alternate_template_wrapper_classes(); + } + + private function alternate_template_after_skin_render(): void { + if ( ! $this->has_alternate_templates() ) { + return; + } + + $this->reset_alternate_template_data(); + $this->maybe_remove_alternate_template_wrapper_classes(); + } + + /** + * @return void + */ + private function reset_alternate_template_data() { + // Reset all the Alternate Template data at the end of the widget so that it does not affect any widgets + // below the current widget. + $this->current_post_index = 0; + $this->alternate_templates = []; + $this->rendered_alternate_templates = []; + $this->has_alternate_templates = false; + $this->has_static_alternate_templates = false; + $this->query_contains_static_alternate_templates = false; + $this->static_alternate_template_query_data = []; + } + + /** + * @return void + */ + private function maybe_add_alternate_template_wrapper_classes(): void { + add_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_alternate_template_wrapper_classes' ] ); + + if ( Plugin::elementor()->editor->is_edit_mode() ) { + add_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_alternate_template_editor_wrapper_classes' ] ); + } + } + + /** + * @return void + */ + private function maybe_remove_alternate_template_wrapper_classes(): void { + remove_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_alternate_template_wrapper_classes' ] ); + + if ( Plugin::elementor()->editor->is_edit_mode() ) { + remove_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_alternate_template_editor_wrapper_classes' ] ); + } + } + + /** + * @param $attributes + * @return array + */ + public function add_alternate_template_wrapper_classes( $attributes ): array { + $template = $this->get_template_data_for_current_post(); + + if ( $this->is_alternate_template( $template ) ) { + $attributes['class'] .= ' elementor-repeater-item-' . $template['_id']; + } + + return $attributes; + } + + /** + * @param $attributes + * @return array + */ + public function add_alternate_template_editor_wrapper_classes( $attributes ): array { + $template = $this->get_template_data_for_current_post(); + + if ( $this->is_alternate_template( $template ) && $this->is_alternate_template_first_occurrence( $template ) ) { + $attributes['class'] .= ' e-loop-alternate-template'; + } + + return $attributes; + } + + /** + * @return void + */ + private function render_post_if_widget_has_alternate_templates(): void { + // If any static templates are rendered they will result in this function being called recursively, so we need + // to make sure we don't render more posts than the user has chosen in their `posts_per_page` widget control. + if ( $this->current_post_index >= $this->parent->get_posts_per_page_value() ) { + return; + } + + $template = $this->get_template_data_for_current_post(); + $this->render_post_content( $template['template_id'] ); + $this->store_rendered_alternate_templates( $template['template_id'] ); + $this->current_post_index++; + + // If the current post has a 'static' alternate template the above will render an extra empty post in the loop. + // We need to render this post again (with incremented `current_post_index`) so it is not skipped as a result + // of a static template. + if ( $this->is_alternate_template_static_position( $template ) ) { + $this->render_post_if_widget_has_alternate_templates(); + } + } + + /** + * @param $template_id + * @return void + */ + private function store_rendered_alternate_templates( $template_id ): void { + if ( ! in_array( $template_id, $this->rendered_alternate_templates, true ) ) { + $this->rendered_alternate_templates[] = $template_id; + } + } + + /** + * Has Alternate Templates + * + * Has the user added any alternate templates to the widget. + * + * Improve performance by storing `has_alternate_templates` once when the widget is rendered + * to avoid using `get_settings_for_display()` each time. + * + * @return bool + */ + private function has_alternate_templates(): bool { + return $this->has_alternate_templates; + } + + /** + * Has Static Alternate Templates + * + * Has the user added any 'static' alternate templates to the widget. + * + * Improve performance by storing `has_static_alternate_templates` once when the widget is rendered + * to avoid iterating through the repeater settings each time. + * + * @return bool + */ + private function has_static_alternate_templates(): bool { + return $this->has_static_alternate_templates; + } + + /** + * Query Contains Static Alternate Templates + * + * After constructing the `init_static_alternate_template_query_data` array, we want to make sure - based on + * the alternate templates settings - that we definitely have valid 'static' alternate templates to display. + * + * This flag is used to avoid modifying the query or re-rendering posts if we don't have to. + * + * @return bool + */ + private function query_contains_static_alternate_templates(): bool { + return $this->query_contains_static_alternate_templates; + } + + /** + * Init Static Alternate Template Query Data + * + * Construct `static_alternate_template_query_data` if the user has added any valid 'static' alternate templates. + * Used to modify the widget query and when rendering a post. + * + * @param $query + * @return void + */ + private function init_static_alternate_template_query_data( $required_posts_count ): void { + $this->static_alternate_template_query_data = [ + 'templates' => [], + 'page_settings' => [], + ]; + + $posts_per_page = $this->parent->get_posts_per_page_value(); + $static_alternate_template_count = 0; + + for ( $current_index = 0; $current_index < $required_posts_count; $current_index++ ) { + $template = $this->get_template_data_by_index( $current_index ); + $this->set_static_alternate_template_query_data_item( $template, $current_index, $static_alternate_template_count, $posts_per_page ); + + if ( 'yes' === $template['static_position'] ) { + $static_alternate_template_count++; + $required_posts_count++; + } + } + + // Note if any valid 'static' alternate templates need to be displayed, after the above calculations. + $this->query_contains_static_alternate_templates = 0 < $static_alternate_template_count; + } + + /** + * Set Static Alternate Template Query Data Item + * + * Store which template to use when each post is rendered. + * + * `init_static_alternate_template_query_data()` stores each template's data in the + * `static_alternate_template_query_data['templates']` and, at the same time, stores + * `static_alternate_template_query_data['page_settings']` used to adjust the query. + * + * @param $template + * @param $current_post_index + * @param $static_alternate_template_count + * @param $posts_per_page + * @return void + */ + private function set_static_alternate_template_query_data_item( $template, $current_post_index, $static_alternate_template_count, $posts_per_page ): void { + // Store template - used when we render the post. + $this->static_alternate_template_query_data['templates'][ $current_post_index ] = $template; + + // Store `page_settings`. + // `query_offset` is used when we query posts and `start_index` when we render posts. + $current_page = ceil( $current_post_index / $posts_per_page ) + 1; + $this->static_alternate_template_query_data['page_settings'][ $current_page ] = [ + 'query_offset' => $current_post_index - $static_alternate_template_count, + 'start_index' => $current_post_index, + ]; + } + + /** + * Get Static Alternate Template Current Page Settings + * + * Used to modify the widget query and when rendering a post. + * + * @return array|bool + */ + private function get_static_alternate_template_current_page_settings() { + $current_page = $this->parent->get_current_page(); + if ( empty( $this->static_alternate_template_query_data['page_settings'][ $current_page ] ) ) { + return false; + } + return $this->static_alternate_template_query_data['page_settings'][ $current_page ]; + } + + /** + * Get Static Alternate Template Query Offset + * + * Used to modify the widget query. + * + * @return array|bool + */ + private function get_static_alternate_template_query_offset() { + $current_page_settings = $this->get_static_alternate_template_current_page_settings(); + if ( ! $current_page_settings ) { + return false; + } + return $current_page_settings['query_offset']; + } + + /** + * Get Static Alternate Template Start Index + * + * Used when calling `render_post_if_widget_has_alternate_templates()`. + * + * @return array|bool + */ + private function get_static_alternate_template_start_index() { + $current_page_settings = $this->get_static_alternate_template_current_page_settings(); + if ( ! $current_page_settings ) { + return false; + } + return $current_page_settings['start_index']; + } + + /** + * Get Static Alternate Template Adjusted Found Posts + * + * Used to modify the widget query. + * + * @return int + */ + private function get_static_alternate_template_adjusted_found_posts(): int { + if ( empty( $this->static_alternate_template_query_data['templates'] ) ) { + return 0; + } + return count( $this->static_alternate_template_query_data['templates'] ); + } + + /** + * Get Static Alternate Template Adjusted Max Num Pages + * + * Used to modify the widget query. + * + * @return float + */ + private function get_static_alternate_template_adjusted_max_num_pages(): float { + return ceil( $this->get_static_alternate_template_adjusted_found_posts() / $this->parent->get_posts_per_page_value() ); + } + + /** + * Get Data For Static Alternate Template + * + * Used when rendering the current post. + * + * @param $index + * @return array + */ + private function get_data_for_static_alternate_template( $index ): array { + if ( ! empty( $this->static_alternate_template_query_data['templates'][ $index ] ) ) { + return $this->static_alternate_template_query_data['templates'][ $index ]; + } + return $this->get_default_template(); + } + + /** + * @return array + */ + private function get_template_data_for_current_post() { + $current_post_index = $this->get_current_post_index(); + + if ( $this->query_contains_static_alternate_templates() ) { + return $this->get_data_for_static_alternate_template( $current_post_index ); + } + + return $this->get_template_data_by_index( $current_post_index ); + } + + /** + * @return int + */ + private function get_current_post_index() { + if ( $this->query_contains_static_alternate_templates() ) { + return $this->get_static_alternate_template_start_index() + $this->current_post_index; + } + + return $this->parent->get_query()->current_post; + } + + /** + * @param $index + * @return array + */ + private function get_template_data_by_index( $index ): array { + if ( ! $this->has_alternate_templates() ) { + return $this->get_default_template(); + } + + foreach ( $this->alternate_templates as $alternate_template ) { + $found_alternate_template = $this->is_alternate_template_show_once( $alternate_template ) ? + $this->should_show_alternate_template_once( $alternate_template, $index ) : + $this->should_show_repeating_alternate_template( $alternate_template, $index ); + + if ( $found_alternate_template ) { + return [ + 'template_id' => $alternate_template['template_id'], + 'alternate_template' => 'yes', + 'static_position' => $alternate_template['static_position'] ? 'yes' : 'no', + '_id' => $alternate_template['_id'], + ]; + } + } + + return $this->get_default_template(); + } + + /** + * @return array + */ + private function get_default_template(): array { + return [ + 'template_id' => $this->parent->get_settings_for_display( 'template_id' ), + 'alternate_template' => 'no', + 'static_position' => 'no', + '_id' => '-', + ]; + } + + /** + * @param $alternate_template + * @return bool + */ + private function is_alternate_template( $alternate_template ): bool { + return isset( $alternate_template['alternate_template'] ) && 'yes' === $alternate_template['alternate_template']; + } + + /** + * @param $alternate_template + * @return bool + */ + private function is_alternate_template_static_position( $alternate_template ): bool { + return isset( $alternate_template['static_position'] ) && 'yes' === $alternate_template['static_position']; + } + + /** + * @param $alternate_template + * @return bool + */ + private function is_alternate_template_show_once( $alternate_template ): bool { + return isset( $alternate_template['show_once'] ) && 'yes' === $alternate_template['show_once']; + } + + /** + * @param $number_to_check + * @param $multiple_to_check + * @return bool + */ + private function is_repeating_alternate_template_multiple_of( $number_to_check, $multiple_to_check ): bool { + return 0 === $multiple_to_check % $number_to_check; + } + + /** + * @param $template + * @return bool + */ + private function is_alternate_template_first_occurrence( $template ): bool { + return ! in_array( $template['template_id'], $this->rendered_alternate_templates, true ); + } + + /** + * @param $alternate_template + * @param $current_item_index + * @return bool + */ + private function should_show_alternate_template_once( $alternate_template, $current_item_index ): bool { + return $alternate_template['repeat_template'] === $current_item_index + 1; + } + + /** + * @param $alternate_template + * @param $current_item_index + * @return bool + */ + private function should_show_repeating_alternate_template( $alternate_template, $current_item_index ): bool { + return $this->is_repeating_alternate_template_multiple_of( $alternate_template['repeat_template'], $current_item_index + 1 ); + } +} diff --git a/modules/loop-builder/views/cta-template.php b/modules/loop-builder/views/cta-template.php new file mode 100644 index 0000000..5e2888a --- /dev/null +++ b/modules/loop-builder/views/cta-template.php @@ -0,0 +1,80 @@ + + + + + diff --git a/modules/loop-builder/widgets/base.php b/modules/loop-builder/widgets/base.php new file mode 100644 index 0000000..bd13fcf --- /dev/null +++ b/modules/loop-builder/widgets/base.php @@ -0,0 +1,370 @@ +get_current_skin_id() ); + return $skin_id . '_' . Module::QUERY_ID; + } + + protected function get_initial_config() { + $config = parent::get_initial_config(); + + $config['is_loop'] = true; + $config['add_parent_render_header'] = true; + $config['add_parent_render_footer'] = true; + + return $config; + } + + public function query_posts() { + $skin = $this->get_current_skin(); + $query = false; + if ( $skin ) { + $query = $skin->query_posts( $this ); + } + + if ( $query ) { + $this->query = $query; + } else { + parent::query_posts(); + } + } + + /** + * Get Posts Per Page Value + * + * Returns the value of the Posts Per Page control of the widget. + * + * @since 3.8.0 + * @access protected + * + * @return mixed + */ + public function get_posts_per_page_value() { + return $this->get_settings_for_display( 'posts_per_page' ); + } + + protected function register_skins() { + $this->add_skin( new Skin_Loop_Post( $this ) ); + } + + protected function register_controls() { + $this->register_layout_section(); + $this->register_query_section(); + + // Carousel + $this->register_settings_section_controls(); + $this->register_navigation_section_controls(); + + $this->register_pagination_section_controls(); + $this->register_additional_options_section_controls(); + + $this->register_design_layout_controls(); + $this->register_design_nothing_found_message_controls(); + $this->register_design_navigation_controls(); + $this->register_design_pagination_controls(); + + // The `_skins` control determines the Loop's query source, so it is renamed for this to be clearer to the user. + $this->update_control( '_skin', [ + 'label' => esc_html__( 'Choose template type', 'elementor-pro' ), + 'label_block' => true, + 'frontend_available' => true, + ] ); + } + + /** + * Register Layout Section + * + * This registers the Layout section in order to allow Skins to register their layout controls. + * + * @since 3.8.0 + */ + protected function register_layout_section() { + $this->start_controls_section( + 'section_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'template_id', + [ + 'label' => esc_html__( 'Choose a template', 'elementor-pro' ), + 'type' => Template_Query::CONTROL_ID, + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_LIBRARY_TEMPLATE, + 'query' => [ + 'post_status' => Document::STATUS_PUBLISH, + 'meta_query' => [ + [ + 'key' => Document::TYPE_META_KEY, + 'value' => LoopDocument::get_type(), + 'compare' => 'IN', + ], + ], + ], + ], + 'actions' => [ + 'new' => [ + 'visible' => true, + 'document_config' => [ + 'type' => LoopDocument::get_type(), + ], + ], + 'edit' => [ + 'visible' => true, + ], + ], + 'frontend_available' => true, + ] + ); + + $this->end_controls_section(); + } + + /** + * Register Query Section + * + * This registers the Query section in order to allow Skins to register their query controls. + * + * @since 3.8.0 + */ + protected function register_query_section() { + $this->start_controls_section( + 'section_query', + [ + 'label' => esc_html__( 'Query', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->end_controls_section(); + } + + public function register_pagination_section_controls() { + parent::register_pagination_section_controls(); + + $this->remove_responsive_control( 'align' ); + + $this->start_injection( [ + 'of' => 'pagination_align', + 'at' => 'after', + ] ); + + $this->add_responsive_control( + 'load_more_button_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'default' => 'center', + 'condition' => [ + 'pagination_type' => 'load_more_on_click', + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => '--load-more-button-align: start;', + 'center' => '--load-more-button-align: center;', + 'end' => '--load-more-button-align: end;', + 'justify' => '--load-more-button-width: 100%;', + ], + ] + ); + + $this->add_control( + 'pagination_load_type', + [ + 'label' => esc_html__( 'Load Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'page_reload', + 'options' => [ + 'page_reload' => esc_html__( 'Page Reload', 'elementor-pro' ), + 'ajax' => esc_html__( 'AJAX', 'elementor-pro' ), + ], + 'frontend_available' => true, + 'condition' => [ + 'pagination_type' => [ + 'numbers', + 'prev_next', + 'numbers_and_prev_next', + ], + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'auto_scroll', + [ + 'label' => esc_html__( 'Autoscroll', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'condition' => [ + 'pagination_load_type' => [ + 'ajax', + ], + ], + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( + 'auto_scroll_offset', + [ + 'label' => esc_html__( 'Autoscroll offset', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 0, + 'selectors' => [ + '{{WRAPPER}}' => '--auto-scroll-offset: {{VALUE}}px;', + ], + 'condition' => [ + 'pagination_load_type' => [ + 'ajax', + ], + 'auto_scroll' => 'yes', + ], + ] + ); + + $this->end_injection(); + + // Remove the HTML Entity arrows inherited from the Posts widget from the prev/next pagination link labels. + $this->update_control( + 'pagination_prev_label', + [ + 'default' => esc_html__( 'Previous', 'elementor-pro' ), + ] + ); + + $this->update_control( + 'pagination_next_label', + [ + 'default' => esc_html__( 'Next', 'elementor-pro' ), + ] + ); + + $this->update_control( + 'pagination_individual_divider', + [ + 'condition' => [ + 'pagination_type' => [ + 'numbers', + 'numbers_and_prev_next', + 'prev_next', + ], + 'pagination_load_type' => [ + 'page_reload', + ], + ], + ] + ); + + $this->update_control( + 'pagination_individual_handle', + [ + 'condition' => [ + 'pagination_type' => [ + 'numbers', + 'numbers_and_prev_next', + 'prev_next', + ], + 'pagination_load_type' => [ + 'page_reload', + ], + ], + ] + ); + + $this->update_control( + 'pagination_individual_handle_message', + [ + 'raw' => esc_html__( 'For multiple Loop Widgets on the same page, toggle this on to control the pagination for each individually. Note: It affects the page\'s URL structure.', 'elementor-pro' ), + 'condition' => [ + 'pagination_type' => [ + 'numbers', + 'numbers_and_prev_next', + 'prev_next', + ], + 'pagination_load_type' => [ + 'page_reload', + ], + ], + ] + ); + } + + protected function register_additional_options_section_controls() {} + + protected function register_design_layout_controls() {} + + protected function register_design_nothing_found_message_controls() {} + + protected function register_design_navigation_controls() {} + + protected function register_design_pagination_controls() {} + + public function register_settings_section_controls() {} + + public function register_navigation_section_controls() {} + + public function get_loop_header_widget_classes(): array { + return []; + } + + public function render_loop_header() {} + + public function render_loop_footer() {} + + public function before_skin_render() {} + + public function after_skin_render() {} +} diff --git a/modules/loop-builder/widgets/loop-carousel.php b/modules/loop-builder/widgets/loop-carousel.php new file mode 100644 index 0000000..5d36dd0 --- /dev/null +++ b/modules/loop-builder/widgets/loop-carousel.php @@ -0,0 +1,249 @@ +add_carousel_settings_controls( [ + 'css_prefix' => '', + ] ); + } + + public function register_navigation_section_controls() { + $this->add_carousel_navigation_controls( [ + 'css_prefix' => '', + 'navigation_custom_settings' => [ + 'condition' => [ + 'template_id!' => '', + ], + ], + ] ); + } + + public function register_pagination_section_controls() { + $this->add_carousel_pagination_controls( [ + 'css_prefix' => '', + 'section_carousel_pagination' => [ + 'condition' => [ + 'template_id!' => '', + ], + ], + ] ); + } + + public function register_design_layout_controls() { + $this->start_controls_section( + 'section_layout_style', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'image_spacing_custom', + [ + 'label' => esc_html__( 'Gap between slides', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px' ], + 'range' => [ + 'px' => [ + 'max' => 400, + ], + ], + 'default' => [ + 'size' => 10, + ], + 'frontend_available' => true, + 'render_type' => 'none', + 'selectors' => [ + '{{WRAPPER}}' => '--swiper-slides-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function register_design_navigation_controls() { + $this->add_carousel_navigation_styling_controls( [ + 'css_prefix' => '', + 'navigation_styling_custom_settings' => [ + 'condition' => [ + 'arrows' => 'yes', + 'template_id!' => '', + ], + ], + ] ); + } + + public function register_design_pagination_controls() { + $this->add_carousel_pagination_style_controls( [ + 'css_prefix' => '', + ] ); + } + + public function render_loop_header() { + $has_autoplay_enabled = 'yes' === $this->get_settings_for_display( 'autoplay' ); + + $this->add_render_attribute( 'swiper-wrapper', [ + 'class' => 'swiper-wrapper', + 'aria-live' => $has_autoplay_enabled ? 'off' : 'polite', + ] ); + ?> +
    print_render_attribute_string( 'swiper-wrapper' ); ?>> + +
    +
    + get_settings_for_display(); + $this->render_carousel_footer( $settings ); + } + + public function add_swiper_slide_attributes_to_loop_item( $attributes, $document ) { + if ( LoopDocument::DOCUMENT_TYPE === $document::get_type() ) { + $attributes['class'] .= ' swiper-slide'; + $attributes['role'] = 'group'; + $attributes['aria-roledescription'] = 'slide'; + } + + return $attributes; + } + + public function add_loop_header_attributes( $render_attributes ) { + $settings = $this->get_settings_for_display(); + + if ( ! empty( $settings['direction'] ) ) { + $render_attributes['dir'] = $settings['direction']; + } + + return $render_attributes; + } + + + public function get_loop_header_widget_classes(): array { + $swiper_class = Plugin::elementor()->experiments->is_feature_active( 'e_swiper_latest' ) ? 'swiper' : 'swiper-container'; + return [ $swiper_class ]; + } + + public function before_skin_render() { + add_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_swiper_slide_attributes_to_loop_item' ], 10, 2 ); + add_filter( 'elementor/skin/loop_header_attributes', [ $this, 'add_loop_header_attributes' ], 10, 1 ); + } + + public function after_skin_render() { + remove_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_swiper_slide_attributes_to_loop_item' ] ); + remove_filter( 'elementor/skin/loop_header_attributes', [ $this, 'add_loop_header_attributes' ] ); + } + + protected function register_layout_section() { + parent::register_layout_section(); + + $this->start_injection( [ + 'of' => 'template_id', + ] ); + + $this->add_control( + 'posts_per_page', + [ + 'label' => esc_html__( 'Number of slides', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 6, + 'min' => 1, + 'condition' => [ + 'template_id!' => '', + ], + 'separator' => 'before', + ] + ); + + $this->add_carousel_layout_controls( [ + 'css_prefix' => '', + 'slides_to_show_custom_settings' => [ + 'default' => '3', + 'widescreen_default' => '3', + 'laptop_default' => '3', + 'tablet_extra_default' => '3', + 'tablet_default' => '2', + 'mobile_extra_default' => '2', + 'mobile_default' => '1', + 'condition' => [ + 'posts_per_page!' => 1, + 'template_id!' => '', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--swiper-slides-to-display: {{VALUE}}', + ], + ], + 'slides_to_scroll_custom_settings' => [ + 'default' => '1', + 'condition' => [ + 'posts_per_page!' => 1, + 'template_id!' => '', + ], + ], + 'equal_height_custom_settings' => [ + 'condition' => [ + 'posts_per_page!' => 1, + 'template_id!' => '', + ], + 'selectors' => [ + '{{WRAPPER}} .swiper-slide > .elementor-element' => 'height: 100%', + ], + ], + 'slides_on_display' => 8, + ] ); + + // Location for the Edit handle. + $this->add_control( + 'edit_handle_selector', + [ + 'label' => esc_html__( 'Edit Handle Selector', 'elementor-pro' ), + 'type' => Controls_Manager::HIDDEN, + 'default' => '.elementor-widget-container', + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->end_injection(); + } +} diff --git a/modules/loop-builder/widgets/loop-grid.php b/modules/loop-builder/widgets/loop-grid.php new file mode 100644 index 0000000..3eea608 --- /dev/null +++ b/modules/loop-builder/widgets/loop-grid.php @@ -0,0 +1,579 @@ +start_injection( [ + 'of' => 'template_id', + ] ); + + $this->add_responsive_control( + 'columns', + [ + 'label' => esc_html__( 'Columns', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => '3', + 'tablet_default' => '2', + 'mobile_default' => '1', + 'min' => 1, + 'max' => 12, + 'prefix_class' => 'elementor-grid%s-', + 'frontend_available' => true, + 'separator' => 'before', + 'condition' => [ + 'template_id!' => '', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--grid-columns: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'posts_per_page', + [ + 'label' => esc_html__( 'Items Per Page', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 6, + 'min' => 1, + 'condition' => [ + 'template_id!' => '', + ], + ] + ); + + $this->add_control( + 'masonry', + [ + 'label' => esc_html__( 'Masonry', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'condition' => [ + 'columns!' => 1, + 'template_id!' => '', + ], + 'render_type' => 'ui', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'equal_height', + [ + 'label' => esc_html__( 'Equal height', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'condition' => [ + 'columns!' => 1, + 'template_id!' => '', + 'masonry' => '', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-loop-container' => 'grid-auto-rows: 1fr', + // `.elementor-section-wrap` exists only when editing the loop template. + '{{WRAPPER}} .e-loop-item > .elementor-section, {{WRAPPER}} .e-loop-item > .elementor-section > .elementor-container, {{WRAPPER}} .e-loop-item > .e-con, {{WRAPPER}} .e-loop-item .elementor-section-wrap > .e-con' => 'height: 100%', + ], + ] + ); + + $this->add_control( + 'alternate_template', + [ + 'label' => esc_html__( 'Apply an alternate template', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'condition' => [ + 'posts_per_page!' => 1, + 'template_id!' => '', + '_skin!' => [ + LoopBuilderModule::LOOP_POST_TAXONOMY_SKIN_ID, + WoocommerceModule::LOOP_PRODUCT_TAXONOMY_SKIN_ID, + ], + ], + 'render_type' => 'template', + 'frontend_available' => true, + 'separator' => 'before', + ] + ); + + $repeater = new Repeater(); + + $repeater->add_control( + 'template_id', + [ + 'label' => esc_html__( 'Choose a template', 'elementor-pro' ), + 'type' => Template_Query::CONTROL_ID, + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_LIBRARY_TEMPLATE, + 'query' => [ + 'post_status' => Document::STATUS_PUBLISH, + 'meta_query' => [ + [ + 'key' => Document::TYPE_META_KEY, + 'value' => LoopDocument::get_type(), + 'compare' => 'IN', + ], + ], + ], + ], + 'actions' => [ + 'new' => [ + 'visible' => true, + 'document_config' => [ + 'type' => LoopDocument::get_type(), + ], + 'after_action' => 'redirect', + ], + 'edit' => [ + 'visible' => true, + 'after_action' => 'redirect', + ], + ], + 'frontend_available' => true, + ] + ); + + $repeater->add_control( + 'repeat_template', + [ + 'label' => esc_html__( 'Position in grid', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'condition' => [ + 'template_id!' => '', + ], + ] + ); + + $repeater->add_control( + 'repeat_template_note', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'Note: Repeat the alternate template once every chosen number of items.', 'elementor-pro' ), + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'template_id!' => '', + ], + ] + ); + + $repeater->add_control( + 'show_once', + [ + 'label' => esc_html__( 'Apply Once', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'condition' => [ + 'template_id!' => '', + ], + 'render_type' => 'template', + ] + ); + + $repeater->add_responsive_control( + 'column_span', + [ + 'label' => esc_html__( 'Column Span', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '1', + 'options' => [ + '1' => '1', + '2' => '2', + '3' => '3', + '4' => '4', + '5' => '5', + '6' => '6', + '7' => '7', + '8' => '8', + '9' => '9', + '10' => '10', + '11' => '11', + '12' => '12', + ], + 'condition' => [ + 'template_id!' => '', + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}}' => 'grid-column: span min( {{VALUE}}, var(--grid-columns) );', + ], + ] + ); + + $repeater->add_control( + 'column_span_note', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'Note: Item will span across a number of columns.', 'elementor-pro' ), + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'template_id!' => '', + ], + ] + ); + + $repeater->add_control( + 'column_span_masonry_note', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'warning', + 'content' => esc_html__( 'Note: The Masonry option combined with Column Span might cause unexpected results and break the layout.', 'elementor-pro' ), + 'condition' => [ + 'column_span!' => '1', + ], + ] + ); + + $repeater->add_control( + 'static_position', + [ + 'label' => esc_html__( 'Static item position', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'condition' => [ + 'template_id!' => '', + ], + 'render_type' => 'template', + ] + ); + + $repeater->add_control( + 'static_position_note', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'Note: Static Items remain in place when new items are added to grid. Other items appear according to query settings.', 'elementor-pro' ), + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'static_position!' => '', + 'template_id!' => '', + ], + ] + ); + + $this->add_control( + 'alternate_templates', + [ + 'label' => esc_html__( 'Templates', 'elementor-pro' ), + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'title_field' => 'Alternate Template', + 'condition' => [ + 'alternate_template' => 'yes', + 'posts_per_page!' => 1, + 'template_id!' => '', + ], + 'default' => [ + [ + 'template_id' => null, + ], + ], + ] + ); + + // Location for the Edit handle. + $this->add_control( + 'edit_handle_selector', + [ + 'label' => esc_html__( 'Edit Handle Selector', 'elementor-pro' ), + 'type' => Controls_Manager::HIDDEN, + 'default' => '[data-elementor-type="loop-item"]', + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->end_injection(); + } + + protected function register_additional_options_section_controls() { + $this->start_controls_section( + 'section_additional_options', + [ + 'label' => esc_html__( 'Additional Options', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + 'condition' => [ + 'template_id!' => '', + ], + ] + ); + + $this->add_control( + 'enable_nothing_found_message', + [ + 'label' => esc_html__( 'Nothing Found Message', 'elementor-pro' ), + 'description' => esc_html__( 'Note: This message will appear when no content is loaded in the grid.', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'nothing_found_message_text', + [ + 'type' => Controls_Manager::TEXTAREA, + 'ai' => [ + 'type' => 'text', + ], + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'enable_nothing_found_message' => 'yes', + ], + 'default' => esc_html__( 'It seems we can’t find what you’re looking for.', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'nothing_found_message_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-loop-nothing-found-message-align: {{VALUE}};', + ], + 'condition' => [ + 'enable_nothing_found_message' => 'yes', + ], + 'render_type' => 'ui', + ] + ); + + $this->add_control( + 'nothing_found_message_html_tag', + [ + 'label' => esc_html__( 'HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + ], + 'default' => 'div', + 'condition' => [ + 'enable_nothing_found_message' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function register_design_layout_controls() { + $this->start_controls_section( + 'section_design_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'column_gap', + [ + 'label' => esc_html__( 'Gap between columns', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--grid-column-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'row_gap', + [ + 'label' => esc_html__( 'Gap between rows', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'frontend_available' => true, + 'selectors' => [ + '{{WRAPPER}}' => '--grid-row-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function register_design_nothing_found_message_controls() { + $this->start_controls_section( + 'section_nothing_found_message_design', + [ + 'label' => esc_html__( 'Nothing Found Message', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'template_id!' => '', + 'enable_nothing_found_message' => 'yes', + ], + ] + ); + + $this->add_responsive_control( + 'nothing_found_message_space_from_top', + [ + 'label' => esc_html__( 'Space from top', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-loop-nothing-found-message-space-from-top: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'nothing_found_message_space_from_bottom', + [ + 'label' => esc_html__( 'Space from bottom', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-loop-nothing-found-message-space-from-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'nothing_found_message_typography', + 'selector' => '{{WRAPPER}} .e-loop-nothing-found-message__text', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'nothing_found_message_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--e-loop-nothing-found-message-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'nothing_found_message_text_shadow', + 'selector' => '{{WRAPPER}} .e-loop-nothing-found-message__text', + ] + ); + + $this->add_group_control( + Group_Control_Text_Stroke::get_type(), + [ + 'name' => 'nothing_found_message_text_stroke', + 'selector' => '{{WRAPPER}} .e-loop-nothing-found-message__text', + ] + ); + + $this->end_controls_section(); + } +} diff --git a/modules/loop-filter/data/controller.php b/modules/loop-filter/data/controller.php new file mode 100644 index 0000000..940023c --- /dev/null +++ b/modules/loop-filter/data/controller.php @@ -0,0 +1,38 @@ +register_endpoint( Endpoints\Get_Post_Type_Taxonomies::class ); + $this->register_endpoint( Endpoints\Refresh_Loop::class ); + } + + private function register_endpoint( $endpoint ) { + $endpoint_instance = new $endpoint( $this ); + + $this->endpoints[ $endpoint_instance->get_name() ] = $endpoint_instance; + } + + public function __construct() { + add_action( 'rest_api_init', function () { + $this->register_endpoints(); // Because 'register_endpoints' is protected. + } ); + } +} diff --git a/modules/loop-filter/data/endpoints/base.php b/modules/loop-filter/data/endpoints/base.php new file mode 100644 index 0000000..2c085d4 --- /dev/null +++ b/modules/loop-filter/data/endpoints/base.php @@ -0,0 +1,28 @@ +>register()`. + * + * @param Controller $controller + */ + public function __construct( Controller $controller ) { + $this->controller = $controller; + + $this->register(); + } +} diff --git a/modules/loop-filter/data/endpoints/get-post-type-taxonomies.php b/modules/loop-filter/data/endpoints/get-post-type-taxonomies.php new file mode 100644 index 0000000..28e1b58 --- /dev/null +++ b/modules/loop-filter/data/endpoints/get-post-type-taxonomies.php @@ -0,0 +1,58 @@ +get_params(); + + return $this->get_taxonomy_options( [ $data['post_type'] ] ); + } + + protected function register() { + register_rest_route( $this->controller->get_namespace(), $this->get_route(), [ + [ + 'args' => [ + 'post_type' => [ + 'description' => 'The post type for which to fetch the list of taxonomies.', + 'type' => 'string', + 'required' => true, + 'validate_callback' => function ( $param ) { + return ! empty( $param ) && is_string( $param ); + }, + ], + ], + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'get_items' ], + 'permission_callback' => function ( $request ) { + return $this->permission_callback( $request ); + }, + ], + ] ); + } +} diff --git a/modules/loop-filter/data/endpoints/refresh-loop.php b/modules/loop-filter/data/endpoints/refresh-loop.php new file mode 100644 index 0000000..8f3da8a --- /dev/null +++ b/modules/loop-filter/data/endpoints/refresh-loop.php @@ -0,0 +1,194 @@ +get_params(); + + if ( $this->is_edit_mode( $data['post_id'] ) ) { + return true; + } + + $post = get_post( $data['post_id'] ); + + if ( ! $post || 'publish' !== $post->post_status ) { + return false; + } + + $document = Plugin::elementor()->documents->get( $data['post_id'] ); + + if ( ! $document ) { + return false; + } + + $element_data = $document->get_elements_data(); + $loop_widget = Utils::find_element_recursive( $element_data, $data['widget_id'] ); + + if ( empty( $loop_widget ) ) { + return false; + } + + if ( 'widget' !== $loop_widget['elType'] || 'loop-grid' !== $loop_widget['widgetType'] ) { + return false; + } + + return true; + } + + private function create_widget_instance_from_db( $post_id, $widget_id ) { + $document = Plugin::elementor()->documents->get( $post_id ); + + $widget_data = Utils::find_element_recursive( $document->get_elements_data(), $widget_id ); + + return Plugin::elementor()->elements_manager->create_element_instance( $widget_data ); + } + + public function get_updated_loop_widget_markup( \WP_REST_Request $request ): array { + $data = $request->get_params(); + + /** @var Module $loop_filter */ + $loop_filter = Plugin::instance()->modules_manager->get_modules( 'loop-filter' ); + + $loop_filter->register_widget_filter( $data['widget_id'], $data['widget_filters'] ); + + if ( $this->is_edit_mode( $data['post_id'] ) ) { + $widget_instance = Plugin::elementor()->elements_manager->create_element_instance( $data['widget_model'] ); + } else { + $widget_instance = $this->create_widget_instance_from_db( $data['post_id'], $data['widget_id'] ); + } + + set_query_var( 'pagination_base_url', $data['pagination_base_url'] ); + + ob_start(); + + /** @var Module $loop_filter */ + $loop_filter = Plugin::instance()->modules_manager->get_modules( 'loop-filter' ); + + add_filter( 'elementor/query/query_args', [ $loop_filter, 'filter_loop_query' ], 10, 2 ); + + /** @var Widget_Base $widget_instance */ + $widget_instance->render_content(); + + remove_filter( 'elementor/query/query_args', [ $loop_filter, 'filter_loop_query' ] ); + + $markup = ob_get_clean(); + + set_query_var( 'pagination_base_url', null ); + + return [ + 'data' => $markup, + ]; + } + + private function is_edit_mode( $post_id ) { + if ( isset( $this->is_edit_mode ) ) { + return $this->is_edit_mode; + } + + $document = Plugin::elementor()->documents->get( $post_id ); + + $this->is_edit_mode = ! empty( $document ) && $document->is_editable_by_current_user(); + + return $this->is_edit_mode; + } + + protected function register() { + register_rest_route( $this->controller->get_namespace(), $this->get_route(), [ + [ + 'args' => [ + 'post_id' => [ + 'description' => 'The post ID of the page containing the loop.', + 'type' => 'integer', + 'required' => true, + 'validate_callback' => function ( $param ) { + return ! empty( $param ) && is_numeric( $param ); + }, + ], + 'widget_id' => [ + 'description' => 'The ID of the loop widget.', + 'type' => 'string', + 'required' => true, + 'validate_callback' => function ( $param ) { + return $this->is_widget_id_valid( $param ); + }, + ], + 'widget_filters' => [ + 'description' => 'The filters for the loop widget.', + 'type' => 'object', + 'required' => true, + 'validate_callback' => function( $param ) { + // TODO: Build a validator for this that iterates over all passed filters and validates them. + return is_array( $param ); + }, + ], + 'widget_model' => [ + 'description' => 'The model of the loop widget. In Editor mode only.', + 'type' => 'object', + 'required' => false, + 'validate_callback' => function ( $param, $request ) { + $params = $request->get_params(); + + if ( ! $this->is_edit_mode( $params['post_id'] ) ) { + return false; + } + + return $this->is_widget_model_valid( $param ); + }, + ], + 'pagination_base_url' => [ + 'required' => false, + 'validate_callback' => function ( $param ) { + return filter_var( $param, FILTER_VALIDATE_URL ); + }, + 'sanitize_callback' => 'esc_url_raw', + ], + ], + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'get_updated_loop_widget_markup' ], + 'permission_callback' => function ( $request ) { + return $this->permission_callback( $request ); + }, + ], + ] ); + } +} diff --git a/modules/loop-filter/data/interfaces/endpoint.php b/modules/loop-filter/data/interfaces/endpoint.php new file mode 100644 index 0000000..23ea232 --- /dev/null +++ b/modules/loop-filter/data/interfaces/endpoint.php @@ -0,0 +1,18 @@ +experiments->is_feature_active( static::EXPERIMENT_NAME ); + } + + /** + * Add to the experiments + * + * @return array + */ + public static function get_experimental_data() { + $experiment_data = [ + 'name' => static::EXPERIMENT_NAME, + 'title' => esc_html__( 'Taxonomy Filter', 'elementor-pro' ), + 'description' => sprintf( + esc_html__( 'Taxonomy Filter is a powerful tool that enables users to easily filter and sort their posts and product categories. %1$sLearn More%2$s', 'elementor-pro' ), + '', + '' + ), + 'release_status' => Manager::RELEASE_STATUS_BETA, + 'default' => Manager::STATE_INACTIVE, + 'new_site' => [ + 'default_active' => true, + 'minimum_installation_version' => '3.20', + ], + ]; + + return $experiment_data; + } + + public function get_post_type_taxonomies( $data ) { + if ( ! current_user_can( 'edit_posts' ) || empty( $data['post_type'] ) ) { + return []; + } + + $post_type_taxonomies = get_object_taxonomies( $data['post_type'], 'objects' ); + + $control_options = []; + + foreach ( $post_type_taxonomies as $taxonomy ) { + $control_options[ $taxonomy->name ] = $taxonomy->label; + } + + return $control_options; + } + + public function register_widget_filter( $widget_id, $filter_data ) { + if ( empty( $this->filters[ $widget_id ] ) ) { + $this->filters[ $widget_id ] = $filter_data; + return; + } + + foreach ( $filter_data as $filter_type => $filter ) { + if ( empty( $this->filters[ $widget_id ][ $filter_type ] ) ) { + $this->filters[ $widget_id ][ $filter_type ] = $filter; + continue; + } + + $this->filters[ $widget_id ][ $filter_type ] = array_merge( $this->filters[ $widget_id ][ $filter_type ], $filter ); + } + } + + public function filter_loop_query( $query_args, $widget ) { + $widget_id = $widget->get_id(); + + if ( empty( $this->filters[ $widget_id ] ) ) { + return $query_args; + } + + /** @var array $filter_types An array containing all of a widget's different filters - e.g. taxonomy, price, rating... */ + $filter_types = $this->filters[ $widget_id ]; + + // TODO: This part is non-generic and should be refactored to allow for multiple types of filters. + $query_args['tax_query']['relation'] = $this->query['AND']['relation']; + + foreach ( $filter_types as $filters ) { + // The $filters array contains all filters of a specific type. For example, for the taxonomy filter type, + // it would contain all taxonomies to be filtered - e.g. 'category', 'tag', 'product-cat', etc. + $tax_query = []; + + foreach ( $filters as $filter_taxonomy => $filter ) { + if ( 'logicalJoin' === $filter_taxonomy ) { + continue; + } + + if ( $this->is_filter_empty( $filter ) ) { + continue; + } + + // Sanitize request data. + $taxonomy = sanitize_key( $filter_taxonomy ); + ( new Taxonomy_Query_Builder() )->get_merged_queries( $tax_query, $taxonomy, $filter ); + } + } + + $query_args['tax_query'] = array_merge( $query_args['tax_query'], $tax_query ?? [] ); + + return $query_args; + } + + /** + * @description Check if the filter is empty. + * Taxonomy Filter URL parameter is empty but not removed i.e. `&e-filter-389c132-product_cat=`. + * This edge case happens if a user clears terms and not the Taxonomy filter parameter + * @param $filter + * @return bool + */ + public function is_filter_empty( $filter ) { + if ( '' === $filter['terms'][0] ) { + return true; + } + return false; + } + + public function add_localize_data( $config ) { + $current_query_vars = $this->get_current_query_vars(); + + $config['loopFilter'] = [ + 'mainQueryPostType' => $current_query_vars['post_type'], + 'nonce' => wp_create_nonce( 'wp_rest' ), + ]; + + return $config; + } + + private function get_current_query_vars() { + $current_query_vars = $GLOBALS['wp_query']->query_vars; + + /** + * Current query variables. + * + * Filters the query variables for the current query. This hook allows + * developers to alter those variables. + * + * @param array $current_query_vars Current query variables. + */ + return apply_filters( 'elementor/query/get_query_args/current_query', $current_query_vars ); + } + + private function parse_query_string( $param_key ) { + // Check if the query param is a filter. match a regex for `e-filter-14f9e1d-post_tag` where `14f9e1d` is the widget ID and must be 7 characters long and have only letters and numbers, then followed by a string that can only have letters, numbers, dashes and underscores. + if ( ! preg_match( '/^e-filter-[a-z0-9]{7}-[a-z0-9_\-]+$/', $param_key ) ) { + return []; + } + + // Remove the 'filter_' prefix from the query param + $filter = str_replace( 'e-filter-', '', $param_key ); + + // Split the filter into an array of widget ID and filter type + $filter = explode( '-', $filter ); + + if ( count( $filter ) !== 2 ) { + return []; + } + + // Get the widget ID + $widget_id = $filter[0]; + + // Get the taxonomy + $taxonomy = $filter[1]; + + return [ + 'taxonomy' => $taxonomy, + 'widget_id' => $widget_id, + ]; + } + + private function maybe_populate_filters_from_query_string() { + if ( ! isset( $_SERVER['QUERY_STRING'] ) ) { + return; + } + + $query_params = []; + wp_parse_str( htmlspecialchars_decode( Utils::_unstable_get_super_global_value( $_SERVER, 'QUERY_STRING' ) ), $query_params ); + + foreach ( $query_params as $param_key => $param_value ) { + // TODO: This part is not generic - it only supports taxonomy filters. It should be refactored to allow for multiple types of filters. + $parsed_query_string = $this->parse_query_string( $param_key ); + + if ( empty( $parsed_query_string ) || empty( $parsed_query_string['taxonomy'] ) || empty( $parsed_query_string['widget_id'] ) ) { + continue; + } + + $terms = $this->get_terms_array_from_params( $param_value ); + $logical_join = $this->get_logical_join_from_params( $param_value ); + + if ( empty( $terms ) ) { + continue; + } + + $filter_data = [ + 'taxonomy' => [ + $parsed_query_string['taxonomy'] => [ + 'terms' => $terms, + 'logicalJoin' => $logical_join, + ], + ], + ]; + + $this->register_widget_filter( $parsed_query_string['widget_id'], $filter_data ); + } + } + + private function get_seperator_from_params( $param_value ) { + $separator = $this->query['AND']['separator']['from-browser']; // The web browser automatically replaces the plus sign with a space character before sending the data to the server. + + if ( strstr( $param_value, $this->query['OR']['separator']['from-browser'] ) ) { + $separator = $this->query['OR']['separator']['from-browser']; + $this->operator = $this->query['OR']['operator']; + } + return $separator; + } + + private function get_terms_array_from_params( $param_value ) { + $separator = $this->get_seperator_from_params( $param_value ); + return explode( $separator, $param_value ); + } + + private function get_logical_join_from_params( $param_value ) { + $separator = $this->get_seperator_from_params( $param_value ); + + foreach ( $this->query as $index => $data ) { + if ( $data['separator']['decoded'] === $separator ) { + return $index; // Return the index when the decoded separator is found + } + } + + return 'AND'; // Default logical join + } + + /** + * @return array + */ + public function get_query_string_filters() { + return $this->filters; + } + + public function remove_rest_route_parameter( $link ) { + return remove_query_arg( 'rest_route', $link ); + } + + /** + * @return boolean + */ + public function is_term_not_selected_for_inclusion( $loop_widget_settings, $term, $skin ) { + return ! empty( $loop_widget_settings[ $skin . '_query_include' ] ) && + in_array( 'terms', $loop_widget_settings[ $skin . '_query_include' ] ) && + isset( $loop_widget_settings[ $skin . '_query_include_term_ids' ] ) && + ! in_array( $term->term_id, $loop_widget_settings[ $skin . '_query_include_term_ids' ] ); + } + + /** + * @return boolean + */ + public function is_term_selected_for_exclusion( $loop_widget_settings, $term, $skin ) { + return ! empty( $loop_widget_settings[ $skin . '_query_exclude' ] ) && + in_array( 'terms', $loop_widget_settings[ $skin . '_query_exclude' ] ) && + isset( $loop_widget_settings[ $skin . '_query_exclude_term_ids' ] ) && + in_array( $term->term_id, $loop_widget_settings[ $skin . '_query_exclude_term_ids' ] ); + } + + /** + * @return boolean + */ + public function should_exclude_term_by_manual_selection( $loop_widget_settings, $term, $user_selected_taxonomy, $skin ) { + if ( ! $this->loop_widget_has_manual_selection( $loop_widget_settings, $skin ) ) { + return false; + } + + $terms_to_exclude_by_manual_selection = $this->get_terms_to_exclude_by_manual_selection( $loop_widget_settings[ $skin . '_query_exclude_ids' ] ?? [], $user_selected_taxonomy ); + + if ( in_array( $term->term_id, $terms_to_exclude_by_manual_selection ) ) { + return true; + } + + return false; + } + + /** + * @return boolean + */ + private function loop_widget_has_manual_selection( $loop_widget_settings, $skin ) { + return ! empty( $loop_widget_settings[ $skin . '_query_exclude' ] ) && + in_array( 'manual_selection', $loop_widget_settings[ $skin . '_query_exclude' ] ) && + ! empty( $loop_widget_settings[ $skin . '_query_exclude_ids' ] ); + } + + /** + * @return array + */ + private function get_terms_to_exclude_by_manual_selection( $selected_posts, $user_selected_taxonomy ) { + $terms_to_exclude = []; + $term_exclude_counts = []; + $term_actual_counts = []; + + foreach ( $selected_posts as $post_id ) { + $this->calculate_post_terms_counts( $post_id, $user_selected_taxonomy, $term_exclude_counts, $term_actual_counts ); + } + + foreach ( $term_exclude_counts as $term_id => $selected_count ) { + $this->maybe_add_term_to_exclusion( $term_id, $selected_count, $term_actual_counts, $terms_to_exclude ); + } + + return $terms_to_exclude; + } + + /** + * @return void + */ + private function calculate_post_terms_counts( $post_id, $user_selected_taxonomy, &$term_exclude_counts, &$term_actual_counts ) { + $post_terms = wp_get_post_terms( $post_id, $user_selected_taxonomy ); + + foreach ( $post_terms as $term ) { + $this->calculate_term_counts( $term, $term_exclude_counts, $term_actual_counts ); + } + } + + /** + * @return void + */ + private function calculate_term_counts( $term, &$term_exclude_counts, &$term_actual_counts ) { + if ( empty( $term_exclude_counts[ $term->term_id ] ) ) { + $term_exclude_counts[ $term->term_id ] = 0; + } + + $term_exclude_counts[ $term->term_id ] = (int) $term_exclude_counts[ $term->term_id ] + 1; + $term_actual_counts[ $term->term_id ] = (int) $term->count; + } + + /** + * @return void + */ + private function maybe_add_term_to_exclusion( $term_id, $selected_count, $term_actual_counts, &$terms_to_exclude ) { + $user_selected_all_the_posts_for_this_term = $selected_count >= $term_actual_counts[ $term_id ]; + + if ( $user_selected_all_the_posts_for_this_term ) { + $terms_to_exclude[] = $term_id; + } + } + + public function __construct() { + parent::__construct(); + + $this->query = Query_Constants::DATA; + + if ( ! empty( $_SERVER['QUERY_STRING'] ) ) { + $this->maybe_populate_filters_from_query_string(); + } + + // Register the controller. + new Controller(); + + add_filter( 'elementor/query/query_args', [ $this, 'filter_loop_query' ], 10, 2 ); + + add_filter( 'elementor_pro/editor/localize_settings', [ $this, 'add_localize_data' ] ); + + add_filter( 'paginate_links', [ $this, 'remove_rest_route_parameter' ] ); + } +} diff --git a/modules/loop-filter/query/data/query-constants.php b/modules/loop-filter/query/data/query-constants.php new file mode 100644 index 0000000..652e68b --- /dev/null +++ b/modules/loop-filter/query/data/query-constants.php @@ -0,0 +1,44 @@ + [ + 'separator' => [ + 'decoded' => '+', + 'from-browser' => ' ', + 'encoded' => '%2B', + ], + 'operator' => 'AND', + 'relation' => 'AND', + ], + 'OR' => [ + 'separator' => [ + 'decoded' => '~', + 'from-browser' => '~', + 'encoded' => '%7C', + ], + 'operator' => 'IN', + 'relation' => 'OR', + ], + 'NOT' => [ + 'separator' => [ + 'decoded' => '!', + 'from-browser' => '!', + 'encoded' => '%21', + ], + 'operator' => 'NOT IN', + 'relation' => 'AND', + ], + 'DISABLED' => [ + 'separator' => [ + 'decoded' => '', + 'from-browser' => '', + 'encoded' => '', + ], + 'operator' => 'AND', + 'relation' => 'AND', + ], + ]; +} diff --git a/modules/loop-filter/query/interfaces/query-interface.php b/modules/loop-filter/query/interfaces/query-interface.php new file mode 100644 index 0000000..a2a7cd6 --- /dev/null +++ b/modules/loop-filter/query/interfaces/query-interface.php @@ -0,0 +1,10 @@ +query = Query_Constants::DATA; + $this->taxonomy_manager = $taxonomy_manager; + $this->terms = $filter_terms['hierarchical-terms'] ?? []; + $this->taxonomy = $filter_terms['taxonomy']; + $this->logical_join = $filter_terms['logicalJoin']; + } + + /** + * @return array + */ + public function get_query() { + $query = []; + + if ( empty( $this->terms ) ) { + return $query; + } + + foreach ( $this->terms as $parent_term => $terms ) { + $filtered_terms = $this->filter_query_terms( $parent_term, $terms ); + $query[] = $this->get_hierarchy_query( $filtered_terms ); + } + + return $query; + } + + /** + * @param $parent_term + * @param $terms + * @return array + */ + private function filter_query_terms( $parent_term, $terms ) { + $query_terms = []; + $filters_on_parent_term = in_array( $parent_term, $terms ); + $filters_on_parent_and_child_terms = count( $terms ) > 1 && $filters_on_parent_term; + $is_parent_term_only = $filters_on_parent_term && ! $filters_on_parent_and_child_terms; + $has_child_terms_only = ! $filters_on_parent_term; + + // For an AND Queries exclude parent term if there are child and parent terms. + if ( $filters_on_parent_and_child_terms ) { + $query_terms = array_diff( $terms, [ $parent_term ] ); //drop parent term + } + + if ( $is_parent_term_only || $has_child_terms_only ) { + $query_terms = $terms; + } + + return $query_terms; + } + + + /** + * Create the Inner query for AND OR queries with one or more filter terms targeted at the same Widget. + * @param array $terms + * @return array + */ + private function get_hierarchy_query( $terms ) { + $inner_query = [ + 'taxonomy' => $this->taxonomy, + 'field' => 'slug', + 'terms' => [], + 'operator' => $this->query['OR']['operator'], + ]; + + foreach ( $terms as $term ) { + if ( 1 < count( $terms ) && $this->taxonomy_manager->is_parent_term_with_children( $term, $this->taxonomy ) ) { + $parent_query = $this->get_hierarchy_query( [ $term ] ); + continue; + } + + $inner_query['terms'] = [ urldecode( sanitize_title( $term ?? '' ) ) ]; //decode non-latin strings + $child_queries[] = $inner_query; + } + + $hierarchy_query = array_merge( $parent_query ?? [], $child_queries ?? [] ); + $hierarchy_query['relation'] = $this->query['OR']['relation']; //broaden search results when two child terms are selected. And relation between unrelated terms. + return $hierarchy_query; + } +} diff --git a/modules/loop-filter/query/query-types/hierarchy-or-query.php b/modules/loop-filter/query/query-types/hierarchy-or-query.php new file mode 100644 index 0000000..5c1285d --- /dev/null +++ b/modules/loop-filter/query/query-types/hierarchy-or-query.php @@ -0,0 +1,90 @@ +query = Query_Constants::DATA; + $this->terms = $filter_terms['hierarchical-terms'] ?? []; + $this->taxonomy = $filter_terms['taxonomy']; + $this->logical_join = $filter_terms['logicalJoin']; + } + + /** + * @return array + */ + public function get_query() { + $query = []; + + if ( empty( $this->terms ) ) { + return $query; + } + + foreach ( $this->terms as $parent_term => $terms ) { + $filtered_terms = $this->filter_query_terms( $parent_term, $terms ); + $query[] = $this->get_hierarchy_query( $filtered_terms ); + } + + return $query; + } + + /** + * @description + * @param $parent_term + * @param $terms + * @return array + */ + private function filter_query_terms( $parent_term, $terms ) { + $query_terms = []; + $filters_on_parent_term = in_array( $parent_term, $terms ); + $filters_on_parent_and_child_terms = count( $terms ) > 1 && $filters_on_parent_term; + $is_parent_term_only = $filters_on_parent_term && ! $filters_on_parent_and_child_terms; + $has_child_terms_only = ! $filters_on_parent_term; + + // For an OR Queries exclude child terms if there is a parent term in the terms. + if ( $filters_on_parent_and_child_terms ) { + $query_terms = [ $parent_term ]; // drop child terms + } + + if ( $is_parent_term_only || $has_child_terms_only ) { + $query_terms = $terms; + } + + return $query_terms; + } + + /** + * Create the Inner query for AND OR queries with one or more filter terms targeted at the same Widget. + * @param array $terms + * @return array + */ + private function get_hierarchy_query( $terms ) { + $inner_query = [ + 'taxonomy' => $this->taxonomy, + 'field' => 'slug', + 'terms' => [], + 'operator' => $this->query['OR']['operator'], + ]; + + foreach ( $terms as $term ) { + if ( 1 < count( $terms ) && $this->taxonomy_manager->is_parent_term_with_children( $term, $this->taxonomy ) ) { + $parent_query = $this->get_hierarchy_query( [ $term ] ); + continue; + } + + $inner_query['terms'] = [ urldecode( sanitize_title( $term ?? '' ) ) ]; //decode non-latin strings + $child_queries[] = $inner_query; + } + + $hierarchy_query = array_merge( $parent_query ?? [], $child_queries ?? [] ); + $hierarchy_query['relation'] = $this->query['OR']['relation']; //broaden search results when two child terms are selected. And relation between unrelated terms. + return $hierarchy_query; + } +} diff --git a/modules/loop-filter/query/query-types/single-terms-query.php b/modules/loop-filter/query/query-types/single-terms-query.php new file mode 100644 index 0000000..d4b36fc --- /dev/null +++ b/modules/loop-filter/query/query-types/single-terms-query.php @@ -0,0 +1,88 @@ +query = Query_Constants::DATA; + $this->taxonomy_manager = $taxonomy_manager; + $this->set_single_or_multiple_selection_terms( $filter_terms ); + $this->taxonomy = $filter_terms['taxonomy'] ?? []; + $this->logical_join = $filter_terms['logicalJoin']; + } + + /** + * Create the Inner query for AND OR queries with one or more filter terms targeted at the same Widget using terms with no parent and no children + * @return array + */ + public function get_query() { + if ( empty( $this->terms ) ) { + return []; + } + + $query = [ + [ + 'taxonomy' => $this->taxonomy, + 'field' => 'slug', + 'terms' => [], + 'operator' => $this->get_inner_query_operator(), + ], + ]; + + foreach ( $this->terms as $term ) { + $query[0]['terms'][] = urldecode( sanitize_title( $term ?? '' ) ); //decode non-latin strings + } + + return $query; + } + + /** + * @param $filter_terms + * @return void + */ + private function set_single_or_multiple_selection_terms( $filter_terms ) { + // Single Selection + if ( ! empty( $filter_terms['single-term'][0] ) ) { + $this->terms = $filter_terms['single-term']; + return; + } + + $this->terms = $filter_terms['parent-terms-without-children']; + + } + + /** + * @return string 'IN' / 'AND' (default) + */ + private function get_inner_query_operator() { + if ( $this->is_single_parent_term() ) { + return $this->query['OR']['operator']; // Allow showing posts from parent category when it's selected. + } + + if ( 'DISABLED' !== $this->logical_join ) { + return $this->query[ $this->logical_join ?? 'DISABLED' ]['operator']; + } + + return $this->query['AND']['operator']; + } + + /** + * @return boolean + */ + private function is_single_parent_term() { + if ( empty( $this->terms ?? [] ) ) { + return false; + } + + $is_parent_term = $this->taxonomy_manager->is_parent_term_with_children( $this->terms[0], $this->taxonomy ); + return 1 === count( $this->terms ?? [] ) && $is_parent_term; + } +} diff --git a/modules/loop-filter/query/taxonomy-manager.php b/modules/loop-filter/query/taxonomy-manager.php new file mode 100644 index 0000000..7aa120d --- /dev/null +++ b/modules/loop-filter/query/taxonomy-manager.php @@ -0,0 +1,260 @@ + $taxonomy, + 'hide_empty' => true, + ]; + + $terms = get_terms( $args ); + + if ( is_wp_error( $terms ) ) { + return; + } + + $this->get_terms_by_slug_and_id( $terms ); + $this->terms_hierarchy = $this->filter_child_terms_by_depth( $terms, 100 ); + } + + private function get_term_id( $slug, $taxonomy ) { + if ( ! isset( $this->terms_by_slug[ $taxonomy ][ $slug ] ) ) { + return -1; + } + return $this->terms_by_slug[ $taxonomy ][ $slug ]['term_id']; + } + + /** + * Check if a term is a parent term. + * @param string $slug + * @param string $taxonomy + * @return bool; + */ + public function is_parent_term_without_children( $slug, $taxonomy ) { + // Empty terms do not exist in $terms_by_slug + $has_children = $this->has_children( $slug, $taxonomy ); + $is_top_level_parent_term = $this->is_top_level_parent_term( $slug, $taxonomy ); + return ( isset( $this->terms_by_slug[ $taxonomy ][ $slug ] ) && $is_top_level_parent_term && ! $has_children ); + } + + public function is_parent_term_with_children( $slug, $taxonomy ) { + // Empty terms do not exist in $terms_by_slug + return ( isset( $this->terms_by_slug[ $taxonomy ][ $slug ] ) && $this->has_children( $slug, $taxonomy ) ); + } + + private function has_children( $slug, $taxonomy ) { + $term_id = $this->get_term_id( $slug, $taxonomy ); + return isset( $this->terms_hierarchy[ $term_id ] ) && count( $this->terms_hierarchy[ $term_id ] ) > 0; + } + + private function get_children( $slug, $taxonomy ) { + $term_id = $this->get_term_id( $slug, $taxonomy ); + return $this->terms_hierarchy[ $term_id ]; + } + + private function is_child_term( $slug, $taxonomy ) { + return ( isset( $this->terms_by_slug[ $taxonomy ][ $slug ] ) && 0 !== $this->terms_by_slug[ $taxonomy ][ $slug ]['parent'] ); + } + + public function is_top_level_parent_term( $slug, $taxonomy ) { + return ( isset( $this->terms_by_slug[ $taxonomy ][ $slug ] ) && 0 === $this->terms_by_slug[ $taxonomy ][ $slug ]['parent'] ); + } + + private function get_parent( $slug, $taxonomy ) { + return $this->terms_by_slug[ $taxonomy ][ $slug ]['parent']; + } + + private function get_parent_id( $child_slug, $taxonomy ) { + if ( ! isset( $this->terms_by_slug[ $taxonomy ][ $child_slug ]['parent'] ) ) { + return -1; + } + + return $this->terms_by_slug[ $taxonomy ][ $child_slug ]['parent']; + } + + private function get_parent_slug( $child_slug, $taxonomy ) { + $parent_id = $this->get_parent_id( $child_slug, $taxonomy ); + + if ( -1 === $parent_id ) { + return 'UNDEFINED'; + } + + return $this->terms_by_id[ $taxonomy ][ $parent_id ]['slug']; + } + + /** + * @param array $filter_terms + * @param string $taxonomy + * @return array + */ + public function get_hierarchy_of_selected_terms( $filter_terms, $taxonomy ) { + // Taxonomy Filter parameter is empty i.e. `e-filter-389c132-product_cat=`. + if ( ! empty( $filter_terms['terms'] ) && '' === $filter_terms['terms'][0] ) { + return [ + 'single-term' => [], + 'parent-terms-without-children' => [], + 'hierarchical-terms' => [], + 'logicalJoin' => $filter_terms['logicalJoin'], + 'taxonomy' => $taxonomy, + ]; + } + + $parents_without_children = []; + $parents_with_children_by_parent = []; + $single_selection_term = []; + + if ( 1 === count( $filter_terms['terms'] ) ) { + $single_selection_term = $filter_terms['terms']; + } + + foreach ( $filter_terms['terms'] as $term ) { + + if ( ! empty( $single_selection_term ) ) { + break; + } + + $term = urldecode( sanitize_title( $term ) ); // decode non-latin slugs. + $is_parent_term_without_children = $this->is_parent_term_without_children( $term, $taxonomy ); + $is_parent_term_with_children = $this->is_parent_term_with_children( $term, $taxonomy ); + + if ( $is_parent_term_without_children ) { + $parents_without_children[] = $term; + continue; + } + + if ( $is_parent_term_with_children ) { + $parents_with_children_by_parent[ $term ][] = $term; + continue; + } + + $parent_slug = $this->get_parent_slug( $term, $taxonomy ); + if ( 'UNDEFINED' === $parent_slug ) { + continue; + } + $parents_with_children_by_parent[ $parent_slug ][] = $term; + } + + return [ + 'single-term' => $single_selection_term, + 'parent-terms-without-children' => $parents_without_children, + 'hierarchical-terms' => $parents_with_children_by_parent, + 'logicalJoin' => $filter_terms['logicalJoin'], + 'taxonomy' => $taxonomy, + ]; + } + + /** + * @param array $terms + * @return void + */ + private function get_terms_by_slug_and_id( $terms = [] ) { + $this->terms_by_slug = []; + foreach ( $terms as $term ) { + $slug = urldecode( $term->slug ); + + $this->try_set_terms_by_slug( $slug, $term ); + $this->try_set_terms_by_id( $slug, $term ); + } + } + + /** + * @param string $slug + * @param string $term + * @return void + */ + public function try_set_terms_by_slug( $slug, $term ) { + $term_id = $term->term_id; + $taxonomy = $term->taxonomy; + + if ( ! isset( $this->terms_by_slug[ $taxonomy ][ $slug ] ) ) { + $this->terms_by_slug[ $taxonomy ][ $slug ] = [ + 'term_id' => $term_id, + 'parent' => $term->parent, + 'count' => $term->count, + ]; + } + } + + /** + * @param string $slug + * @param string $term + * @return void + */ + public function try_set_terms_by_id( $slug, $term ) { + $term_id = $term->term_id; + $taxonomy = $term->taxonomy; + + if ( ! isset( $this->terms_by_id[ $taxonomy ][ $slug ] ) ) { + $this->terms_by_id[ $taxonomy ][ $term_id ] = [ + 'slug' => $slug, + 'parent' => $term->parent, + 'count' => $term->count, + ]; + } + } + + /** + * @param \WP_Term[] $terms + * @param int $target_depth + * @return \WP_Term[] + */ + private function filter_child_terms_by_depth( $terms, $target_depth ) { + $filtered = []; + + foreach ( $terms as $term ) { + $this->filter_single_term( $filtered, $terms, $term, $target_depth ); + } + + return $filtered; + } + + /** + * @param \WP_Term[] $terms + * @param \WP_Term $current_term + * @param int $target_depth + * @return void + */ + private function filter_single_term( &$result, $terms, $current_term, $target_depth ) { + if ( 0 === $current_term->parent ) { + $result[ $current_term->parent ][ $current_term->term_id ] = $current_term; + return; + } + + $item_depth = $this->calculate_depth_for_child_term_recursively( $terms, $current_term, 0 ); + + if ( $item_depth <= $target_depth ) { + $result[ $current_term->parent ][ $current_term->term_id ] = $current_term; + } + } + + /** + * @param \WP_Term[] $terms + * @param \WP_Term $child_term + * @param int $depth + * @return int|void + */ + private function calculate_depth_for_child_term_recursively( $terms, $child_term, $depth ) { + $depth++; + + foreach ( $terms as $term ) { + if ( $term->term_id !== $child_term->parent ) { + continue; + } + + if ( 0 === $term->parent ) { + return $depth; + } + return $this->calculate_depth_for_child_term_recursively( $terms, $term, $depth ); + } + } + +} diff --git a/modules/loop-filter/query/taxonomy-query-builder.php b/modules/loop-filter/query/taxonomy-query-builder.php new file mode 100644 index 0000000..959653e --- /dev/null +++ b/modules/loop-filter/query/taxonomy-query-builder.php @@ -0,0 +1,81 @@ +query = Query_Constants::DATA; + } + + public function get_merged_queries( &$tax_query, $taxonomy, $filter ) { + $this->tax_query = &$tax_query; + + // Taxonomy Filter parameter is empty i.e. `e-filter-389c132-product_cat=`. + if ( ! empty( $filter['terms'] ) && '' === $filter['terms'][0] ) { + return; + } + + $this->taxonomy_manager = new Taxonomy_Manager(); + $this->taxonomy_manager->get_taxonomy_terms( $taxonomy ); + $this->filter_terms = $this->taxonomy_manager->get_hierarchy_of_selected_terms( $filter, $taxonomy ); + + if ( ! empty( $this->filter_terms['single-term'] ) ) { + $this->get_single_selection_query( $tax_query ); + return; + } + + $this->get_multiple_selection_query( $tax_query ); + } + + private function get_single_selection_query( &$tax_query ) { + $this->single_terms_query = $this->get_query( new Single_Terms_Query( $this->filter_terms, $this->taxonomy_manager ) ); + $this->merge_single_selection_query( $tax_query ); + } + + private function get_multiple_selection_query( &$tax_query ) { + $this->single_terms_query = $this->get_query( new Single_Terms_Query( $this->filter_terms, $this->taxonomy_manager ) ); + + if ( 'AND' === $this->filter_terms['logicalJoin'] ) { + $this->hierarchy_query = $this->get_query( new Hierarchy_And_Query( $this->filter_terms, $this->taxonomy_manager ) ); + } + + if ( 'OR' === $this->filter_terms['logicalJoin'] ) { + $this->hierarchy_query = $this->get_query( new Hierarchy_Or_Query( $this->filter_terms, $this->taxonomy_manager ) ); + } + + $this->merge_multiple_selection_query( $tax_query ); + } + + private function merge_single_selection_query( &$tax_query ) { + if ( empty( $this->single_terms_query ?? [] ) ) { + return; + } + + $tax_query = array_merge( $tax_query, $this->single_terms_query ?? [] ); + } + + private function merge_multiple_selection_query( &$tax_query ) { + if ( empty( $this->single_terms_query ?? [] ) && empty( $this->hierarchy_query ?? [] ) ) { + return; + } + + $tax_query = [ array_merge( $tax_query, $this->single_terms_query ?? [], $this->hierarchy_query ?? [], [ 'relation' => $this->query[ $this->filter_terms['logicalJoin'] ]['relation'] ] ) ]; + } + + private function get_query( Query_Interface $query_type ) { + return $query_type->get_query(); + } +} diff --git a/modules/loop-filter/traits/hierarchical-taxonomy-trait.php b/modules/loop-filter/traits/hierarchical-taxonomy-trait.php new file mode 100644 index 0000000..a269a54 --- /dev/null +++ b/modules/loop-filter/traits/hierarchical-taxonomy-trait.php @@ -0,0 +1,80 @@ +filter_single_term( $filtered, $terms, $term, $target_depth ); + } + + return $filtered; + } + + /** + * @param \WP_Term[] $terms + * @param \WP_Term $current_term + * @param int $target_depth + * @return void + */ + private function filter_single_term( &$result, $terms, $current_term, $target_depth ) { + if ( 0 === $current_term->parent ) { + $result[ $current_term->parent ][] = $current_term; + return; + } + + $item_depth = $this->calculate_depth_for_child_term( $terms, $current_term, 0 ); + + if ( $item_depth <= $target_depth ) { + $result[ $current_term->parent ][] = $current_term; + } + } + + /** + * @param \WP_Term[] $terms + * @param \WP_Term $child_term + * @param int $depth + * @return int|void + */ + private function calculate_depth_for_child_term( $terms, $child_term, $depth ) { + $depth++; + + foreach ( $terms as $term ) { + if ( $term->term_id !== $child_term->parent ) { + continue; + } + + if ( 0 === $term->parent ) { + return $depth; + } + return $this->calculate_depth_for_child_term( $terms, $term, $depth ); + } + } + + + /** + * Transform terms hierarchy structure to plain [ parent_term_id => [ term, term ... ], ...] to [ term, term, ... ] + * + * @param array $taxonomy_plain_view + * @param array $hierarchy_terms + * @param int $parent_term_id + * @return void + */ + public function transform_taxonomy_hierarchy_to_plain( &$taxonomy_plain_view, $hierarchy_terms, $parent_term_id = 0 ) { + if ( empty( $hierarchy_terms[ $parent_term_id ] ) ) { + return; + } + + foreach ( $hierarchy_terms[ $parent_term_id ] as $term ) { + $taxonomy_plain_view[] = $term; + $this->transform_taxonomy_hierarchy_to_plain( $taxonomy_plain_view, $hierarchy_terms, $term->term_id ); + } + } +} diff --git a/modules/loop-filter/traits/taxonomy-filter-trait.php b/modules/loop-filter/traits/taxonomy-filter-trait.php new file mode 100644 index 0000000..09916b1 --- /dev/null +++ b/modules/loop-filter/traits/taxonomy-filter-trait.php @@ -0,0 +1,93 @@ +get_taxonomies_to_exclude( $post_type ) ); + } + + $control_options = []; + + foreach ( $post_type_taxonomies as $taxonomy ) { + if ( $this->should_exclude_taxonomy( $taxonomy->name, $taxonomies_to_exclude ) ) { + continue; + } + + $control_options[ $key_prefix . $taxonomy->name ] = $taxonomy->label; + } + + return $control_options; + } + + /** + * @param array $settings + * @param array $display_settings + * @return void|\WP_Term[] + */ + public function get_filtered_taxonomies( $settings, $display_settings ) { + $args = [ + 'taxonomy' => $settings['taxonomy'], + 'hide_empty' => 'yes' !== $settings['show_empty_items'], + ]; + $avoid_reset_parent = ! empty( $settings['avoid_reset_parent'] ); + + if ( ( ! isset( $settings['show_child_taxonomy'] ) || 'yes' !== $settings['show_child_taxonomy'] ) && ! $avoid_reset_parent ) { + $args['parent'] = 0; + } + + $args = apply_filters( + 'elementor/loop_taxonomy/args', + $this->get_additional_allowed_args( $args, $display_settings ), + $settings, + $display_settings, + ); + + $terms = get_terms( $args ); + + if ( is_wp_error( $terms ) ) { + return; + } + + if ( isset( $settings['show_child_taxonomy'] ) && 'yes' === $settings['show_child_taxonomy'] && isset( $display_settings['child_taxonomy_depth'] ) ) { + $taxonomy_plain_view = []; + $hierarchy_terms = $this->filter_child_terms_by_depth( $terms, $display_settings['child_taxonomy_depth'] ); + $this->transform_taxonomy_hierarchy_to_plain( $taxonomy_plain_view, $hierarchy_terms ); + $terms = ! empty( $taxonomy_plain_view ) ? $taxonomy_plain_view : $terms; + } + + return $terms; + } + + private function get_additional_allowed_args( $args, $display_settings ) { + $allowed_args = [ 'include', 'exclude', 'term_taxonomy_id', 'child_taxonomy_depth', 'hierarchical', 'child_of', 'offset', 'orderby', 'order', 'number' ]; + + foreach ( $allowed_args as $arg ) { + if ( isset( $display_settings[ $arg ] ) ) { + $args[ $arg ] = $display_settings[ $arg ]; + } + } + + return $args; + } + + private function get_taxonomies_to_exclude( $post_type ) { + $excluded_taxonomies_per_post_type = [ + 'post' => [ 'post_format' ], + 'product' => [ 'product_type', 'product_visibility', 'product_shipping_class', 'pa_color', 'pa_size' ], + ]; + + return $excluded_taxonomies_per_post_type[ $post_type ] ?? []; + } + + private function should_exclude_taxonomy( $taxonomy_name, $taxonomies_to_exclude ) { + return ! empty( $taxonomies_to_exclude ) && in_array( $taxonomy_name, $taxonomies_to_exclude ); + } +} diff --git a/modules/loop-filter/widgets/taxonomy-filter.php b/modules/loop-filter/widgets/taxonomy-filter.php new file mode 100644 index 0000000..4591168 --- /dev/null +++ b/modules/loop-filter/widgets/taxonomy-filter.php @@ -0,0 +1,809 @@ +start_controls_section( + 'section_taxonomy_filter', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'selected_element', + [ + 'label' => esc_html__( 'Selected loop grid', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Select a widget', 'elementor-pro' ), + ], + 'label_block' => true, + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'taxonomy', + [ + 'label' => esc_html__( 'Taxonomy', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Select a taxonomy', 'elementor-pro' ), + ], + 'label_block' => true, + 'condition' => [ + 'selected_element!' => '', + ], + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( + 'direction', + [ + 'label' => esc_html__( 'Direction', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'horizontal' => esc_html__( 'Horizontal', 'elementor-pro' ), + 'vertical' => esc_html__( 'Vertical', 'elementor-pro' ), + ], + 'default' => 'horizontal', + 'selectors_dictionary' => [ + 'horizontal' => '--e-filter-direction: row;--e-filter-white-space: nowrap;', + 'vertical' => '--e-filter-direction: column;--e-filter-white-space: initial;', + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}};', + ], + 'condition' => [ + 'selected_element!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'item_alignment_horizontal', + [ + 'label' => esc_html__( 'Item Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-align-start-h', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-align-center-h', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-align-end-h', + ], + 'stretch' => [ + 'title' => esc_html__( 'Stretch', 'elementor-pro' ), + 'icon' => 'eicon-align-stretch-h', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}};', + ], + 'selectors_dictionary' => [ + 'start' => '--e-filter-justify-content: flex-start; --e-filter-item-width: initial; --e-filter-item-flex-grow: 0;', + 'center' => '--e-filter-justify-content: center; --e-filter-item-width: initial; --e-filter-item-flex-grow: 0;', + 'end' => '--e-filter-justify-content: flex-end; --e-filter-item-width: initial; --e-filter-item-flex-grow: 0;', + 'stretch' => '--e-filter-justify-content: initial; --e-filter-item-width: 100%; --e-filter-item-flex-grow: 1;', + ], + 'condition' => [ + 'direction' => 'horizontal', + 'selected_element!' => '', + ], + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( + 'title_alignment_horizontal', + [ + 'label' => esc_html__( 'Title Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-align-start-h', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-align-center-h', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-align-end-h', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}};', + ], + 'selectors_dictionary' => [ + 'start' => '--e-filter-item-justify-content: flex-start;', + 'center' => '--e-filter-item-justify-content: center;', + 'end' => '--e-filter-item-justify-content: flex-end;', + ], + 'condition' => [ + 'direction' => 'horizontal', + 'selected_element!' => '', + 'item_alignment_horizontal' => 'stretch', + ], + ] + ); + + $this->add_responsive_control( + 'item_alignment_vertical', + [ + 'label' => esc_html__( 'Item Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-align-start-h', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-align-center-h', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-align-end-h', + ], + 'stretch' => [ + 'title' => esc_html__( 'Stretch', 'elementor-pro' ), + 'icon' => 'eicon-align-stretch-h', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}};', + ], + 'selectors_dictionary' => [ + 'start' => '--e-filter-align-items: flex-start; --e-filter-item-width: initial; --e-filter-item-max-width: calc(100% - calc(var( --e-filter-item-padding )*3 ) ); --e-filter-item-flex-grow: initial; --e-filter-item-box-sizing: initial; --e-filter-item-align-text: start;', + 'center' => '--e-filter-align-items: center; --e-filter-item-width: initial; --e-filter-item-max-width: calc(100% - calc(var( --e-filter-item-padding )*3 ) ); --e-filter-item-flex-grow: initial; --e-filter-item-box-sizing: initial; --e-filter-item-align-text: center;', + 'end' => '--e-filter-align-items: flex-end; --e-filter-item-width: initial; --e-filter-item-max-width: calc(100% - calc(var( --e-filter-item-padding )*3 ) ); --e-filter-item-flex-grow: initial; --e-filter-item-box-sizing: initial; --e-filter-item-align-text: end;', + 'stretch' => '--e-filter-align-items: center; --e-filter-item-width: 100%; --e-filter-item-max-width: 100%; --e-filter-item-flex-grow: 1; --e-filter-item-box-sizing: border-box; --e-filter-item-align-text: center;', + ], + 'condition' => [ + 'direction' => 'vertical', + 'selected_element!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'title_alignment_vertical', + [ + 'label' => esc_html__( 'Title Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-align-start-h', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-align-center-h', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-align-end-h', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}};', + ], + 'selectors_dictionary' => [ + 'start' => '--e-filter-item-justify-content: flex-start; --e-filter-item-align-items: flex-start; --e-filter-item-align-text: start;', + 'center' => '--e-filter-item-justify-content: center; --e-filter-item-align-items: center; --e-filter-item-align-text: center;', + 'end' => '--e-filter-item-justify-content: flex-end; --e-filter-item-align-items: flex-end; --e-filter-item-align-text: end;', + ], + 'condition' => [ + 'direction' => 'vertical', + 'selected_element!' => '', + 'item_alignment_vertical' => 'stretch', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_settings', + [ + 'label' => esc_html__( 'Settings', 'elementor-pro' ), + 'condition' => [ + 'selected_element!' => '', + ], + ] + ); + + $this->add_control( + 'heading_filter_logic', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Filter Logic', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'multiple_selection', + [ + 'label' => esc_html__( 'Multiple Selection', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'default' => 'no', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'logical_combination', + [ + 'label' => esc_html__( 'Logical Combination', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'AND', + 'options' => [ + 'AND' => esc_html__( 'AND', 'elementor-pro' ), + 'OR' => esc_html__( 'OR', 'elementor-pro' ), + ], + 'condition' => [ + 'multiple_selection' => 'yes', + ], + 'separator' => 'after', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'heading_displayed_elements', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Displayed Elements', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'show_empty_items', + [ + 'label' => esc_html__( 'Empty Items', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'no', + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'show_child_taxonomy', + [ + 'label' => esc_html__( 'Taxonomy Children', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'no', + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'child_taxonomy_depth', + [ + 'label' => esc_html__( 'Depth', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '1', + 'options' => [ + '1' => esc_html__( '1', 'elementor-pro' ), + '2' => esc_html__( '2', 'elementor-pro' ), + '3' => esc_html__( '3', 'elementor-pro' ), + '4' => esc_html__( '4', 'elementor-pro' ), + '5' => esc_html__( '5', 'elementor-pro' ), + '6' => esc_html__( '6', 'elementor-pro' ), + ], + 'condition' => [ + 'show_child_taxonomy' => 'yes', + ], + ] + ); + $this->add_control( + 'show_first_item', + [ + 'label' => esc_html__( 'First Item', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'first_item_title', + [ + 'label' => esc_html__( 'First Item Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'All', 'elementor-pro' ), + 'label_block' => true, + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'show_first_item' => 'yes', + ], + ] + ); + + $this->add_control( + 'number_of_taxonomies', + [ + 'label' => esc_html__( 'Number of taxonomies', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'min' => 1, + ] + ); + + $this->add_responsive_control( + 'horizontal_scroll', + [ + 'label' => esc_html__( 'Horizontal Scroll', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'disable' => esc_html__( 'Disable', 'elementor-pro' ), + 'enable' => esc_html__( 'Enable', 'elementor-pro' ), + ], + 'condition' => [ + 'direction' => 'horizontal', + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}};', + ], + 'selectors_dictionary' => [ + 'enable' => '--e-filter-wrap: nowrap; --e-filter-overflow-x: scroll;', + 'disable' => '--e-filter-wrap: wrap; --e-filter-overflow-x: initial;', + ], + 'default' => 'disable', + 'frontend_available' => true, + 'separator' => 'before', + 'description' => esc_html__( 'Scroll items if they don’t fit into their parent container', 'elementor-pro' ), + ] + ); + + $this->end_controls_section(); + + $this->register_design_layout_controls(); + } + + protected function register_design_layout_controls() { + $this->start_controls_section( + 'section_design_layout', + [ + 'label' => esc_html__( 'Items', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'taxonomy_filter_items_space_between', + [ + 'label' => esc_html__( 'Space between Items', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-filter-space-between: {{SIZE}}{{UNIT}}', + ], + 'separator' => 'after', + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'taxonomy_filter_typography', + 'selector' => '{{WRAPPER}} .e-filter-item', + ] + ); + + $this->start_controls_tabs( 'taxonomy_filter_tabs_section' ); + + $this->start_controls_tab( 'taxonomy_filter_tabs_normal', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'taxonomy_filter_normal_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--e-filter-normal-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'taxonomy_filter_normal_text_shadow', + 'selector' => '{{WRAPPER}} .e-filter-item:not( [aria-pressed=true] ):not( :hover )', + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'taxonomy_filter_normal_background', + 'selector' => '{{WRAPPER}} .e-filter-item:not( [aria-pressed=true] ):not( :hover )', + 'exclude' => [ 'image', 'video' ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'taxonomy_filter_normal_border', + 'selector' => '{{WRAPPER}} .e-filter-item:not( [aria-pressed=true] ):not( :hover )', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'taxonomy_filter_normal_box_shadow', + 'selector' => '{{WRAPPER}} .e-filter-item:not( [aria-pressed=true] ):not( :hover )', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'taxonomy_filter_tabs_hover', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'taxonomy_filter_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--e-filter-hover-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'taxonomy_filter_hover_text_shadow', + 'selector' => '{{WRAPPER}} .e-filter-item:hover:not( [aria-pressed=true] )', + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'taxonomy_filter_hover_background', + 'selector' => '{{WRAPPER}} .e-filter-item:hover:not( [aria-pressed=true] )', + 'exclude' => [ 'image', 'video' ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'taxonomy_filter_hover_border', + 'selector' => '{{WRAPPER}} .e-filter-item:hover:not( [aria-pressed=true] )', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'taxonomy_filter_hover_box_shadow', + 'selector' => '{{WRAPPER}} .e-filter-item:hover:not( [aria-pressed=true] )', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'taxonomy_filter_tabs_active', [ 'label' => esc_html__( 'Active', 'elementor-pro' ) ] ); + + $this->add_control( + 'taxonomy_filter_active_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--e-filter-active-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'taxonomy_filter_active_text_shadow', + 'selector' => '{{WRAPPER}} .e-filter-item[aria-pressed="true"]', + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'taxonomy_filter_active_background', + 'selector' => '{{WRAPPER}} .e-filter-item[aria-pressed="true"]', + 'exclude' => [ 'image', 'video' ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'taxonomy_filter_active_border', + 'selector' => '{{WRAPPER}} .e-filter-item[aria-pressed="true"]', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'taxonomy_filter_active_box_shadow', + 'selector' => '{{WRAPPER}} .e-filter-item[aria-pressed="true"]', + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_responsive_control( + 'taxonomy_filter_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-filter-item-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'taxonomy_filter_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vm', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-filter-item' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function get_empty_widget_message_by_key( $message_key ) { + $messages = [ + 'select_loop_widget' => esc_html__( 'Choose a Loop Grid to view the Taxonomy Filter.', 'elementor-pro' ), + 'no_taxonomy_selected' => esc_html__( 'Please select a taxonomy.', 'elementor-pro' ), + 'no_terms_found' => esc_html__( 'No taxonomy terms found.', 'elementor-pro' ), + ]; + + return $messages[ $message_key ]; + } + + protected function print_empty_results_if_editor( $message_key ) { + if ( ! Plugin::elementor()->editor->is_edit_mode() ) { + return; + } + + ?> +
    + get_empty_widget_message_by_key( $message_key ) ); ?> +
    + print_empty_results_if_editor( 'select_loop_widget' ); + + return true; + } + + if ( empty( $user_selected_taxonomy ) ) { + $this->print_empty_results_if_editor( 'no_taxonomy_selected' ); + + return true; + } + + if ( empty( $terms ) ) { + $this->print_empty_results_if_editor( 'no_terms_found' ); + + return true; + } + + return false; + } + + /** + * @return array + */ + private function get_loop_widget_settings() { + $document = Plugin::elementor()->documents->get_doc_for_frontend( $this->get_current_ID() ); + + if ( ! $document ) { + return []; + } + + $widget_data = Utils::find_element_recursive( $document->get_elements_data(), $this->get_settings_for_display( 'selected_element' ) ); + + return ! empty( $widget_data['settings'] ) ? $widget_data['settings'] : []; + } + + /** + * @return int + */ + private function get_current_ID() { + $post_id = 0; + $theme_builder = ThemeBuilderModule::instance(); + $location = $theme_builder->get_locations_manager()->get_current_location(); + $documents = $theme_builder->get_conditions_manager()->get_documents_for_location( $location ); + + if ( empty( $documents ) ) { + return get_the_ID(); + } + + foreach ( $documents as $document ) { + $post_id = $document->get_post()->ID; + } + + return $post_id; + } + + /** + * @return boolean + */ + private function is_term_excluded_by_query_control( $term, $loop_filter_module ) { + $loop_widget_settings = $this->get_loop_widget_settings(); + $skin = ! empty( $loop_widget_settings['_skin'] ) ? $loop_widget_settings['_skin'] : 'post'; + + return $loop_filter_module->is_term_not_selected_for_inclusion( $loop_widget_settings, $term, $skin ) + || $loop_filter_module->is_term_selected_for_exclusion( $loop_widget_settings, $term, $skin ) + || $loop_filter_module->should_exclude_term_by_manual_selection( $loop_widget_settings, $term, $this->get_settings_for_display( 'taxonomy' ), $skin ); + } + + public function render() { + $settings = $this->get_settings_for_display(); + $selected_element = $settings['selected_element']; + $user_selected_taxonomy = $settings['taxonomy']; + + $terms = $this->get_filtered_taxonomy_terms( $user_selected_taxonomy, $settings ); + + if ( $this->has_empty_results( $selected_element, $user_selected_taxonomy, $terms ) ) { + return; + } + + $active_filters = []; + $loop_filter_module = Plugin::instance()->modules_manager->get_modules( 'loop-filter' ); + $query_string_filters = $loop_filter_module->get_query_string_filters(); + + if ( array_key_exists( $selected_element, $query_string_filters ) ) { + $active_filters = $query_string_filters[ $selected_element ]['taxonomy']; + } + + $active_terms = 0; + $total_taxonomies = 0; + $number_of_taxonomies = $settings['number_of_taxonomies']; + + $this->add_render_attribute( 'filter-bar', [ + 'class' => 'e-filter', + 'role' => 'search', // BC for older browser versions that don't support `` element. + 'data-base-url' => $this->get_base_url(), + 'data-page-num' => max( 1, get_query_var( 'paged' ), get_query_var( 'page' ) ), + ] ); + ?> + print_render_attribute_string( 'filter-bar' ); ?>> + taxonomy ) || $this->is_term_excluded_by_query_control( $term, $loop_filter_module ) ) { + continue; + } + + $term_taxonomy = $term->taxonomy; + + if ( array_key_exists( $term_taxonomy, $active_filters ) && in_array( urldecode( $term->slug ), $active_filters[ $term_taxonomy ]['terms'] ) ) { + $aria_pressed_value = 'true'; + $active_terms++; + } + + if ( ! empty( $number_of_taxonomies ) && $total_taxonomies > $number_of_taxonomies ) { + continue; + } + + // This filter allows us to write the slug with non-latin characters as well, such as Hebrew. + $slug = apply_filters( 'editable_slug', $term->slug, $term ); + ?> + + + + + + + + + get_settings_for_display(); + $settings['taxonomy'] = $user_selected_taxonomy; + + return $this->get_filtered_taxonomies( $settings, $display_settings ); + } +} diff --git a/modules/lottie/assets/animations/default.json b/modules/lottie/assets/animations/default.json new file mode 100644 index 0000000..3a1107f --- /dev/null +++ b/modules/lottie/assets/animations/default.json @@ -0,0 +1 @@ +{"v":"5.5.7","meta":{"g":"LottieFiles AE ","a":"","k":"","d":"","tc":""},"fr":25,"ip":0,"op":125,"w":1920,"h":1080,"nm":"InLogo 3","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 14","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[862,408,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[28.7,53,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Turbulent Displace","np":16,"mn":"ADBE Turbulent Displace","ix":1,"en":1,"ef":[{"ty":7,"nm":"Displacement","mn":"ADBE Turbulent Displace-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":0,"nm":"Amount","mn":"ADBE Turbulent Displace-0002","ix":2,"v":{"a":0,"k":33,"ix":2}},{"ty":0,"nm":"Size","mn":"ADBE Turbulent Displace-0003","ix":3,"v":{"a":0,"k":90,"ix":3}},{"ty":3,"nm":"Offset (Turbulence)","mn":"ADBE Turbulent Displace-0004","ix":4,"v":{"a":0,"k":[960,540],"ix":4}},{"ty":0,"nm":"Complexity","mn":"ADBE Turbulent Displace-0005","ix":5,"v":{"a":0,"k":1,"ix":5}},{"ty":0,"nm":"Evolution","mn":"ADBE Turbulent Displace-0006","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":6,"nm":"Evolution Options","mn":"ADBE Turbulent Displace-0007","ix":7,"v":0},{"ty":7,"nm":"Cycle Evolution","mn":"ADBE Turbulent Displace-0008","ix":8,"v":{"a":0,"k":0,"ix":8}},{"ty":0,"nm":"Cycle (in Revolutions)","mn":"ADBE Turbulent Displace-0009","ix":9,"v":{"a":0,"k":1,"ix":9}},{"ty":0,"nm":"Random Seed","mn":"ADBE Turbulent Displace-0010","ix":10,"v":{"a":0,"k":0,"ix":10}},{"ty":6,"nm":"Random Seed","mn":"ADBE Turbulent Displace-0011","ix":11,"v":0},{"ty":7,"nm":"Pinning","mn":"ADBE Turbulent Displace-0012","ix":12,"v":{"a":0,"k":3,"ix":12}},{"ty":7,"nm":"Resize Layer","mn":"ADBE Turbulent Displace-0013","ix":13,"v":{"a":0,"k":0,"ix":13}},{"ty":7,"nm":"Antialiasing for Best Quality","mn":"ADBE Turbulent Displace-0014","ix":14,"v":{"a":0,"k":1,"ix":14}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":66.833,"s":[{"i":[[4.75,0.75],[1,-1.25],[-8,-0.25]],"o":[[-4.75,-0.75],[-1,1.25],[8,0.25]],"v":[[346.5,-0.25],[334.25,0.5],[341.75,4.25]],"c":true}],"h":1},{"t":68.5003255208333,"s":[{"i":[[1.286,0.48],[0.271,-0.799],[-2.167,-0.16]],"o":[[-1.286,-0.48],[-0.271,0.799],[2.167,0.16]],"v":[[352.589,0.471],[349.271,0.951],[351.302,3.348]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":66,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":66.833,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[100]},{"t":70.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 13","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":70.166,"s":[{"i":[[4.18,-0.372],[0.022,-1.823],[-6.574,0.303],[0.536,2.784]],"o":[[-4.18,0.372],[-0.03,2.442],[6.574,-0.303],[-0.604,-3.14]],"v":[[-510.668,-7.313],[-517.525,-0.891],[-508.861,4.275],[-501.457,-3.208]],"c":true}],"h":1},{"t":71.833,"s":[{"i":[[1.998,-0.14],[-0.044,-1.297],[-3.151,0.02],[0.341,1.997]],"o":[[-1.998,0.14],[0.058,1.737],[3.151,-0.02],[-0.384,-2.252]],"v":[[-518.552,-4.632],[-521.656,-0.268],[-517.355,3.533],[-511.502,-1.693]],"c":true}],"h":1},{"t":73.5003255208333,"s":[{"i":[[1.184,-0.136],[-0.026,-0.772],[-2.27,0.176],[0.203,1.189]],"o":[[-2.219,0.255],[0.035,1.034],[1.87,-0.145],[-0.229,-1.341]],"v":[[-521.063,-2.58],[-523.443,0.073],[-520.398,2.327],[-517.399,-0.776]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[830.5,-2.125],"ix":2},"a":{"a":0,"k":[-502,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":187.253,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":74.333,"s":[100]},{"t":75.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 12","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":68.5,"s":[{"i":[[8.475,-4.457],[-1.556,-3.859],[-13.577,6.419],[3.576,5.392]],"o":[[-8.475,4.457],[2.084,5.169],[13.577,-6.419],[-4.034,-6.081]],"v":[[-523.509,-12.882],[-532.302,6.668],[-509.517,9.93],[-500.504,-12.337]],"c":true}],"h":1},{"t":70.166,"s":[{"i":[[2.779,-0.194],[-0.061,-1.804],[-4.383,0.027],[0.474,2.778]],"o":[[-2.779,0.194],[0.081,2.416],[4.383,-0.027],[-0.535,-3.133]],"v":[[-531.232,-4.697],[-535.55,1.374],[-529.567,6.662],[-521.425,-0.608]],"c":true}],"h":1},{"t":71.8333333333333,"s":[{"i":[[1.516,-0.174],[-0.033,-0.988],[-2.905,0.226],[0.26,1.521]],"o":[[-2.841,0.327],[0.044,1.323],[2.393,-0.186],[-0.293,-1.716]],"v":[[-538.976,-1.008],[-542.022,2.387],[-538.125,5.273],[-534.286,1.301]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[758.5,-2.125],"ix":2},"a":{"a":0,"k":[-502,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":187.253,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":67.666,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":68.5,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":72.666,"s":[100]},{"t":73.5003255208333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 11","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":66.833,"s":[{"i":[[5.552,-0.494],[0.03,-2.422],[-8.733,0.403],[0.712,3.698]],"o":[[-5.552,0.494],[-0.04,3.244],[8.733,-0.403],[-0.803,-4.171]],"v":[[-518.497,-8.318],[-527.605,0.213],[-516.128,6.827],[-499.303,-3.636]],"c":true}],"h":1},{"t":68.5,"s":[{"i":[[2.779,-0.194],[-0.061,-1.804],[-4.383,0.027],[0.474,2.778]],"o":[[-2.779,0.194],[0.081,2.416],[4.383,-0.027],[-0.535,-3.133]],"v":[[-531.232,-4.697],[-535.55,1.374],[-529.567,6.662],[-521.425,-0.608]],"c":true}],"h":1},{"t":70.1663411458333,"s":[{"i":[[1.516,-0.174],[-0.033,-0.988],[-2.905,0.226],[0.26,1.521]],"o":[[-2.841,0.327],[0.044,1.323],[2.393,-0.186],[-0.293,-1.716]],"v":[[-538.976,-1.008],[-542.022,2.387],[-538.125,5.273],[-534.286,1.301]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[638,-2],"ix":2},"a":{"a":0,"k":[-502,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":187.253,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":66,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":66.833,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":71,"s":[100]},{"t":71.8333333333333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 10","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":66.833,"s":[{"i":[[5.552,-0.494],[0.03,-2.422],[-8.733,0.403],[0.712,3.698]],"o":[[-5.552,0.494],[-0.04,3.244],[8.733,-0.403],[-0.803,-4.171]],"v":[[-517.685,-5.901],[-527.515,-1.059],[-517.499,3.978],[-494.807,-3.956]],"c":true}],"h":1},{"t":68.5,"s":[{"i":[[2.327,-0.154],[-0.063,-1.604],[-3.672,-0.005],[0.416,2.473]],"o":[[-2.327,0.154],[0.084,2.149],[3.672,0.005],[-0.469,-2.789]],"v":[[-526.191,-3.085],[-530.187,-0.183],[-525.805,3.467],[-516.479,-1.084]],"c":true}],"h":1},{"t":70.1663411458333,"s":[{"i":[[0.793,-0.028],[-0.057,-0.821],[-1.256,-0.083],[0.196,1.272]],"o":[[-0.793,0.028],[0.076,1.099],[1.256,0.083],[-0.221,-1.435]],"v":[[-530.305,-0.876],[-531.609,0.52],[-530.03,2.479],[-526.94,0.358]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[542,-2],"ix":2},"a":{"a":0,"k":[-502,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":187.253,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":66,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":66.833,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":71,"s":[100]},{"t":71.8333333333333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 9","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":71.833,"s":[{"i":[[3.776,0.019],[-1.805,-0.357],[0.815,1.363]],"o":[[-1.825,-0.009],[1.641,0.325],[0.065,-0.538]],"v":[[-461.744,-1.505],[-461.964,0.088],[-452.414,-0.453]],"c":true}],"h":1},{"t":73.5003255208333,"s":[{"i":[[1.518,-0.096],[-0.453,-0.638],[-0.713,0.267]],"o":[[-0.734,0.046],[0.453,0.638],[0.713,-0.267]],"v":[[-465.108,-1.338],[-467.09,-0.546],[-461.402,-0.46]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-341.688,1.75],"ix":2},"a":{"a":0,"k":[-455.313,-0.938],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":178.598,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":71,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":71.833,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":74.333,"s":[100]},{"t":75.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 8","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":70.166,"s":[{"i":[[3.776,0.019],[-1.031,-1.318],[0.815,1.363]],"o":[[-1.825,-0.009],[1.031,1.318],[0.065,-0.538]],"v":[[-461.237,-1.805],[-466.254,-0.892],[-449.162,-0.498]],"c":true}],"h":1},{"t":71.8333333333333,"s":[{"i":[[1.521,0],[-0.412,-0.665],[-0.729,0.222]],"o":[[-0.735,0],[0.412,0.665],[0.729,-0.222]],"v":[[-465.182,-1.777],[-467.21,-1.112],[-461.538,-0.668]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-382.875,1.25],"ix":2},"a":{"a":0,"k":[-455.313,-0.938],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":178.598,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":72.666,"s":[100]},{"t":73.5003255208333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 6","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":68.5,"s":[{"i":[[3,0],[-0.813,-1.313],[-1.438,0.438]],"o":[[-1.45,0],[0.813,1.313],[1.438,-0.438]],"v":[[-456.438,-2.188],[-460.438,-0.875],[-449.25,0]],"c":true}],"h":1},{"t":70.1663411458333,"s":[{"i":[[1.521,0],[-0.412,-0.665],[-0.729,0.222]],"o":[[-0.735,0],[0.412,0.665],[0.729,-0.222]],"v":[[-455.498,-1.54],[-457.526,-0.875],[-451.854,-0.431]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-421.625,1.125],"ix":2},"a":{"a":0,"k":[-455.375,-1.063],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-183.942,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":67.666,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":68.5,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":71,"s":[100]},{"t":71.8333333333333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 5","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":66.833,"s":[{"i":[[4.25,-0.125],[0.477,-1.343],[-17.974,-0.972],[-17.25,-1.25],[-0.226,6.325],[9.25,-0.75]],"o":[[-4.25,0.125],[-1.375,3.875],[18.5,1],[12.53,0.908],[0.25,-7],[-9.25,0.75]],"v":[[-455.25,-3.125],[-490.125,-1.625],[-454,3.75],[-411.25,5.25],[-374.25,0.75],[-414.75,-5.25]],"c":true}],"h":1},{"t":68.5,"s":[{"i":[[4.086,-0.081],[0.458,-0.874],[-5.769,-0.163],[-16.585,-0.814],[-0.217,4.118],[8.893,-0.488]],"o":[[-4.086,0.081],[-1.322,2.523],[17.797,0.502],[12.046,0.591],[0.24,-4.557],[-8.893,0.488]],"v":[[-435.501,-2.216],[-446.678,-0.832],[-437.905,2.748],[-397.063,4.062],[-367.24,0.632],[-399.178,-3.774]],"c":true}],"h":1},{"t":70.166,"s":[{"i":[[2.531,-0.05],[0.284,-0.542],[-3.573,-0.101],[-3.522,-0.193],[-0.135,2.55],[5.508,-0.302]],"o":[[-2.531,0.05],[-0.819,1.562],[11.022,0.311],[7.458,0.409],[0.149,-2.822],[-5.508,0.302]],"v":[[-392.395,-1.267],[-399.317,-0.41],[-393.884,1.807],[-378.731,2.904],[-365.138,0.47],[-379.886,-2.8]],"c":true}],"h":1},{"t":71.833,"s":[{"i":[[1.313,-0.257],[-0.001,-0.413],[-1.873,-0.146],[-1.851,0.065],[0.075,1.157],[2.532,0.118]],"o":[[-1.138,0.223],[-0.031,0.658],[2.76,0.215],[1.761,-0.062],[-0.059,-0.909],[-2.16,-0.101]],"v":[[-371.104,-1.254],[-374.278,0.212],[-371.197,1.838],[-365.507,2.199],[-362.637,0.385],[-365.818,-1.488]],"c":true}],"h":1},{"t":73.5003255208333,"s":[{"i":[[0.895,-0.155],[-0.001,-0.249],[-1.275,-0.088],[-1.26,0.039],[0.051,0.698],[1.724,0.072]],"o":[[-0.775,0.135],[-0.021,0.397],[1.88,0.13],[1.2,-0.037],[-0.04,-0.549],[-1.471,-0.061]],"v":[[-363.818,-0.623],[-365.979,0.262],[-363.88,1.243],[-360.005,1.461],[-358.051,0.366],[-360.217,-0.764]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-384.5,2],"ix":2},"a":{"a":0,"k":[-433,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":66,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":66.833,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":74.333,"s":[100]},{"t":75.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 7","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[430.984,2.882],"ix":2},"a":{"a":0,"k":[-383.766,1.382],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":65.166,"s":[{"i":[[5.552,-0.494],[0.03,-2.422],[-8.733,0.403],[1.951,2.164]],"o":[[-5.552,0.494],[-0.04,3.244],[8.733,-0.403],[-1.951,-2.164]],"v":[[-513.027,-2.965],[-524.904,1.633],[-513.464,5.984],[-492.197,-1.265]],"c":true}],"h":1},{"t":66.833,"s":[{"i":[[1.933,-0.356],[-0.386,-1.859],[-3.102,0.273],[1.062,1.668]],"o":[[-1.933,0.356],[0.517,2.489],[3.102,-0.273],[-1.062,-1.668]],"v":[[-522.22,-1.02],[-527.341,3.093],[-521.05,6.088],[-515.846,1.385]],"c":true}],"h":1},{"t":68.5003255208333,"s":[{"i":[[0.928,-0.039],[-0.058,-0.895],[-1.47,-0.077],[0.387,0.851]],"o":[[-0.928,0.039],[0.077,1.199],[1.47,0.077],[-0.387,-0.851]],"v":[[-526.389,1.435],[-529.058,3.02],[-526.312,4.838],[-523.566,2.982]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[221,-2],"ix":2},"a":{"a":0,"k":[-502,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":187.253,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[100]},{"t":70.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 8","np":3,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":65.166,"s":[{"i":[[2.487,-0.104],[-0.155,-2.399],[-3.938,-0.207],[1.036,2.28]],"o":[[-2.487,0.104],[0.207,3.213],[3.938,0.207],[-1.036,-2.28]],"v":[[-504.58,-4.041],[-509.655,-0.308],[-504.157,4.8],[-495.002,-0.908]],"c":true}],"h":1},{"t":66.833,"s":[{"i":[[1.933,-0.356],[-0.386,-1.859],[-3.102,0.273],[1.062,1.668]],"o":[[-1.933,0.356],[0.517,2.489],[3.102,-0.273],[-1.062,-1.668]],"v":[[-502.876,-3.482],[-507.998,0.631],[-501.706,3.626],[-496.502,-1.077]],"c":true}],"h":1},{"t":68.5003255208333,"s":[{"i":[[0.928,-0.039],[-0.058,-0.895],[-1.47,-0.077],[0.387,0.851]],"o":[[-0.928,0.039],[0.077,1.199],[1.47,0.077],[-0.387,-0.851]],"v":[[-502.085,-1.659],[-504.754,-0.073],[-502.008,1.745],[-499.262,-0.112]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[23,0],"ix":2},"a":{"a":0,"k":[-502,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":187.253,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[100]},{"t":70.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 7","np":3,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":65.166,"s":[{"i":[[3,-0.125],[-0.187,-2.893],[-4.75,-0.25],[1.25,2.75]],"o":[[-3,0.125],[0.25,3.875],[4.75,0.25],[-1.25,-2.75]],"v":[[-502.625,-5.5],[-511.25,-0.375],[-502.375,5.5],[-493.5,-0.5]],"c":true}],"h":1},{"t":66.833,"s":[{"i":[[1.964,-0.082],[-0.122,-1.894],[-3.11,-0.164],[0.818,1.8]],"o":[[-1.964,0.082],[0.164,2.537],[3.11,0.164],[-0.818,-1.8]],"v":[[-502.355,-3.579],[-508.002,-0.224],[-502.191,3.622],[-496.381,-0.306]],"c":true}],"h":1},{"t":68.5003255208333,"s":[{"i":[[0.928,-0.039],[-0.058,-0.895],[-1.47,-0.077],[0.387,0.851]],"o":[[-0.928,0.039],[0.077,1.199],[1.47,0.077],[-0.387,-0.851]],"v":[[-502.085,-1.659],[-504.754,-0.073],[-502.008,1.745],[-499.262,-0.112]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-296,0],"ix":2},"a":{"a":0,"k":[-502,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":171.987,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[100]},{"t":70.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 6","np":3,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":65.166,"s":[{"i":[[4.875,1.125],[1,-4],[-3,1.125]],"o":[[-4.7,-1.085],[-1,4],[9.843,-3.691]],"v":[[-238.125,-1.125],[-265.5,3.125],[-229.125,6.375]],"c":true}],"h":1},{"t":66.833,"s":[{"i":[[3.438,0.793],[0.705,-2.821],[-2.116,0.793]],"o":[[-3.314,-0.765],[-0.705,2.821],[6.941,-2.603]],"v":[[-232.569,0.075],[-247.202,2.984],[-228.691,5.187]],"c":true}],"h":1},{"t":68.5003255208333,"s":[{"i":[[1.056,0.411],[0.217,-1.463],[-0.65,0.411]],"o":[[-1.019,-0.397],[-0.217,1.463],[2.133,-1.35]],"v":[[-227.556,1.312],[-232.053,2.821],[-226.364,3.964]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[88,-3],"ix":2},"a":{"a":0,"k":[-241,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[100]},{"t":70.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 5","np":3,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":65.166,"s":[{"i":[[3.375,3.25],[1,-4],[-10.5,-0.5]],"o":[[-6.918,-6.662],[-1,4],[10.5,0.5]],"v":[[-220.5,1],[-264.5,2],[-230,6]],"c":true}],"h":1},{"t":66.833,"s":[{"i":[[1.463,1.5],[0.55,-2.933],[-5.754,0.611]],"o":[[-4.478,-4.592],[-0.55,2.933],[5.688,-0.604]],"v":[[-219.4,1],[-243.6,1.733],[-224.625,4.667]],"c":true}],"h":1},{"t":68.5003255208333,"s":[{"i":[[0.571,0.861],[0.186,-1.867],[-1.309,0.229]],"o":[[-1.7,-2.563],[-0.186,1.867],[1.937,-0.339]],"v":[[-218.987,0.375],[-226.98,1.467],[-220.566,3.333]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[100]},{"t":70.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 4","np":3,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":70.166,"s":[{"i":[[3,0],[-0.813,-1.313],[-1.438,0.438]],"o":[[-1.45,0],[0.813,1.313],[1.438,-0.438]],"v":[[-456.438,-2.188],[-460.438,-0.875],[-449.25,0]],"c":true}],"h":1},{"t":71.8333333333333,"s":[{"i":[[1.521,0],[-0.412,-0.665],[-0.729,0.222]],"o":[[-0.735,0],[0.412,0.665],[0.729,-0.222]],"v":[[-455.498,-1.54],[-457.526,-0.875],[-451.854,-0.431]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-350.375,1],"ix":2},"a":{"a":0,"k":[-455.313,-0.938],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":178.598,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":72.666,"s":[100]},{"t":73.5003255208333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 6","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":70.166,"s":[{"i":[[3,0],[-0.813,-1.313],[-1.438,0.438]],"o":[[-1.45,0],[0.813,1.313],[1.438,-0.438]],"v":[[-456.438,-2.188],[-460.438,-0.875],[-449.25,0]],"c":true}],"h":1},{"t":71.8333333333333,"s":[{"i":[[1.521,0],[-0.412,-0.665],[-0.729,0.222]],"o":[[-0.735,0],[0.412,0.665],[0.729,-0.222]],"v":[[-455.498,-1.54],[-457.526,-0.875],[-451.854,-0.431]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-394.5,-0.25],"ix":2},"a":{"a":0,"k":[-455.375,-1.063],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":72.666,"s":[100]},{"t":73.5003255208333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 5","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":65.166,"s":[{"i":[[4.25,-0.125],[4.002,-4.002],[-17.974,-0.972],[-17.25,-1.25],[1,6.25],[9.25,-0.75]],"o":[[-4.25,0.125],[-5.375,5.375],[18.5,1],[12.53,0.908],[-1.644,-10.274],[-9.25,0.75]],"v":[[-453.25,-2.625],[-490.125,-2.375],[-454,3.75],[-409,5.75],[-386.75,-0.75],[-420.5,-5.25]],"c":true}],"h":1},{"t":66.833,"s":[{"i":[[3.669,-0.106],[3.455,-3.4],[-15.518,-0.826],[-11.428,-1.229],[0.458,5.361],[7.986,-0.637]],"o":[[-3.669,0.106],[-4.64,4.567],[15.972,0.85],[10.782,1.159],[-0.637,-7.456],[-7.986,0.637]],"v":[[-440.274,-3.388],[-475.86,-2.425],[-441.922,2.029],[-406.322,3.979],[-386.613,-1.044],[-414.751,-4.618]],"c":true}],"h":1},{"t":68.5003255208333,"s":[{"i":[[2.858,-0.084],[2.774,-2.606],[-7.473,0.19],[-12.303,-2.034],[3.317,2.746],[6.221,-0.504]],"o":[[-2.858,0.084],[-4.23,3.973],[10.762,-0.273],[8.336,1.378],[-3.744,-3.1],[-6.221,0.504]],"v":[[-429.443,-1.916],[-451.964,-2.289],[-431.274,1.139],[-399.548,3.122],[-390.339,-1.364],[-414.612,-1.376]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-384.5,2],"ix":2},"a":{"a":0,"k":[-433,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[100]},{"t":70.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 7","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-121.266,2.382],"ix":2},"a":{"a":0,"k":[-383.766,1.382],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.726,-0.489],[0.33,0.9],[1.837,-0.574]],"o":[[1.667,0.219],[-0.408,-1.112],[-4.387,1.372]],"v":[[-416.513,1.844],[-414.744,0.325],[-418.558,-1.419]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":67.666,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":68.5,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[100]},{"t":70.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 10","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":70.166,"s":[{"i":[[3,0],[-0.813,-1.313],[-1.438,0.438]],"o":[[-1.45,0],[0.813,1.313],[1.438,-0.438]],"v":[[-456.438,-2.188],[-460.438,-0.875],[-449.25,0]],"c":true}],"h":1},{"t":71.8333333333333,"s":[{"i":[[1.521,0],[-0.412,-0.665],[-0.729,0.222]],"o":[[-0.735,0],[0.412,0.665],[0.729,-0.222]],"v":[[-455.498,-1.54],[-457.526,-0.875],[-451.854,-0.431]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-350.375,1],"ix":2},"a":{"a":0,"k":[-455.313,-0.938],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":178.598,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":72.666,"s":[100]},{"t":73.5003255208333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 9","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":70.166,"s":[{"i":[[3,0],[-0.813,-1.313],[-1.438,0.438]],"o":[[-1.45,0],[0.813,1.313],[1.438,-0.438]],"v":[[-456.438,-2.188],[-460.438,-0.875],[-449.25,0]],"c":true}],"h":1},{"t":71.8333333333333,"s":[{"i":[[1.521,0],[-0.412,-0.665],[-0.729,0.222]],"o":[[-0.735,0],[0.412,0.665],[0.729,-0.222]],"v":[[-455.498,-1.54],[-457.526,-0.875],[-451.854,-0.431]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-394.5,-0.25],"ix":2},"a":{"a":0,"k":[-455.375,-1.063],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":72.666,"s":[100]},{"t":73.5003255208333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 8","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":65.166,"s":[{"i":[[4.25,-0.125],[3.792,-4.202],[-17.974,-0.972],[-17.25,-1.25],[8.5,6],[9.25,-0.75]],"o":[[-4.25,0.125],[-4.625,5.125],[18.5,1],[12.53,0.908],[-8.5,-6],[-9.25,0.75]],"v":[[-446.75,-3.875],[-485,-1.25],[-449,6.75],[-397.75,7],[-388.25,-3],[-424,-4.5]],"c":true}],"h":1},{"t":66,"s":[{"i":[[4.25,-0.125],[3.792,-4.202],[-17.974,-0.972],[-17.25,-1.25],[8.5,6],[9.25,-0.75]],"o":[[-4.25,0.125],[-4.625,5.125],[18.5,1],[12.53,0.908],[-8.5,-6],[-9.25,0.75]],"v":[[-446.75,-3.875],[-487.5,-1],[-449,6.75],[-397.75,7],[-388.25,-3],[-424,-4.5]],"c":true}],"h":1},{"t":66.833,"s":[{"i":[[3.672,-0.108],[3.564,-3.348],[-15.551,0.11],[-15.606,-1.602],[4.32,3.456],[7.992,-0.648]],"o":[[-3.672,0.108],[-5.433,5.104],[14.923,-0.106],[10.797,1.108],[-7.019,-5.615],[-7.992,0.648]],"v":[[-437.551,-2.315],[-474.355,-2.294],[-438.896,3.136],[-399.435,4.442],[-387.32,-1.606],[-419.071,-3.334]],"c":true}],"h":1},{"t":68.5003255208333,"s":[{"i":[[2.858,-0.084],[0.597,-3.759],[-7.473,0.19],[-12.303,-2.034],[3.317,2.746],[6.221,-0.504]],"o":[[-2.858,0.084],[-0.661,4.164],[10.762,-0.273],[8.336,1.378],[-3.744,-3.1],[-6.221,0.504]],"v":[[-429.443,-1.916],[-454.339,-1.539],[-431.274,1.139],[-399.548,3.122],[-390.339,-1.364],[-414.612,-1.376]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[48.5,1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[100]},{"t":70.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 4","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-362.658,1.059],"ix":2},"a":{"a":0,"k":[-372.408,1.059],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":65.166,"s":[{"i":[[3,-0.125],[-0.187,-2.893],[-4.75,-0.25],[1.25,2.75]],"o":[[-3,0.125],[0.25,3.875],[4.75,0.25],[-1.25,-2.75]],"v":[[-502.625,-5.5],[-511.25,-0.375],[-502.375,5.5],[-493.5,-0.5]],"c":true}],"h":1},{"t":66.833,"s":[{"i":[[1.964,-0.082],[-0.122,-1.894],[-3.11,-0.164],[0.818,1.8]],"o":[[-1.964,0.082],[0.164,2.537],[3.11,0.164],[-0.818,-1.8]],"v":[[-502.355,-3.579],[-508.002,-0.224],[-502.191,3.622],[-496.381,-0.306]],"c":true}],"h":1},{"t":68.5003255208333,"s":[{"i":[[0.928,-0.039],[-0.058,-0.895],[-1.47,-0.077],[0.387,0.851]],"o":[[-0.928,0.039],[0.077,1.199],[1.47,0.077],[-0.387,-0.851]],"v":[[-502.085,-1.659],[-504.754,-0.073],[-502.008,1.745],[-499.262,-0.112]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[46,1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.333,"s":[100]},{"t":70.1663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 3","np":3,"cix":2,"bm":0,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":65.166,"s":[{"i":[[2.299,0],[-0.143,-2.356],[-2.244,0],[0.115,1.897]],"o":[[-2.36,0],[0.136,2.24],[1.9,0],[-0.139,-2.295]],"v":[[-527.5,-4],[-532.75,0.75],[-527,4.25],[-522.5,0.75]],"c":true}],"h":1},{"t":66.8333333333333,"s":[{"i":[[0.826,0],[-0.051,-0.847],[-0.807,0],[0.041,0.682]],"o":[[-0.848,0],[0.049,0.805],[0.683,0],[-0.05,-0.825]],"v":[[-527.589,-1.358],[-529.477,0.35],[-527.409,1.608],[-525.791,0.35]],"c":true}],"h":1}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.333,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65.166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":67.666,"s":[100]},{"t":68.5003255208333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"t":56.833,"s":[{"i":[[20,0],[1,-19],[-18.998,-0.576],[0.5,25]],"o":[[-17.007,0],[-1,19],[16.5,0.5],[-0.5,-25]],"v":[[-833,-32.5],[-865,0.5],[-830,34.5],[-796.5,0.5]],"c":true}],"h":1},{"t":58.5,"s":[{"i":[[20,0],[1,-19],[-18.998,-0.576],[0.5,25]],"o":[[-17.007,0],[-1,19],[16.5,0.5],[-0.5,-25]],"v":[[-717,-32.5],[-865,0.5],[-714,33],[-668,0.5]],"c":true}],"h":1},{"t":60.166,"s":[{"i":[[20,0],[1,-19],[-18.998,-0.462],[0.5,25]],"o":[[-17.007,0],[-1,19],[16.5,0.402],[-0.5,-25]],"v":[[149,-27],[-862,0],[152,25.598],[198,-0.5]],"c":true}],"h":1},{"t":61.833,"s":[{"i":[[20,0],[1,-19],[-18.998,-0.349],[-0.45,18.001]],"o":[[-17.007,0],[-1,19],[16.5,0.303],[0.5,-20]],"v":[[559,-20.5],[-798,0],[562,19.197],[608,-0.5]],"c":true}],"h":1},{"t":63.5,"s":[{"i":[[73,0.658],[30.5,-6.5],[-19.002,-0.276],[0.5,25]],"o":[[-17.006,-0.153],[58,16.5],[68,0.987],[-0.5,-25]],"v":[[673,-22.08],[-589.5,0],[676,21.013],[776,0]],"c":true}],"h":1},{"t":65.166,"s":[{"i":[[19.987,-0.708],[221,-12],[-229,-13],[0.5,25]],"o":[[-127,4.5],[114,15],[16.481,0.936],[-0.5,-25]],"v":[[797,-26.5],[282,0],[797,26.5],[846,-0.5]],"c":true}],"h":1},{"t":66.833,"s":[{"i":[[20,0],[1,-19],[-18.998,-0.576],[0.5,25]],"o":[[-17.007,0],[-1,19],[16.5,0.5],[-0.5,-25]],"v":[[819,-33.5],[722,0],[822,32],[868,-0.5]],"c":true}],"h":1},{"t":68.5,"s":[{"i":[[18.242,0.223],[-0.24,-17.143],[-18.057,-0.023],[0.516,15.75]],"o":[[-17.559,-0.215],[0.217,15.55],[17.043,0.022],[-0.664,-20.262]],"v":[[852.508,-30.223],[821.533,-0.05],[854.057,28.773],[883.484,-0.5]],"c":true}],"h":1},{"t":70.166,"s":[{"i":[[14.169,0.174],[-0.186,-13.316],[-14.026,-0.018],[0.401,12.234]],"o":[[-13.639,-0.167],[0.169,12.078],[13.238,0.017],[-0.516,-15.738]],"v":[[871.289,-23.659],[847.229,-0.222],[872.492,22.166],[895.349,-0.572]],"c":true}],"h":1},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":71.833,"s":[{"i":[[11.26,0.138],[-0.148,-10.582],[-11.146,-0.014],[0.319,9.722]],"o":[[-10.839,-0.133],[0.134,9.598],[10.52,0.013],[-0.41,-12.507]],"v":[[882.561,-18.97],[863.441,-0.345],[883.517,17.446],[901.681,-0.623]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":73.5,"s":[{"i":[[8.06,0.099],[-0.106,-7.575],[-7.978,-0.01],[0.228,6.959]],"o":[[-7.758,-0.095],[0.096,6.871],[7.53,0.01],[-0.293,-8.953]],"v":[[891.585,-13.813],[877.899,-0.481],[892.27,12.255],[905.272,-0.68]],"c":true}]},{"t":76.8333333333333,"s":[{"i":[[2.64,0.056],[-0.035,-4.335],[-2.613,-0.006],[0.075,3.982]],"o":[[-2.541,-0.054],[0.031,3.932],[2.466,0.005],[-0.096,-5.123]],"v":[[907.017,-8.256],[902.535,-0.627],[907.241,6.661],[911.499,-0.74]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":56.833,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":76.833,"s":[100]},{"t":77.6663411458333,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":16,"mn":"ADBE Vector Group","hd":false}],"ip":61,"op":81.8333333333333,"st":56.8333333333333,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 13","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[779.346,392,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[43.7,48,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Turbulent Displace","np":16,"mn":"ADBE Turbulent Displace","ix":1,"en":1,"ef":[{"ty":7,"nm":"Displacement","mn":"ADBE Turbulent Displace-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":0,"nm":"Amount","mn":"ADBE Turbulent Displace-0002","ix":2,"v":{"a":0,"k":33,"ix":2}},{"ty":0,"nm":"Size","mn":"ADBE Turbulent Displace-0003","ix":3,"v":{"a":0,"k":90,"ix":3}},{"ty":3,"nm":"Offset (Turbulence)","mn":"ADBE Turbulent Displace-0004","ix":4,"v":{"a":0,"k":[960,540],"ix":4}},{"ty":0,"nm":"Complexity","mn":"ADBE Turbulent Displace-0005","ix":5,"v":{"a":0,"k":1,"ix":5}},{"ty":0,"nm":"Evolution","mn":"ADBE Turbulent Displace-0006","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":6,"nm":"Evolution Options","mn":"ADBE Turbulent Displace-0007","ix":7,"v":0},{"ty":7,"nm":"Cycle Evolution","mn":"ADBE Turbulent Displace-0008","ix":8,"v":{"a":0,"k":0,"ix":8}},{"ty":0,"nm":"Cycle (in Revolutions)","mn":"ADBE Turbulent Displace-0009","ix":9,"v":{"a":0,"k":1,"ix":9}},{"ty":0,"nm":"Random Seed","mn":"ADBE Turbulent Displace-0010","ix":10,"v":{"a":0,"k":0,"ix":10}},{"ty":6,"nm":"Random Seed","mn":"ADBE Turbulent Displace-0011","ix":11,"v":0},{"ty":7,"nm":"Pinning","mn":"ADBE Turbulent Displace-0012","ix":12,"v":{"a":0,"k":3,"ix":12}},{"ty":7,"nm":"Resize Layer","mn":"ADBE Turbulent Displace-0013","ix":13,"v":{"a":0,"k":0,"ix":13}},{"ty":7,"nm":"Antialiasing for Best Quality","mn":"ADBE Turbulent Displace-0014","ix":14,"v":{"a":0,"k":1,"ix":14}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":56.833,"s":[{"i":[[11.322,0],[0.345,-0.017],[0,-10.973],[-11.159,-0.189],[-0.118,0],[0,11.322]],"o":[[-0.349,0],[-10.838,0.542],[0,11.204],[0.118,0.002],[11.322,0],[0,-11.322]],"v":[[1.25,-21.25],[0.208,-21.224],[-19.25,-0.75],[0.896,19.747],[1.25,19.75],[21.75,-0.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":57.666,"s":[{"i":[[16.482,-0.109],[0.514,-0.021],[0.053,-10.972],[-16.369,-0.081],[-0.172,0.001],[-0.054,11.321]],"o":[[-0.508,0.003],[-16.137,0.649],[-0.054,11.203],[0.173,0.001],[16.482,-0.109],[0.054,-11.321]],"v":[[16.806,-21.487],[15.273,-21.451],[-19.303,-0.751],[16.093,19.51],[16.61,19.51],[40.383,-1.145]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":58.5,"s":[{"i":[[15.609,-0.124],[1.007,-0.027],[0.06,-10.971],[-21.26,-0.005],[-0.163,0.001],[-0.062,11.321]],"o":[[-0.482,0.004],[-31.615,0.837],[-0.061,11.203],[0.224,0],[15.609,-0.124],[0.062,-11.321]],"v":[[294.52,-15.545],[292.279,-15.499],[-19.31,-0.751],[293.715,25.453],[294.296,25.452],[316.829,4.775]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":59.333,"s":[{"i":[[22.713,0],[1.996,-0.02],[0,-10.973],[-36.822,-0.163],[-0.237,0],[0,11.322]],"o":[[-0.701,0],[-62.69,0.615],[0,11.204],[0.388,0.002],[22.713,0],[0,-11.322]],"v":[[732.113,-7.432],[728.047,-7.403],[-19.25,-0.75],[731.174,33.565],[732.113,33.568],[764.738,13.068]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60.166,"s":[{"i":[[22.713,0],[2.143,-0.019],[0,-10.973],[-38.457,-0.168],[-0.237,0],[0,11.322]],"o":[[-0.701,0],[-67.327,0.6],[0,11.204],[0.405,0.002],[22.713,0],[0,-11.322]],"v":[[878.053,-4.043],[873.763,-4.014],[-4.25,-1.5],[877.088,36.954],[878.053,36.957],[910.678,16.457]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61,"s":[{"i":[[13.629,-4.561],[86.652,-5.975],[0,-10.358],[-53.446,1.16],[-22.24,8.602],[2.444,23.969]],"o":[[-9.862,3.3],[-80.243,5.533],[0,10.857],[46.354,-1.006],[18.302,-7.078],[-1.149,-11.263]],"v":[[1074.284,-36.077],[963.177,-3.705],[190.576,4.925],[941.208,34.747],[1057.908,19.478],[1102.693,-22.181]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61.833,"s":[{"i":[[25.734,-14.458],[57.535,0.433],[0,-10.358],[-53.458,-0.289],[-28.449,20.495],[16.742,10.948]],"o":[[-45.444,25.531],[-80.43,-0.606],[0,10.857],[97.834,0.528],[20.653,-14.879],[-26.837,-17.549]],"v":[[1072.672,-29.604],[937.627,-1.065],[432.522,10.148],[956.717,34.381],[1120.036,-15.944],[1150.156,-76.861]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62.666,"s":[{"i":[[23.815,-17.439],[60.087,-2.523],[0,-10.358],[-53.395,2.605],[-27.04,22.321],[16.742,10.948]],"o":[[-61.791,45.249],[-80.362,3.374],[0,10.857],[115.945,-5.656],[30.862,-25.477],[-26.837,-17.549]],"v":[[1107.611,-54.498],[937.531,4.434],[656.485,14.205],[956.813,28.882],[1139.838,-33.101],[1191.46,-122.647]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63.5,"s":[{"i":[[18.893,-22.679],[60.087,-2.523],[-0.009,-7.129],[-53.457,0.418],[-28.313,20.683],[27.034,12.374]],"o":[[-49.538,59.465],[-80.362,3.374],[0.014,10.931],[106.359,-0.832],[43.474,-31.758],[-19.486,-8.919]],"v":[[1132.449,-73.568],[950.977,7.678],[819.989,15.441],[962.338,27.487],[1135.304,-31.18],[1210.331,-172.326]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":64.333,"s":[{"i":[[11.856,-27.032],[56.914,-19.432],[-1.357,-6.999],[-58.312,21.909],[-22.064,27.25],[29.714,1.04]],"o":[[-31.29,71.344],[-44.492,15.191],[2.081,10.731],[33.919,-12.744],[45.923,-56.717],[-24.377,-0.853]],"v":[[1179.204,-142.321],[1032.438,-3.447],[944.982,17.633],[1072.983,5.804],[1169.801,-60.084],[1218.771,-211.834]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65.166,"s":[{"i":[[14.53,-35.892],[30.675,-23.851],[5.099,-4.983],[-50.58,36.358],[-16.698,30.832],[29.683,1.694]],"o":[[-19.208,47.446],[-37.115,28.858],[-11.962,11.69],[28.617,-20.57],[23.405,-43.217],[-25.543,-1.458]],"v":[[1178.34,-146.817],[1108.748,-47.775],[1041.305,6.278],[1128.594,-38.743],[1209.941,-137.785],[1216.557,-246.794]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66,"s":[{"i":[[6.463,-38.179],[26.105,-35.723],[3.462,-6.232],[-38.476,48.988],[-6.091,22.999],[28.929,-6.861]],"o":[[-2.665,15.742],[-27.739,37.959],[-8.122,14.621],[16.705,-21.269],[12.583,-47.51],[-24.895,5.904]],"v":[[1195.756,-182.445],[1160.966,-93.998],[1108.528,-35.694],[1176.987,-93.916],[1220.9,-173.321],[1205.693,-274.264]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66.833,"s":[{"i":[[-3.967,-38.518],[14.738,-41.718],[2.805,-6.554],[-17.135,35.515],[3.642,44.481],[5.311,-9.959]],"o":[[1.636,15.882],[-10.128,28.668],[-4.058,9.483],[11.752,-24.358],[-4.01,-48.984],[-7.603,14.257]],"v":[[1203.101,-232.396],[1185.56,-129.169],[1153.419,-77.434],[1199.41,-126.896],[1227.572,-246.524],[1191.203,-296.143]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":67.666,"s":[{"i":[[-6.517,-18.867],[6.536,-33.775],[1.403,-5.363],[-9.315,43.217],[8.841,20.883],[2.939,-8.269]],"o":[[4.053,11.734],[-4.492,23.21],[-2.03,7.758],[4.43,-20.556],[-14.898,-35.19],[-4.207,11.838]],"v":[[1193.273,-271.965],[1203.312,-170.863],[1184.545,-127.384],[1220.176,-196.608],[1217.473,-286.022],[1181.171,-312.54]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.5,"s":[{"i":[[-9.304,-14.906],[-0.873,-44.41],[0.562,-4.797],[-0.324,53.603],[11.725,14.991],[1.63,-7.53]],"o":[[5.786,9.27],[0.403,20.504],[-0.813,6.94],[0.11,-18.265],[-20.528,-26.245],[-2.334,10.78]],"v":[[1182.667,-295.219],[1206.787,-226.586],[1201.598,-166.302],[1220.883,-246.831],[1203.141,-311.834],[1169.962,-323.009]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":69.333,"s":[{"i":[[-9.304,-14.906],[-6.738,-23.509],[0.562,-4.797],[3.138,27.16],[11.278,15.331],[4.942,-5.911]],"o":[[5.786,9.27],[5.651,19.714],[-0.813,6.94],[-2.096,-18.145],[-13.204,-17.949],[-5.257,6.287]],"v":[[1174.843,-305.357],[1208.609,-245.057],[1211.646,-197.632],[1218.662,-262.872],[1195.807,-321.463],[1166.646,-333.568]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":70.166,"s":[{"i":[[-11.178,-13.557],[-7.335,-18.018],[0.562,-4.797],[7.715,22.739],[11.278,15.331],[4.942,-5.911]],"o":[[9.466,11.481],[7.732,18.994],[-0.813,6.94],[-5.869,-17.297],[-13.204,-17.949],[-5.257,6.287]],"v":[[1164.993,-314.03],[1200.968,-265.693],[1214.144,-226.092],[1209.987,-281.526],[1186.957,-330.119],[1157.769,-340.724]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":71,"s":[{"i":[[-5.68,-5.275],[-6.615,-5.699],[-5.996,-9.279],[4.449,11.328],[8.129,7.475],[4.963,-6.237]],"o":[[9.337,8.672],[7.612,6.558],[4.077,-17.869],[-3.896,-9.921],[-10.024,-9.217],[-4.807,6.041]],"v":[[1156.32,-325.652],[1174.022,-308.569],[1196.611,-282.346],[1192.558,-321.685],[1176.913,-343.998],[1153.575,-346.545]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":71.833,"s":[{"i":[[-4.618,-4.412],[-5.383,-4.772],[-4.833,-7.713],[3.536,9.381],[6.61,6.253],[4.158,-5.076]],"o":[[7.591,7.253],[6.195,5.492],[3.56,-14.665],[-3.097,-8.217],[-8.152,-7.711],[-4.028,4.917]],"v":[[1150.562,-334.187],[1164.948,-319.907],[1183.255,-298.042],[1180.363,-330.484],[1167.73,-349.046],[1148.538,-351.424]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":72.666,"s":[{"i":[[-4.207,-3.771],[-4.886,-4.078],[-4.552,-6.589],[3.511,8.013],[6.019,5.343],[3.352,-4.333]],"o":[[6.916,6.198],[5.623,4.693],[2.372,-12.522],[-3.075,-7.019],[-7.422,-6.589],[-3.247,4.197]],"v":[[1147.89,-337.464],[1161.022,-325.261],[1177.911,-306.579],[1173.845,-334.286],[1162.023,-350.145],[1145.308,-352.186]],"c":true}]},{"t":73.5003255208333,"s":[{"i":[[-3.787,-3.137],[-4.403,-3.357],[-4.057,-5.786],[3.081,7.256],[5.419,4.439],[3.136,-4.492]],"o":[[6.225,5.157],[5.067,3.863],[2.373,-12.188],[-2.698,-6.355],[-6.682,-5.475],[-3.038,4.351]],"v":[[1146.958,-339.941],[1158.771,-329.735],[1173.915,-313.763],[1170.646,-339.723],[1160.09,-353.554],[1144.835,-353.689]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-721,85],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-1,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66.833,"s":[{"i":[[-1.848,-1.875],[-1.957,2.875],[-0.435,6],[5,-6]],"o":[[1.848,1.875],[1.957,-2.875],[0.435,-6],[-5,6]],"v":[[404.456,17.75],[410.543,14.375],[419.348,4.625],[410.869,6.875]],"c":true}]},{"t":71.0003255208333,"s":[{"i":[[-0.708,-0.483],[-0.42,0.573],[0.389,0.655],[0.499,-1.177]],"o":[[1.05,0.716],[0.992,-1.354],[-0.735,-1.237],[-0.242,0.571]],"v":[[433.253,-14.046],[435.614,-14.589],[436.296,-17.576],[433.331,-16.399]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":66,"s":[0],"h":1},{"t":66.833,"s":[100],"h":1},{"t":71.8333333333333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 11","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66,"s":[{"i":[[5.332,-4.406],[10.05,-7.572],[1.148,-0.821],[-8.315,4.877],[-3.93,2.126]],"o":[[-3.121,2.579],[-3.833,2.888],[2.727,2.148],[6.731,-3.948],[8.785,-4.751]],"v":[[321.037,36.094],[290.353,57.89],[284.11,64.219],[302.203,53.915],[319.584,46.207]],"c":true}]},{"t":71.0003255208333,"s":[{"i":[[1.425,0.375],[0.516,-1.431],[0.049,-0.343],[-0.554,-0.143],[-0.279,0.334]],"o":[[-1.228,-0.324],[-0.141,0.391],[-0.06,0.416],[0.573,0.148],[0.944,-1.131]],"v":[[333.77,22.361],[330.951,24.963],[330.662,26.083],[331.622,26.966],[333.142,26.736]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[331.087,62.625],"ix":2},"a":{"a":0,"k":[283.928,58.982],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-0.198,"ix":6},"o":{"a":1,"k":[{"t":65.166,"s":[1],"h":1},{"t":66,"s":[100],"h":1},{"t":71.8333333333333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 10","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65.166,"s":[{"i":[[6.001,-3.438],[4.622,-1.369],[-9.463,3.193]],"o":[[-4.853,2.78],[-6.364,1.884],[9.463,-3.193]],"v":[[287.03,54.981],[272.713,60.955],[289.158,61.059]],"c":true}]},{"t":68.5003255208333,"s":[{"i":[[1.379,-0.683],[-0.18,-1.043],[-1.179,0.646]],"o":[[-1.734,0.859],[0.188,1.086],[1.764,-0.966]],"v":[[307.257,48.925],[305.837,51.696],[308.931,52.026]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[293.913,77.25],"ix":2},"a":{"a":0,"k":[283.928,58.982],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-0.198,"ix":6},"o":{"a":1,"k":[{"t":64.333,"s":[1],"h":1},{"t":65.166,"s":[100],"h":1},{"t":69.3333333333333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 9","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65.166,"s":[{"i":[[2.875,-0.125],[-8.035,-2.12],[-6.725,1.978]],"o":[[-2.875,0.125],[9,2.375],[8.5,-2.5]],"v":[[239.75,63.5],[218.75,70.625],[240.375,70]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66,"s":[{"i":[[2.876,0.091],[-3.613,-2.264],[-3,1.66]],"o":[[-2.876,-0.091],[4.677,2.93],[7.752,-4.291]],"v":[[248.185,61.363],[234.385,66.845],[249.523,68.399]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66.833,"s":[{"i":[[2.781,-0.74],[-1.488,-1.369],[-1.507,2.224]],"o":[[-2.781,0.74],[2.644,2.433],[1.864,-2.752]],"v":[[250.348,60.068],[244.863,65.494],[254.886,64.877]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":67.666,"s":[{"i":[[1.649,-0.439],[-0.882,-0.811],[-0.893,1.319]],"o":[[-1.649,0.439],[1.568,1.442],[1.105,-1.632]],"v":[[252.991,59.068],[251.221,62.433],[255.681,61.919]],"c":true}]},{"t":68.5003255208333,"s":[{"i":[[1.256,-0.334],[-0.672,-0.618],[-0.68,1.004]],"o":[[-1.256,0.334],[1.194,1.098],[0.842,-1.243]],"v":[[254.502,58.588],[253.154,61.152],[256.551,60.76]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[17.826,16],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":64.333,"s":[0],"h":1},{"t":65.166,"s":[100],"h":1},{"t":69.3333333333333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 8","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":64.333,"s":[{"i":[[0,0],[6.25,-5.25],[-8,2.5],[8.5,2.5]],"o":[[0,0],[-6.25,5.25],[8,-2.5],[-8.5,-2.5]],"v":[[101,65.25],[80.75,66.75],[111.75,74.25],[125.5,66.25]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65.166,"s":[{"i":[[0,0],[-3.699,-2.367],[-3.884,2.019],[4.127,2.019]],"o":[[0,0],[2.367,1.514],[3.884,-2.019],[-4.127,-2.019]],"v":[[118.172,64.837],[113.256,70.894],[123.39,72.106],[130.066,65.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66,"s":[{"i":[[0,0],[-1.235,-2.726],[-2.801,1.026],[1.549,1.158]],"o":[[0,0],[0.71,1.566],[2.801,-1.026],[-2.508,-1.874]],"v":[[126.435,64.519],[123.517,68.184],[130.262,69.992],[132.951,65.48]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66.833,"s":[{"i":[[0,0],[-0.755,-1.666],[-1.712,0.627],[0.947,0.708]],"o":[[0,0],[0.434,0.957],[1.712,-0.627],[-1.533,-1.145]],"v":[[129.695,64.355],[127.912,66.595],[132.034,67.7],[133.678,64.942]],"c":true}]},{"t":67.6663411458333,"s":[{"i":[[0,0],[-0.438,-0.966],[-0.993,0.364],[0.549,0.41]],"o":[[0,0],[0.251,0.555],[0.993,-0.364],[-0.889,-0.664]],"v":[[131.885,64.447],[130.852,65.746],[133.242,66.386],[134.195,64.788]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[55.12,18.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":63.5,"s":[0],"h":1},{"t":64.333,"s":[100],"h":1},{"t":68.5003255208333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 7","np":3,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63.5,"s":[{"i":[[0,0],[6.25,-5.25],[-8,2.5],[8.5,2.5]],"o":[[0,0],[-6.25,5.25],[8,-2.5],[-8.5,-2.5]],"v":[[101,65.25],[80.75,66.75],[111.75,74.25],[125.5,66.25]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":64.333,"s":[{"i":[[0,0],[-3.699,-2.367],[-3.884,2.019],[4.127,2.019]],"o":[[0,0],[2.367,1.514],[3.884,-2.019],[-4.127,-2.019]],"v":[[118.172,64.837],[113.256,70.894],[123.391,72.106],[130.066,65.644]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65.166,"s":[{"i":[[0,0],[-1.235,-2.726],[-2.801,1.026],[1.549,1.158]],"o":[[0,0],[0.71,1.566],[2.801,-1.026],[-2.508,-1.874]],"v":[[125.685,65.769],[122.767,69.434],[129.512,71.242],[132.201,66.73]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66,"s":[{"i":[[0,0],[-0.755,-1.666],[-1.712,0.627],[0.947,0.708]],"o":[[0,0],[0.434,0.957],[1.712,-0.627],[-1.533,-1.145]],"v":[[129.445,66.855],[127.662,69.095],[131.784,70.2],[133.428,67.442]],"c":true}]},{"t":66.8333333333333,"s":[{"i":[[0,0],[-0.438,-0.966],[-0.993,0.364],[0.549,0.41]],"o":[[0,0],[0.251,0.555],[0.993,-0.364],[-0.889,-0.664]],"v":[[132.073,67.572],[131.039,68.871],[133.429,69.511],[134.382,67.913]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-31.304,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":62.666,"s":[0],"h":1},{"t":63.5,"s":[100],"h":1},{"t":67.6663411458333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 6","np":3,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":64.333,"s":[{"i":[[4.652,3.866],[0.317,-3.123],[-4.812,-0.938]],"o":[[-4.652,-3.866],[-0.317,3.123],[4.812,0.938]],"v":[[-513.42,73.625],[-538.679,74.835],[-515.78,80.171]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65.166,"s":[{"i":[[2.816,2.132],[0.154,-2.732],[-2.47,0.079]],"o":[[-3.241,-2.454],[-0.154,2.732],[2.974,-0.095]],"v":[[-511.191,74.099],[-521.346,75.103],[-512.006,79.608]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66,"s":[{"i":[[1.529,1.464],[0.083,-1.876],[-1.341,0.054]],"o":[[-1.759,-1.685],[-0.083,1.876],[1.615,-0.065]],"v":[[-509.154,74.587],[-514.667,75.277],[-511.012,78.358]],"c":true}]},{"t":66.8333333333333,"s":[{"i":[[1.046,1.002],[0.057,-1.284],[-0.917,0.037]],"o":[[-1.204,-1.153],[-0.057,1.284],[1.105,-0.045]],"v":[[-508.046,74.925],[-511.818,75.397],[-509.318,77.505]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[126.25,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":63.5,"s":[0],"h":1},{"t":64.333,"s":[100],"h":1},{"t":67.6663411458333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 8","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":64.333,"s":[{"i":[[3.694,2.428],[-0.025,-2.294],[-3.572,-0.288]],"o":[[-3.694,-2.428],[0.025,2.294],[3.572,0.288]],"v":[[-567.944,74.515],[-577.502,78.021],[-570.185,82.462]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65.166,"s":[{"i":[[2.581,1.696],[-0.018,-1.603],[-2.496,-0.201]],"o":[[-2.581,-1.696],[0.018,1.603],[2.496,0.201]],"v":[[-564.081,75.39],[-570.76,77.839],[-565.647,80.943]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66,"s":[{"i":[[1.886,1.239],[-0.013,-1.171],[-1.823,-0.147]],"o":[[-1.886,-1.239],[0.013,1.171],[1.823,0.147]],"v":[[-562.761,75.937],[-567.64,77.726],[-563.905,79.993]],"c":true}]},{"t":66.8333333333333,"s":[{"i":[[1.329,0.736],[-0.009,-0.696],[-1.285,-0.087]],"o":[[-1.329,-0.736],[0.009,0.696],[1.285,0.087]],"v":[[-559.329,76.538],[-562.768,77.601],[-560.136,78.949]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[126.25,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":63.5,"s":[0],"h":1},{"t":64.333,"s":[100],"h":1},{"t":67.6663411458333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.908,0.183],[-0.035,-4.015],[-4.965,-0.504],[-10.464,0.985],[-4.133,0.503],[-0.107,1.024],[13.717,0.931]],"o":[[-6.653,-0.42],[0.035,4.015],[1.388,0.141],[5.467,-0.515],[4.596,-0.559],[0.152,-1.445],[-10.969,-0.745]],"v":[[-566.295,72.587],[-589.833,78.972],[-559.661,83.746],[-536.761,81.379],[-521.818,79.944],[-514.015,77.696],[-533.468,70.031]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[126.25,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":62.666,"s":[0],"h":1},{"t":63.5,"s":[100],"h":1},{"t":64.3333333333333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-4.701,82.841],"ix":2},"a":{"a":0,"k":[-424.093,76.841],"ix":1},"s":{"a":0,"k":[100,-77.71],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":3,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63.5,"s":[{"i":[[4.652,3.866],[0.317,-3.123],[-4.812,-0.938]],"o":[[-4.652,-3.866],[-0.317,3.123],[4.812,0.938]],"v":[[-513.42,73.625],[-538.679,74.835],[-515.78,80.171]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":64.333,"s":[{"i":[[2.816,2.132],[0.154,-2.732],[-2.47,0.079]],"o":[[-3.241,-2.454],[-0.154,2.732],[2.974,-0.095]],"v":[[-511.191,74.099],[-521.346,75.103],[-512.006,79.608]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65.166,"s":[{"i":[[1.529,1.464],[0.083,-1.876],[-1.341,0.054]],"o":[[-1.759,-1.685],[-0.083,1.876],[1.615,-0.065]],"v":[[-509.154,74.587],[-514.667,75.277],[-511.012,78.358]],"c":true}]},{"t":66.0003255208333,"s":[{"i":[[1.046,1.002],[0.057,-1.284],[-0.917,0.037]],"o":[[-1.204,-1.153],[-0.057,1.284],[1.105,-0.045]],"v":[[-508.046,74.925],[-511.818,75.397],[-509.318,77.505]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[126.25,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":62.666,"s":[0],"h":1},{"t":63.5,"s":[100],"h":1},{"t":66.8333333333333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 8","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63.5,"s":[{"i":[[3.694,2.428],[-0.025,-2.294],[-3.572,-0.288]],"o":[[-3.694,-2.428],[0.025,2.294],[3.572,0.288]],"v":[[-567.944,74.515],[-577.502,78.021],[-570.185,82.462]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":64.333,"s":[{"i":[[2.581,1.696],[-0.018,-1.603],[-2.496,-0.201]],"o":[[-2.581,-1.696],[0.018,1.603],[2.496,0.201]],"v":[[-564.081,75.39],[-570.76,77.839],[-565.647,80.943]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65.166,"s":[{"i":[[1.886,1.239],[-0.013,-1.171],[-1.823,-0.147]],"o":[[-1.886,-1.239],[0.013,1.171],[1.823,0.147]],"v":[[-562.761,75.937],[-567.64,77.726],[-563.905,79.993]],"c":true}]},{"t":66.0003255208333,"s":[{"i":[[1.329,0.736],[-0.009,-0.696],[-1.285,-0.087]],"o":[[-1.329,-0.736],[0.009,0.696],[1.285,0.087]],"v":[[-559.329,76.538],[-562.768,77.601],[-560.136,78.949]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[126.25,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":62.666,"s":[0],"h":1},{"t":63.5,"s":[100],"h":1},{"t":66.8333333333333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.908,0.183],[-0.035,-4.015],[-4.965,-0.504],[-10.464,0.985],[-4.133,0.503],[-0.107,1.024],[13.717,0.931]],"o":[[-6.653,-0.42],[0.035,4.015],[1.388,0.141],[5.467,-0.515],[4.596,-0.559],[0.152,-1.445],[-10.969,-0.745]],"v":[[-566.295,72.587],[-589.833,78.972],[-559.661,83.746],[-536.761,81.379],[-521.818,79.944],[-514.015,77.696],[-533.468,70.031]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[126.25,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":61.833,"s":[0],"h":1},{"t":62.666,"s":[100],"h":1},{"t":63.5003255208333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-115.701,82.841],"ix":2},"a":{"a":0,"k":[-424.093,76.841],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":3,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61.833,"s":[{"i":[[14.298,-0.054],[0.036,-3.288],[-10.012,-0.052]],"o":[[-10.607,0.04],[-0.043,3.974],[11.703,0.061]],"v":[[-545.673,75.118],[-590.957,80.579],[-545.703,86.128]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62.666,"s":[{"i":[[9.143,-0.038],[0.023,-2.29],[-6.402,-0.036]],"o":[[-6.782,0.028],[-0.028,2.768],[7.483,0.043]],"v":[[-534.518,76.79],[-563.472,80.593],[-534.537,84.457]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63.5,"s":[{"i":[[6.887,-0.028],[0.017,-1.725],[-4.822,-0.027]],"o":[[-5.109,0.021],[-0.021,2.085],[5.637,0.032]],"v":[[-530.637,77.694],[-538.229,80.567],[-530.651,83.469]],"c":true}]},{"t":64.3333333333333,"s":[{"i":[[1.84,-0.014],[0.005,-0.823],[-1.289,-0.013]],"o":[[-1.365,0.01],[-0.006,0.994],[1.506,0.015]],"v":[[-525.59,79.206],[-527.619,80.576],[-525.594,81.96]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[50,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":61,"s":[0],"h":1},{"t":61.833,"s":[100],"h":1},{"t":65.1663411458333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 4","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61.833,"s":[{"i":[[4.869,1.745],[-0.033,-1.648],[-4.708,-0.207]],"o":[[-4.869,-1.745],[0.033,1.648],[4.708,0.207]],"v":[[-621.244,79.457],[-632.592,82.226],[-622.948,85.418]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62.666,"s":[{"i":[[2.36,1.324],[-0.016,-1.251],[-2.281,-0.157]],"o":[[-2.36,-1.324],[0.016,1.251],[2.281,0.157]],"v":[[-618.235,79.99],[-623.734,82.092],[-619.06,84.515]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63.5,"s":[{"i":[[1.686,0.946],[-0.012,-0.894],[-1.629,-0.112]],"o":[[-1.686,-0.946],[0.012,0.894],[1.629,0.112]],"v":[[-615.436,80.47],[-619.363,81.971],[-616.025,83.701]],"c":true}]},{"t":64.3333333333333,"s":[{"i":[[0.974,0.547],[-0.007,-0.516],[-0.941,-0.065]],"o":[[-0.974,-0.547],[0.007,0.516],[0.941,0.065]],"v":[[-613.099,80.976],[-615.368,81.843],[-613.44,82.843]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":61,"s":[0],"h":1},{"t":61.833,"s":[100],"h":1},{"t":65.1663411458333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 3","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61.833,"s":[{"i":[[10.353,2.306],[-0.07,-2.179],[-10.009,-0.274]],"o":[[-10.353,-2.306],[0.07,2.179],[10.009,0.274]],"v":[[-580.173,78.536],[-606.957,81.865],[-586.453,86.084]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62.666,"s":[{"i":[[2.976,1.724],[-0.02,-1.629],[-2.877,-0.205]],"o":[[-2.976,-1.724],[0.02,1.629],[2.877,0.205]],"v":[[-574.601,79.232],[-582.3,81.721],[-576.406,84.876]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63.5,"s":[{"i":[[1.661,0.651],[-0.012,-0.944],[-1.667,-0.119]],"o":[[-1.855,-0.727],[0.012,0.944],[1.667,0.119]],"v":[[-573.599,80.037],[-576.621,81.52],[-574.27,83.369]],"c":true}]},{"t":64.3333333333333,"s":[{"i":[[0.865,0.339],[-0.006,-0.492],[-0.868,-0.062]],"o":[[-0.966,-0.378],[0.006,0.491],[0.868,0.062]],"v":[[-571.428,80.689],[-573.002,81.462],[-571.777,82.425]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":60.166,"s":[0],"h":1},{"t":61,"s":[100],"h":1},{"t":65.1663411458333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-284.543,85.184],"ix":2},"a":{"a":0,"k":[-567.935,80.684],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":3,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61.833,"s":[{"i":[[4.652,3.866],[0.317,-3.123],[-4.812,-0.938]],"o":[[-4.652,-3.866],[-0.317,3.123],[4.812,0.938]],"v":[[-513.42,73.625],[-538.679,74.835],[-515.78,80.171]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62.666,"s":[{"i":[[2.816,2.132],[0.154,-2.732],[-2.47,0.079]],"o":[[-3.241,-2.454],[-0.154,2.732],[2.974,-0.095]],"v":[[-511.191,74.099],[-521.346,75.103],[-512.006,79.608]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63.5,"s":[{"i":[[1.529,1.464],[0.083,-1.876],[-1.341,0.054]],"o":[[-1.759,-1.685],[-0.083,1.876],[1.615,-0.065]],"v":[[-509.154,74.587],[-514.667,75.277],[-511.012,78.358]],"c":true}]},{"t":64.3333333333333,"s":[{"i":[[1.046,1.002],[0.057,-1.284],[-0.917,0.037]],"o":[[-1.204,-1.153],[-0.057,1.284],[1.105,-0.045]],"v":[[-508.046,74.925],[-511.818,75.397],[-509.318,77.505]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[126.25,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":61,"s":[0],"h":1},{"t":61.833,"s":[100],"h":1},{"t":65.1663411458333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 8","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61.833,"s":[{"i":[[3.694,2.428],[-0.025,-2.294],[-3.572,-0.288]],"o":[[-3.694,-2.428],[0.025,2.294],[3.572,0.288]],"v":[[-567.944,74.515],[-577.502,78.021],[-570.185,82.462]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62.666,"s":[{"i":[[2.581,1.696],[-0.018,-1.603],[-2.496,-0.201]],"o":[[-2.581,-1.696],[0.018,1.603],[2.496,0.201]],"v":[[-564.081,75.39],[-570.76,77.839],[-565.647,80.943]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63.5,"s":[{"i":[[1.886,1.239],[-0.013,-1.171],[-1.823,-0.147]],"o":[[-1.886,-1.239],[0.013,1.171],[1.823,0.147]],"v":[[-562.761,75.937],[-567.64,77.726],[-563.905,79.993]],"c":true}]},{"t":64.3333333333333,"s":[{"i":[[1.329,0.736],[-0.009,-0.696],[-1.285,-0.087]],"o":[[-1.329,-0.736],[0.009,0.696],[1.285,0.087]],"v":[[-559.329,76.538],[-562.768,77.601],[-560.136,78.949]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[126.25,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":61,"s":[0],"h":1},{"t":61.833,"s":[100],"h":1},{"t":65.1663411458333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.908,0.183],[-0.035,-4.015],[-4.965,-0.504],[-10.464,0.985],[-4.133,0.503],[-0.107,1.024],[13.717,0.931]],"o":[[-6.653,-0.42],[0.035,4.015],[1.388,0.141],[5.467,-0.515],[4.596,-0.559],[0.152,-1.445],[-10.969,-0.745]],"v":[[-566.295,72.587],[-589.833,78.972],[-559.661,83.746],[-536.761,81.379],[-521.818,79.944],[-514.015,77.696],[-533.468,70.031]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[126.25,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":60.166,"s":[0],"h":1},{"t":61,"s":[100],"h":1},{"t":61.8333333333333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-426.701,87.841],"ix":2},"a":{"a":0,"k":[-424.093,76.841],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61,"s":[{"i":[[14.298,-0.054],[0.036,-3.288],[-10.012,-0.052]],"o":[[-10.607,0.04],[-0.043,3.974],[11.703,0.061]],"v":[[-571.673,75.179],[-590.957,80.579],[-571.703,86.189]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61.833,"s":[{"i":[[7.176,-0.042],[0.018,-2.549],[-5.025,-0.041]],"o":[[-5.323,0.031],[-0.022,3.081],[5.873,0.047]],"v":[[-563.301,77.667],[-572.978,81.853],[-563.316,86.203]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62.666,"s":[{"i":[[3.284,0.186],[0.006,-1.479],[-1.743,-0.063]],"o":[[-1.845,-0.105],[-0.008,1.788],[3.229,0.116]],"v":[[-559.482,79.432],[-562.17,81.888],[-559.487,84.384]],"c":true}]},{"t":63.5003255208333,"s":[{"i":[[2.54,0.144],[0.005,-1.144],[-1.348,-0.048]],"o":[[-1.427,-0.081],[-0.006,1.382],[2.497,0.09]],"v":[[-556.29,79.994],[-558.369,81.894],[-556.294,83.824]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.935,-4.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":60.166,"s":[0],"h":1},{"t":61,"s":[100],"h":1},{"t":64.3333333333333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 4","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61,"s":[{"i":[[-3.416,-2.174],[0.023,2.053],[3.303,0.258]],"o":[[3.416,2.174],[-0.023,-2.053],[-3.303,-0.258]],"v":[[-622.236,85.018],[-613.398,81.88],[-620.164,77.904]],"c":true}]},{"t":61.8333333333333,"s":[{"i":[[2.025,1.288],[-0.014,-1.217],[-1.958,-0.153]],"o":[[-2.025,-1.288],[0.014,1.217],[1.958,0.153]],"v":[[-609.15,80.128],[-614.389,81.988],[-610.378,84.345]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-4.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":60.166,"s":[0],"h":1},{"t":61,"s":[100],"h":1},{"t":62.6663411458333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 3","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61,"s":[{"i":[[10.353,2.306],[-0.07,-2.179],[-10.009,-0.274]],"o":[[-10.353,-2.306],[0.07,2.179],[10.009,0.274]],"v":[[-580.173,78.536],[-606.957,81.865],[-586.453,86.084]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61.833,"s":[{"i":[[2.976,1.724],[-0.02,-1.629],[-2.877,-0.205]],"o":[[-2.976,-1.724],[0.02,1.629],[2.877,0.205]],"v":[[-574.601,79.232],[-582.3,81.721],[-576.406,84.876]],"c":true}]},{"t":62.6663411458333,"s":[{"i":[[1.661,0.651],[-0.012,-0.944],[-1.667,-0.119]],"o":[[-1.855,-0.727],[0.012,0.944],[1.667,0.119]],"v":[[-573.599,80.037],[-576.621,81.52],[-574.27,83.369]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-4.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":60.166,"s":[0],"h":1},{"t":61,"s":[100],"h":1},{"t":63.5003255208333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-570.543,87.684],"ix":2},"a":{"a":0,"k":[-567.935,80.684],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.49,1.863],[0,0],[-7.181,0.153]],"o":[[-7.49,-1.863],[0,0],[7.181,-0.153]],"v":[[-661.49,79.935],[-687.038,82.701],[-668.091,85.619]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.047058868408,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[21.739,1.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"t":60.166,"s":[0],"h":1},{"t":61,"s":[100],"h":1},{"t":61.8333333333333,"s":[0],"h":1}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":13,"mn":"ADBE Vector Group","hd":false}],"ip":61,"op":75.1666666666667,"st":56.8333333333333,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 11","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[960,539.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0,0,0.333],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"t":13,"s":[971,971,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.456,0],[0,-7.456],[-7.456,0],[0,7.456]],"o":[[-7.456,0],[0,7.456],[7.456,0],[0,-7.456]],"v":[[0.75,-10.75],[-12.75,2.75],[0.75,16.25],[14.25,2.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0],"y":[0]},"t":0,"s":[41]},{"t":23.8974609375,"s":[0]}],"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0.5,2.5],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":23,"st":-2,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[960,540,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-29],[1104,-195.999]],"o":[[0,0],[0,770],[0,0.001]],"v":[[-117.997,-146.999],[-118,146],[0,-0.001]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":35,"s":[48]},{"t":56,"s":[-41]}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[0]},{"t":26.09375,"s":[4]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":25,"s":[296]},{"i":{"x":[0],"y":[1]},"o":{"x":[1],"y":[0]},"t":45,"s":[182.4]},{"t":66,"s":[44]}],"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":57.7,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":25,"op":57,"st":25,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[960,540,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-118,1028]],"o":[[0,0],[762,0],[0,-36]],"v":[[-30,117],[146,117],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":27,"s":[48]},{"t":48,"s":[-41]}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[0]},{"t":18.09375,"s":[4]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":17,"s":[296]},{"i":{"x":[0],"y":[1]},"o":{"x":[1],"y":[0]},"t":37,"s":[182.4]},{"t":61,"s":[44]}],"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":60,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":17,"op":49,"st":17,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[960,540,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[30,0],[34.001,998]],"o":[[0,0],[-934,0],[0,-340]],"v":[[144,0],[-30,0],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19,"s":[48]},{"t":40,"s":[-41]}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"t":10.09375,"s":[4]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9,"s":[296]},{"i":{"x":[0],"y":[1]},"o":{"x":[1],"y":[0]},"t":29.227,"s":[182.4]},{"t":48,"s":[44]}],"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":60,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":9,"op":39,"st":9,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[960,540,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,-1040]],"o":[[0,0],[898,0],[0,1172]],"v":[[-30,-118],[146,-118],[0,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[48]},{"t":30,"s":[-41]}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1,"s":[0]},{"t":0.09375,"s":[4]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":-1,"s":[296]},{"i":{"x":[0],"y":[1]},"o":{"x":[1],"y":[0]},"t":19.227,"s":[182.4]},{"t":43,"s":[44]}],"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":60,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-1,"op":31,"st":-1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"E-SideLine Outlines 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[842,539.5,0],"ix":2},"a":{"a":0,"k":[30,147.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0,"y":0},"t":56,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-29.319,146.595],[29.319,146.595],[29.638,148.405],[-29,148.405]],"c":true}]},{"i":{"x":0.45,"y":1},"o":{"x":0.792,"y":0},"t":61,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-29.319,146.595],[29.319,146.595],[29.319,-276.595],[-29.319,-276.595]],"c":true}]},{"i":{"x":0.353,"y":1},"o":{"x":0.766,"y":0},"t":68,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-29.319,146.595],[29.319,146.595],[29.479,-64.095],[-29.16,-64.095]],"c":true}]},{"i":{"x":0.329,"y":1},"o":{"x":0.699,"y":0},"t":75,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-29.319,146.595],[29.319,146.595],[29.387,-185.524],[-29.251,-185.524]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":81,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-29.319,146.595],[29.319,146.595],[29.44,-116.136],[-29.199,-116.136]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":87,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-29.319,146.595],[29.319,146.595],[29.414,-150.83],[-29.225,-150.83]],"c":true}]},{"t":93,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-29.319,146.595],[29.319,146.595],[29.319,-146.595],[-29.319,-146.595]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.223,295.033],"ix":2},"a":{"a":0,"k":[0,147.516],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":57,"op":13182,"st":55,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"E-1rdLine Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0,"y":0},"t":47,"s":[1068.739,656.907,0],"to":[0,0,0],"ti":[0,0,0]},{"t":76,"s":[928.739,656.907,0]}],"ix":2},"a":{"a":0,"k":[89,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0,"y":0},"t":47,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[87.958,-29.319],[90.042,-29.731],[90.042,28.907],[87.958,29.319]],"c":true}]},{"t":66,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[87.958,-29.319],[-87.958,-29.319],[-87.958,29.319],[87.958,29.319]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[269.119,29.774],"ix":2},"a":{"a":0,"k":[89.1,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":49,"op":13174,"st":47,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"E-2ndLine Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0,"y":0},"t":38,"s":[918,539.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":66,"s":[1018,539.5,0]}],"ix":2},"a":{"a":0,"k":[89,30.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0,"y":0},"t":38,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-87.042,-29.638],[-87.958,-29.319],[-87.958,29.319],[-87.042,29]],"c":true}]},{"t":56,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[87.958,-29.319],[-87.958,-29.319],[-87.958,29.319],[87.958,29.319]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[89.119,30.497],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":39,"op":13164,"st":37,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"E-1stLine Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0,"y":0},"t":28,"s":[1158,422,0],"to":[0,0,0],"ti":[0,0,0]},{"t":57,"s":[1018,422,0]}],"ix":2},"a":{"a":0,"k":[89,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0,"y":0},"t":28,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[87.958,-29.319],[88.042,-28.319],[88.042,30.319],[87.958,29.319]],"c":true}]},{"t":47,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[87.958,-29.319],[-87.958,-29.319],[-87.958,29.319],[87.958,29.319]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.482352941176,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[89.119,30.221],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":31,"op":13155,"st":28,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Circle Outlines 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[959.5,539.5,0],"ix":2},"a":{"a":0,"k":[352.5,352.5,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.994,0.994,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":29,"s":[0,0,100]},{"t":40,"s":[100,100,100]}],"ix":6,"x":"var $bm_rt;\nvar damping, elasticity, speedLimit, tempsClefProx, tempsDebut, tempsDebut, temps, spring, spring;\nvar fx = effect('Transform Spring');\ndamping = fx(2).value;\nelasticity = fx(1).value;\n$bm_rt = speedLimit = 0.1;\nif (numKeys > 1 && elasticity != 0) {\n if (nearestKey(time).index == 1) {\n $bm_rt = value;\n } else {\n if (length(velocity) <= speedLimit) {\n tempsClefProx = nearestKey(time).time;\n if (tempsClefProx <= time) {\n tempsDebut = tempsClefProx;\n } else {\n tempsDebut = key($bm_sub(nearestKey(time).index, 1)).time;\n }\n temps = $bm_sub(time, tempsDebut);\n spring = $bm_mul(velocityAtTime($bm_sub(tempsDebut, thisComp.frameDuration)), $bm_div($bm_mul($bm_div(0.15, elasticity), Math.sin($bm_mul($bm_mul($bm_mul(elasticity, temps), 2), Math.PI))), Math.exp($bm_mul(temps, damping))));\n if (speedLimit > 0)\n spring = $bm_mul(spring, $bm_sub(1, $bm_div(length(velocity), speedLimit)));\n $bm_rt = $bm_sum(valueAtTime(tempsDebut), spring);\n } else {\n $bm_rt = value;\n }\n }\n} else {\n $bm_rt = value;\n}"}},"ao":0,"ef":[{"ty":5,"nm":"Transform Spring","np":4,"mn":"Pseudo/DUIK multid spring","ix":1,"en":1,"ef":[{"ty":0,"nm":"Elasticity","mn":"Pseudo/DUIK multid spring-0001","ix":1,"v":{"a":0,"k":2.68,"ix":1}},{"ty":0,"nm":"Damping","mn":"Pseudo/DUIK multid spring-0002","ix":2,"v":{"a":0,"k":5.2,"ix":2}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-194.308],[-194.308,0],[0,194.308],[194.308,0]],"o":[[0,194.308],[194.308,0],[0,-194.308],[-194.308,0]],"v":[[-351.829,0],[0,351.829],[351.829,0],[0,-351.829]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.572549019608,0,0.23137254902,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[352.5,352.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":29,"op":13156,"st":29,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/modules/lottie/module.php b/modules/lottie/module.php new file mode 100644 index 0000000..61b1dd2 --- /dev/null +++ b/modules/lottie/module.php @@ -0,0 +1,77 @@ +start_controls_section( 'lottie', [ + 'label' => esc_html__( 'Lottie', 'elementor-pro' ), + ] ); + + $this->add_control( + 'source', + [ + 'label' => esc_html__( 'Source', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'media_file', + 'options' => [ + 'media_file' => esc_html__( 'Media File', 'elementor-pro' ), + 'external_url' => esc_html__( 'External URL', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'source_external_url', + [ + 'label' => esc_html__( 'External URL', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'condition' => [ + 'source' => 'external_url', + ], + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'Enter your URL', 'elementor-pro' ), + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'source_json', + [ + 'label' => esc_html__( 'Upload JSON File', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'media_types' => [ 'application/json' ], + 'frontend_available' => true, + 'condition' => [ + 'source' => 'media_file', + ], + ] + ); + + $this->add_responsive_control( + 'align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'prefix_class' => 'elementor%s-align-', + 'default' => 'center', + ] + ); + + $this->add_control( + 'caption_source', + [ + 'label' => esc_html__( 'Caption', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'none', + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'title' => esc_html__( 'Title', 'elementor-pro' ), + 'caption' => esc_html__( 'Caption', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'condition' => [ + 'source!' => 'external_url', + 'source_json[url]!' => '', + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'caption', + [ + 'label' => esc_html__( 'Custom Caption', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'render_type' => 'none', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'caption_source', + 'value' => 'custom', + ], + [ + 'name' => 'source', + 'value' => 'external_url', + ], + ], + ], + 'dynamic' => [ + 'active' => true, + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'link_to', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'render_type' => 'none', + 'default' => 'none', + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom URL', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'custom_link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'render_type' => 'none', + 'placeholder' => esc_html__( 'Enter your URL', 'elementor-pro' ), + 'condition' => [ + 'link_to' => 'custom', + ], + 'dynamic' => [ + 'active' => true, + ], + 'default' => [ + 'url' => '', + ], + 'show_label' => false, + 'frontend_available' => true, + ] + ); + + // lottie. + $this->end_controls_section(); + + $this->start_controls_section( 'settings', [ + 'label' => esc_html__( 'Settings', 'elementor-pro' ), + ] ); + + $this->add_control( + 'trigger', + [ + 'label' => esc_html__( 'Trigger', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'arriving_to_viewport', + 'options' => [ + 'arriving_to_viewport' => esc_html__( 'Viewport', 'elementor-pro' ), + 'on_click' => esc_html__( 'On Click', 'elementor-pro' ), + 'on_hover' => esc_html__( 'On Hover', 'elementor-pro' ), + 'bind_to_scroll' => esc_html__( 'Scroll', 'elementor-pro' ), + 'none' => esc_html__( 'None', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'viewport', + [ + 'label' => esc_html__( 'Viewport', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'render_type' => 'none', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'trigger', + 'operator' => '===', + 'value' => 'arriving_to_viewport', + ], + [ + 'name' => 'trigger', + 'operator' => '===', + 'value' => 'bind_to_scroll', + ], + ], + ], + 'default' => [ + 'sizes' => [ + 'start' => 0, + 'end' => 100, + ], + 'unit' => '%', + ], + 'labels' => [ + __( 'Bottom', 'elementor-pro' ), + __( 'Top', 'elementor-pro' ), + ], + 'scales' => 1, + 'handles' => 'range', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'effects_relative_to', + [ + 'label' => esc_html__( 'Effects Relative To', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'render_type' => 'none', + 'condition' => [ + 'trigger' => 'bind_to_scroll', + ], + 'default' => 'viewport', + 'options' => [ + 'viewport' => esc_html__( 'Viewport', 'elementor-pro' ), + 'page' => esc_html__( 'Entire Page', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'loop', + [ + 'label' => esc_html__( 'Loop', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'render_type' => 'none', + 'condition' => [ + 'trigger!' => 'bind_to_scroll', + ], + 'return_value' => 'yes', + 'default' => '', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'number_of_times', + [ + 'label' => esc_html__( 'Times', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'render_type' => 'none', + 'conditions' => [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'trigger', + 'operator' => '!==', + 'value' => 'bind_to_scroll', + ], + [ + 'name' => 'loop', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + 'min' => 0, + 'step' => 1, + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'link_timeout', + [ + 'label' => esc_html__( 'Link Timeout', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::NUMBER, + 'render_type' => 'none', + 'conditions' => [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'link_to', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'trigger', + 'operator' => '===', + 'value' => 'on_click', + ], + [ + 'name' => 'custom_link[url]', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + 'description' => esc_html__( 'Redirect to link after selected timeout', 'elementor-pro' ), + 'min' => 0, + 'max' => 5000, + 'step' => 1, + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'on_hover_out', + [ + 'label' => esc_html__( 'On Hover Out', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'render_type' => 'none', + 'condition' => [ + 'trigger' => 'on_hover', + ], + 'default' => 'default', + 'options' => [ + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'reverse' => esc_html__( 'Reverse', 'elementor-pro' ), + 'pause' => esc_html__( 'Pause', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'hover_area', + [ + 'label' => esc_html__( 'Hover Area', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'render_type' => 'none', + 'condition' => [ + 'trigger' => 'on_hover', + ], + 'default' => 'animation', + 'options' => [ + 'animation' => esc_html__( 'Animation', 'elementor-pro' ), + 'column' => esc_html__( 'Column', 'elementor-pro' ), + 'section' => esc_html__( 'Section', 'elementor-pro' ), + 'container' => esc_html__( 'Container', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'play_speed', + [ + 'label' => esc_html__( 'Play Speed', 'elementor-pro' ) . ' (x)', + 'type' => Controls_Manager::SLIDER, + 'render_type' => 'none', + 'condition' => [ + 'trigger!' => 'bind_to_scroll', + ], + 'default' => [ + 'size' => 1, + ], + 'range' => [ + 'px' => [ + 'min' => 0.1, + 'max' => 5, + 'step' => 0.1, + ], + ], + 'size_units' => [ 'px' ], + 'dynamic' => [ + 'active' => true, + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'start_point', + [ + 'label' => esc_html__( 'Start Point', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'frontend_available' => true, + 'render_type' => 'none', + 'default' => [ + 'size' => 0, + 'unit' => '%', + ], + 'size_units' => [ '%' ], + ] + ); + + $this->add_control( + 'end_point', + [ + 'label' => esc_html__( 'End Point', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'frontend_available' => true, + 'render_type' => 'none', + 'default' => [ + 'size' => 100, + 'unit' => '%', + ], + 'size_units' => [ '%' ], + ] + ); + + $this->add_control( + 'reverse_animation', + [ + 'label' => esc_html__( 'Reverse', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'render_type' => 'none', + 'conditions' => [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'trigger', + 'operator' => '!==', + 'value' => 'bind_to_scroll', + ], + [ + 'name' => 'trigger', + 'operator' => '!==', + 'value' => 'on_hover', + ], + ], + ], + 'return_value' => 'yes', + 'default' => '', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'renderer', + [ + 'label' => esc_html__( 'Renderer', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'svg', + 'options' => [ + 'svg' => esc_html__( 'SVG', 'elementor-pro' ), + 'canvas' => esc_html__( 'Canvas', 'elementor-pro' ), + ], + 'separator' => 'before', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'lazyload', + [ + 'label' => esc_html__( 'Lazy Load', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'return_value' => 'yes', + 'default' => '', + 'frontend_available' => true, + ] + ); + + // Settings. + $this->end_controls_section(); + + $this->start_controls_section( + 'style', + [ + 'label' => esc_html__( 'Lottie', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'tablet_default' => [ + 'unit' => '%', + ], + 'mobile_default' => [ + 'unit' => '%', + ], + 'range' => [ + '%' => [ + 'min' => 1, + 'max' => 100, + ], + 'px' => [ + 'min' => 1, + 'max' => 1000, + ], + 'em' => [ + 'max' => 100, + ], + 'rem' => [ + 'max' => 100, + ], + 'vw' => [ + 'min' => 1, + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--lottie-container-width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'space', + [ + 'label' => esc_html__( 'Max Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'tablet_default' => [ + 'unit' => '%', + ], + 'mobile_default' => [ + 'unit' => '%', + ], + 'range' => [ + '%' => [ + 'min' => 1, + 'max' => 100, + ], + 'px' => [ + 'min' => 1, + 'max' => 1000, + ], + 'em' => [ + 'max' => 100, + ], + 'rem' => [ + 'max' => 100, + ], + 'vw' => [ + 'min' => 1, + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--lottie-container-max-width: {{SIZE}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->start_controls_tabs( 'image_effects' ); + + $this->start_controls_tab( 'normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'opacity', + [ + 'label' => esc_html__( 'Opacity', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'max' => 1, + 'min' => 0.10, + 'step' => 0.01, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--lottie-container-opacity: {{SIZE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Css_Filter::get_type(), + [ + 'name' => 'css_filters', + 'selector' => '{{WRAPPER}} .e-lottie__container', + ] + ); + + // Normal. + $this->end_controls_tab(); + + $this->start_controls_tab( 'hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'opacity_hover', + [ + 'label' => esc_html__( 'Opacity', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'max' => 1, + 'min' => 0.10, + 'step' => 0.01, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--lottie-container-opacity-hover: {{SIZE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Css_Filter::get_type(), + [ + 'name' => 'css_filters_hover', + 'selector' => '{{WRAPPER}} .e-lottie__container:hover', + ] + ); + + $this->add_control( + 'background_hover_transition', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (s)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3, + 'step' => 0.1, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--lottie-container-transition-duration-hover: {{SIZE}}s', + ], + ] + ); + + // Hover. + $this->end_controls_tab(); + + // Image effects. + $this->end_controls_tabs(); + + // lottie style. + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_caption', + [ + 'label' => esc_html__( 'Caption', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'caption_source!' => 'none', + ], + ] + ); + + $this->add_control( + 'caption_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'default' => 'center', + 'selectors' => [ + '{{WRAPPER}}' => '--caption-text-align: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}}' => '--caption-color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'caption_typography', + 'selector' => '{{WRAPPER}} .e-lottie__caption', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_responsive_control( + 'caption_space', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--caption-margin-top: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + } + + private function get_caption( $settings ) { + $is_media_file_caption = $this->is_media_file_caption( $settings ); + $is_external_url_caption = $this->is_external_url_caption( $settings ); + + if ( ( $is_media_file_caption && 'custom' === $settings['caption_source'] ) || $is_external_url_caption ) { + return $settings['caption']; + } else if ( 'caption' === $settings['caption_source'] ) { + return wp_get_attachment_caption( $settings['source_json']['id'] ); + } else if ( 'title' === $settings['caption_source'] ) { + return get_the_title( $settings['source_json']['id'] ); + } + + return ''; + } + + private function is_media_file_caption( $settings ) { + return 'media_file' === $settings['source'] && 'none' !== $settings['caption_source']; + } + + private function is_external_url_caption( $settings ) { + return 'external_url' === $settings['source'] && '' !== $settings['caption']; + } + + protected function render() { + $settings = $this->get_settings_for_display(); + $caption = $this->get_caption( $settings ); + $widget_caption = $caption ? '

    ' . esc_html( $caption ) . '

    ' : ''; + $widget_container = '
    ' . $widget_caption . '
    '; + + if ( ! empty( $settings['custom_link']['url'] ) && 'custom' === $settings['link_to'] ) { + $this->add_link_attributes( 'url', $settings['custom_link'] ); + $widget_container = sprintf( '%2$s', $this->get_render_attribute_string( 'url' ), $widget_container ); + } + + // PHPCS - XSS ok. Everything that should be escaped in the way is escaped. + echo $widget_container; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + protected function content_template() { + ?> + <# + var ensureAttachmentData = function( id, type ) { + if ( 'caption' === type || 'title' === type ) { + if ( 'undefined' === typeof wp.media.attachment( id ).get( type ) ) { + wp.media.attachment( id ).fetch().then( function( data ) { + view.render(); + } ); + } + } + }; + + var getAttachmentData = function( id, type ) { + if ( id && ( 'caption' === type || 'title' === type ) ) { + ensureAttachmentData( id, type ); + return wp.media.attachment( id ).get( type ); + } + + return ''; + }; + + var getCaption = function() { + if ( ( isMediaFileCaption() && 'custom' === settings.caption_source ) || isExternalUrlCaption() ) { + return settings.caption; + } else if ( 'caption' === settings.caption_source || 'title' === settings.caption_source ) { + return getAttachmentData( settings.source_json.id, settings.caption_source ); + } + + return ''; + }; + + var isMediaFileCaption = function() { + return 'media_file' === settings.source && 'none' !== settings.caption_source; + }; + + var isExternalUrlCaption = function() { + return 'external_url' === settings.source && '' !== settings.caption; + }; + + var widget_caption = getCaption() ? '

    ' + getCaption() + '

    ' : ''; + var widget_container = '
    ' + widget_caption + '
    '; + + if ( settings.custom_link.url && 'custom' === settings.link_to ) { + widget_container = '' + widget_container + ''; + } + + print( widget_container ); + #> + experiments->is_feature_active( \Elementor\Modules\NestedElements\Module::EXPERIMENT_NAME ); + } + + /** + * Add to the experiments + * + * @return array + */ + public static function get_experimental_data() { + $experiment_data = [ + 'name' => static::EXPERIMENT_NAME, + 'title' => esc_html__( 'Menu', 'elementor-pro' ), + 'description' => sprintf( + esc_html__( 'Create beautiful menus and mega menus with new nested capabilities. Mega menus are ideal for websites with complex navigation structures and unique designs. %1$sLearn More%2$s', 'elementor-pro' ), + '', + '' + ), + 'hidden' => false, + 'release_status' => Manager::RELEASE_STATUS_BETA, + 'default' => Manager::STATE_INACTIVE, + 'dependencies' => [ + 'container', + 'nested-elements', + ], + ]; + + if ( version_compare( ELEMENTOR_VERSION, '3.11.0', '<' ) ) { + $experiment_data['mutable'] = false; + $experiment_data['dependencies'] = []; + } + return $experiment_data; + } +} diff --git a/modules/mega-menu/traits/url-helper-trait.php b/modules/mega-menu/traits/url-helper-trait.php new file mode 100644 index 0000000..b6e4010 --- /dev/null +++ b/modules/mega-menu/traits/url-helper-trait.php @@ -0,0 +1,44 @@ + ! empty( $link_array['host'] ) ? str_replace( 'www.', '', $link_array['host'] ) : '', + 'path' => ! empty( $link_array['path'] ) ? trim( $link_array['path'], '/' ) : '', + 'query' => ! empty( $link_array['query'] ) ? $link_array['query'] : '', + ]; + } + + public function get_permalink_for_current_page() { + if ( ! is_front_page() && is_home() ) { + return get_post_type_archive_link( 'post' ); + } elseif ( is_front_page() && is_home() ) { + return home_url(); + } elseif ( is_year() ) { + return get_year_link( get_query_var( 'year' ) ); + } elseif ( is_month() ) { + return get_month_link( get_query_var( 'year' ), get_query_var( 'monthnum' ) ); + } elseif ( is_day() ) { + return get_day_link( get_query_var( 'year' ), get_query_var( 'monthnum' ), get_query_var( 'day' ) ); + } elseif ( is_category() || is_tag() || is_tax() ) { + $queried_object = get_queried_object(); + return get_term_link( $queried_object->term_id, $queried_object->taxonomy ); + } elseif ( is_author() ) { + return get_author_posts_url( get_the_author_meta( 'ID' ) ); + } elseif ( is_search() ) { + return get_search_link(); + } elseif ( is_archive() ) { + return get_post_type_archive_link( get_post_type() ); + } + + return ! ( empty( get_the_permalink() ) ) ? get_the_permalink() : ''; + } +} diff --git a/modules/mega-menu/widgets/mega-menu.php b/modules/mega-menu/widgets/mega-menu.php new file mode 100644 index 0000000..dd16052 --- /dev/null +++ b/modules/mega-menu/widgets/mega-menu.php @@ -0,0 +1,2631 @@ + [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-h-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ]; + + return is_rtl() ? array_reverse( $horizontal_controls ) : $horizontal_controls; + } + + protected function get_default_children_elements() { + return [ + [ + 'elType' => 'container', + 'settings' => [ + '_title' => __( 'Item #1', 'elementor-pro' ), + ], + ], + [ + 'elType' => 'container', + 'settings' => [ + '_title' => __( 'Item #2', 'elementor-pro' ), + ], + ], + [ + 'elType' => 'container', + 'settings' => [ + '_title' => __( 'Item #3', 'elementor-pro' ), + ], + ], + ]; + } + + protected function get_default_repeater_title_setting_key() { + return 'item_title'; + } + + protected function get_default_children_title() { + return esc_html__( 'Item #%d', 'elementor-pro' ); + } + + protected function get_default_children_placeholder_selector() { + return '.e-n-menu-content'; + } + + protected function get_html_wrapper_class() { + return 'elementor-widget-n-menu'; + } + + /** + * Define a selector class for a widget control. + * + * @param string $item The name of the element which we need to select. + * @param string $state The state of the selector, e.g. `:hover` or `:focus`. + * + * @return string The css selector for our element. + * @since 3.12.0 + */ + protected function get_control_selector_class( $control_item, $state = '' ) { + if ( 'menu_toggle_icon' === $control_item ) { + return "{{WRAPPER}} > .elementor-widget-container > .e-n-menu > .e-n-menu-toggle{$state} > .e-n-menu-toggle-icon"; + } elseif ( 'active_content_container' === $control_item ) { + return ":where( {{WRAPPER}} > .elementor-widget-container > .e-n-menu > .e-n-menu-wrapper > .e-n-menu-content ) > .e-con{$state}"; + } + } + + /** + * Get Typography Selector + * + * Returns a selector class for the typography widget control. + * + * @param string $heading_selector The css selector for the menu. + * + * @return string The css selector for the typography control. + */ + protected function get_typography_selector( $heading_selector ): string { + $typography_selector = "{$heading_selector} > .e-n-menu-title"; + $typography_selector .= ", {$heading_selector} > .e-n-menu-title > .e-n-menu-title-text"; + $typography_selector .= ", {$heading_selector} > .e-n-menu-title > a.e-n-menu-title-link > .e-n-menu-title-text"; + + return $typography_selector; + } + + protected function register_controls() { + $heading_selector = '{{WRAPPER}} > .elementor-widget-container > .e-n-menu > .e-n-menu-wrapper > .e-n-menu-heading'; + $start = is_rtl() ? 'right' : 'left'; + $end = is_rtl() ? 'left' : 'right'; + $logical_dimensions_inline_start = is_rtl() ? '{{RIGHT}}{{UNIT}}' : '{{LEFT}}{{UNIT}}'; + $logical_dimensions_inline_end = is_rtl() ? '{{LEFT}}{{UNIT}}' : '{{RIGHT}}{{UNIT}}'; + $start_logical = is_rtl() ? 'end' : 'start'; + $end_logical = is_rtl() ? 'start' : 'end'; + + $this->start_controls_section( + 'section_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + ] + ); + + $repeater = new Repeater(); + + $repeater->add_control( + 'item_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Item Title', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'label_block' => true, + ] + ); + + $repeater->add_control( + 'item_link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'placeholder' => esc_html__( 'Paste URL or type', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'frontend_available' => true, + ] + ); + + $repeater->add_control( + 'item_dropdown_content', + [ + 'label' => esc_html__( 'Dropdown Content', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'OFF', 'elementor-pro' ), + 'label_on' => esc_html__( 'ON', 'elementor-pro' ), + 'default' => 'no', + 'description' => esc_html__( 'Click on the menu item to edit its dropdown content.', 'elementor-pro' ), + 'frontend_available' => true, + ] + ); + + $repeater->add_control( + 'item_icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'skin' => 'inline', + 'label_block' => false, + ] + ); + + $repeater->add_control( + 'item_icon_active', + [ + 'label' => esc_html__( 'Active Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'skin' => 'inline', + 'label_block' => false, + 'condition' => [ + 'item_icon[value]!' => '', + ], + ] + ); + + $repeater->add_control( + 'element_id', + [ + 'label' => esc_html__( 'CSS ID', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor-pro' ), + 'style_transfer' => false, + ] + ); + + $this->add_control( + 'menu_items', + [ + 'label' => esc_html__( 'Menu Items', 'elementor-pro' ), + 'type' => Control_Nested_Repeater::CONTROL_TYPE, + 'fields' => $repeater->get_controls(), + 'default' => [ + [ + 'item_title' => esc_html__( 'Item #1', 'elementor-pro' ), + ], + [ + 'item_title' => esc_html__( 'Item #2', 'elementor-pro' ), + ], + [ + 'item_title' => esc_html__( 'Item #3', 'elementor-pro' ), + ], + ], + 'title_field' => '{{{ item_title }}}', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'content_width', + [ + 'label' => esc_html__( 'Content Width', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'full_width', + 'separator' => 'before', + 'options' => [ + 'full_width' => esc_html__( 'Full Width', 'elementor-pro' ), + 'fit_to_content' => esc_html__( 'Fit To Content', 'elementor-pro' ), + ], + 'prefix_class' => 'e-', + 'frontend_available' => true, + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-dropdown-content-max-width: {{VALUE}};', + ], + 'selectors_dictionary' => [ + 'full_width' => 'initial', + 'fit_to_content' => 'fit-content', + ], + ] + ); + + $this->add_control( + 'content_horizontal_position', + [ + 'label' => esc_html__( 'Content Horizontal Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => $this->get_content_horizontal_controls(), + 'default' => 'center', + 'condition' => [ + 'content_width' => 'fit_to_content', + ], + 'frontend_available' => true, + 'render_type' => 'ui', + ] + ); + + $this->add_control( + 'item_layout', + [ + 'label' => esc_html__( 'Item Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'horizontal', + 'options' => [ + 'horizontal' => esc_html__( 'Horizontal', 'elementor-pro' ), + 'dropdown' => esc_html__( 'Dropdown', 'elementor-pro' ), + ], + 'prefix_class' => 'e-n-menu-layout-', + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( 'item_position_horizontal', [ + 'label' => esc_html__( 'Item Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => "eicon-align-$start_logical-h", + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-align-center-h', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => "eicon-align-$end_logical-h", + ], + 'stretch' => [ + 'title' => esc_html__( 'Stretch', 'elementor-pro' ), + 'icon' => 'eicon-align-stretch-h', + ], + ], + 'selectors_dictionary' => [ + 'start' => '--n-menu-heading-justify-content: initial; --n-menu-title-flex-grow: initial; --n-menu-title-justify-content: initial; --n-menu-title-justify-content-mobile: initial;', + 'center' => '--n-menu-heading-justify-content: center; --n-menu-title-flex-grow: initial; --n-menu-title-justify-content: initial; --n-menu-title-justify-content-mobile: center;', + 'end' => '--n-menu-heading-justify-content: flex-end; --n-menu-title-flex-grow: initial; --n-menu-title-justify-content: initial; --n-menu-title-justify-content-mobile: flex-end;', + 'stretch' => '--n-menu-heading-justify-content: space-between; --n-menu-title-flex-grow: 1; --n-menu-title-justify-content: center; --n-menu-title-justify-content-mobile: center;', + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'condition' => [ + 'item_layout' => 'horizontal', + ], + 'frontend_available' => true, + ]); + + $this->add_responsive_control( 'item_position_dropdown', [ + 'label' => esc_html__( 'Item Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => "eicon-align-$start_logical-h", + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-align-center-h', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => "eicon-align-$end_logical-h", + ], + ], + 'selectors_dictionary' => [ + 'start' => '--n-menu-title-justify-content: initial; --n-menu-title-justify-content-mobile: initial;', + 'center' => '--n-menu-title-justify-content: center; --n-menu-title-justify-content-mobile: center;', + 'end' => '--n-menu-title-justify-content: flex-end; --n-menu-title-justify-content-mobile: flex-end;', + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'condition' => [ + 'item_layout' => 'dropdown', + ], + ]); + + $this->add_control( + 'submenu_indicator_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Dropdown Indicator', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'menu_item_icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'default' => [ + 'value' => 'fas fa-caret-down', + 'library' => 'fa-solid', + ], + 'recommended' => [ + 'fa-solid' => [ + 'chevron-down', + 'angle-down', + 'angle-double-down', + 'caret-down', + 'caret-square-down', + ], + 'fa-regular' => [ + 'caret-square-down', + ], + ], + 'skin' => 'inline', + 'label_block' => false, + ] + ); + + $this->add_control( + 'menu_item_icon_active', + [ + 'label' => esc_html__( 'Active Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon_active', + 'default' => [ + 'value' => 'fas fa-caret-up', + 'library' => 'fa-solid', + ], + 'recommended' => [ + 'fa-solid' => [ + 'chevron-up', + 'angle-up', + 'angle-double-up', + 'caret-up', + 'caret-square-up', + ], + 'fa-regular' => [ + 'caret-square-up', + ], + ], + 'skin' => 'inline', + 'label_block' => false, + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( 'section_dropdown_effect', [ + 'label' => esc_html__( 'Dropdown Effect', 'elementor-pro' ), + ] ); + + $this->add_control( + 'open_on', + [ + 'label' => esc_html__( 'Open On', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'hover', + 'options' => [ + 'hover' => esc_html__( 'Hover', 'elementor-pro' ), + 'click' => esc_html__( 'Click', 'elementor-pro' ), + ], + 'condition' => [ + 'item_layout' => 'horizontal', + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'open_on_hover_description', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'The hover effect is inactive while editing. Preview your page to see it in action.', 'elementor-pro' ), + 'content_classes' => 'elementor-control-field-description', + 'condition' => [ + 'item_layout' => 'horizontal', + 'open_on' => 'hover', + ], + ] + ); + + $this->add_control( + 'open_animation', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'none', + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'fadeIn' => esc_html__( 'Fade in', 'elementor-pro' ), // Key must match the class from animate.css + ], + 'assets' => [ + 'styles' => [ + [ + 'name' => 'e-animations', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'open_animation', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'open_animation_duration', + [ + 'label' => esc_html__( 'Animation Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + 'size' => 500, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-open-animation-duration: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'open_animation!' => '', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( 'menu_toggle_section', [ + 'label' => esc_html__( 'Menu Toggle', 'elementor-pro' ), + ] ); + + $this->add_responsive_control( + 'toggle_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'flex-start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => "eicon-align-$start_logical-h", + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-h-align-center', + ], + 'flex-end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => "eicon-align-$end_logical-h", + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-toggle-align: {{VALUE}}', + ], + ] + ); + + $this->start_controls_tabs( 'menu_toggle_tabs_section' ); + + $this->start_controls_tab( 'menu_toggle_normal_options', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $this->add_control( + 'menu_toggle_icon_normal', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'skin' => 'inline', + 'label_block' => false, + 'skin_settings' => [ + 'inline' => [ + 'none' => [ + 'label' => esc_html__( 'Default', 'elementor-pro' ), + 'icon' => 'eicon-menu-bar', + ], + 'icon' => [ + 'icon' => 'eicon-star', + ], + ], + ], + 'recommended' => [ + 'fa-solid' => [ + 'plus-square', + 'plus', + 'plus-circle', + 'bars', + ], + 'fa-regular' => [ + 'plus-square', + ], + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'menu_toggle_hover_options', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] ); + + $this->add_control( + 'menu_toggle_icon_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + 'frontend_available' => true, + 'render_type' => 'template', + ] + ); + + $this->add_control( + 'menu_toggle_animation_duration', + [ + 'label' => esc_html__( 'Animation Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'render_type' => 'ui', + 'default' => [ + 'unit' => 'ms', + 'size' => 500, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-toggle-icon-wrapper-animation-duration: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'menu_toggle_active_options', [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] ); + + $this->add_control( + 'menu_toggle_icon_active', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'skin' => 'inline', + 'label_block' => false, + 'skin_settings' => [ + 'inline' => [ + 'none' => [ + 'label' => esc_html__( 'Default', 'elementor-pro' ), + 'icon' => 'eicon-close', + ], + 'icon' => [ + 'icon' => 'eicon-star', + ], + ], + ], + 'recommended' => [ + 'fa-solid' => [ + 'window-close', + 'times-circle', + 'times', + 'minus-square', + 'minus-circle', + 'minus', + ], + 'fa-regular' => [ + 'window-close', + 'times-circle', + 'minus-square', + ], + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( 'section_responsive_mega_menu', [ + 'label' => esc_html__( 'Additional Settings', 'elementor-pro' ), + ] ); + + $this->add_responsive_control( + 'horizontal_scroll', + [ + 'label' => esc_html__( 'Horizontal Scroll', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'description' => esc_html__( 'Note: Scroll menu items if they don’t fit into their parent container.', 'elementor-pro' ), + 'options' => [ + 'disable' => esc_html__( 'Disable', 'elementor-pro' ), + 'enable' => esc_html__( 'Enable', 'elementor-pro' ), + ], + 'default' => 'disable', + 'selectors_dictionary' => [ + 'disable' => '--n-menu-heading-wrap: wrap; --n-menu-heading-overflow-x: initial;', + 'enable' => '--n-menu-heading-wrap: nowrap; --n-menu-heading-overflow-x: scroll;', + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'frontend_available' => true, + 'condition' => [ + 'item_layout' => 'horizontal', + ], + ] + ); + + $dropdown_options = [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + ]; + + $excluded_breakpoints = [ + 'widescreen', + ]; + + foreach ( Plugin::elementor()->breakpoints->get_active_breakpoints() as $breakpoint_key => $breakpoint_instance ) { + // Exclude the larger breakpoints from the dropdown selector. + if ( in_array( $breakpoint_key, $excluded_breakpoints, true ) ) { + continue; + } + + $dropdown_options[ $breakpoint_key ] = sprintf( + /* translators: 1: Breakpoint label, 2: `>` character, 3: Breakpoint value. */ + esc_html__( '%1$s (%2$s %3$dpx)', 'elementor-pro' ), + $breakpoint_instance->get_label(), + '>', + $breakpoint_instance->get_value() + ); + } + + $this->add_control( + 'breakpoint_selector', + [ + 'label' => esc_html__( 'Breakpoint', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'description' => esc_html__( 'Note: Item layout will switch to dropdown on any screen smaller than the selected breakpoint.', 'elementor-pro' ), + 'options' => $dropdown_options, + 'default' => 'tablet', + 'prefix_class' => 'e-n-menu-', + 'frontend_available' => true, + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( 'section_menu_items_style', [ + 'label' => esc_html__( 'Menu Items', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] ); + + $this->add_responsive_control( 'menu_item_title_space_between', [ + 'label' => esc_html__( 'Space between Items', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'default' => [ + 'size' => 0, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-title-space-between: {{SIZE}}{{UNIT}}', + ], + ] ); + + $this->add_responsive_control( 'menu_item_title_distance_from_content', [ + 'label' => esc_html__( 'Distance from content', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'default' => [ + 'size' => 0, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-title-distance-from-content: {{SIZE}}{{UNIT}}', + ], + 'frontend_available' => true, + ] ); + + $this->add_group_control( Group_Control_Typography::get_type(), [ + 'name' => 'menu_item_title_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => $this->get_typography_selector( $heading_selector ), + 'fields_options' => [ + 'font_size' => [ + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-title-font-size: {{SIZE}}{{UNIT}}', + ], + ], + 'line_height' => [ + 'selectors' => [ + '{{SELECTOR}}' => '--e-global-typography-{{external._id.VALUE}}-line-height: {{SIZE}}{{UNIT}}', + '{{SELECTOR}}' => '--n-menu-title-line-height: {{SIZE}}', + ], + ], + ], + ] ); + + $this->start_controls_tabs( 'menu_item_title_style' ); + + $this->start_controls_tab( + 'menu_item_title_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'menu_item_title_text_color_normal', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-title-color-normal: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'menu_item_title_text_shadow_normal', + 'selector' => "{$heading_selector} > .e-n-menu-title:not( .e-current ):not( :hover )", + 'fields_options' => [ + 'text_shadow_type' => [ + 'label' => esc_html_x( 'Shadow', 'Text Shadow Control', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'menu_item_title_background_color', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => "{$heading_selector} > .e-n-menu-title:not( .e-current ):not( :hover )", + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'menu_item_title_box_border', + 'selector' => "{$heading_selector} > .e-n-menu-title:not( .e-current ):not( :hover )", + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + ], + 'width' => [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'menu_item_title_box_shadow', + 'selector' => "{$heading_selector} > .e-n-menu-title:not( .e-current ):not( :hover )", + ] + ); + + $divider_condition = [ + 'menu_divider' => 'yes', + 'item_layout' => 'horizontal', + ]; + + $this->add_control( + 'menu_divider', + [ + 'label' => esc_html__( 'Divider', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'condition' => [ + 'item_layout' => 'horizontal', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-divider-content: "";', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'menu_divider_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'solid' => esc_html__( 'Solid', 'elementor-pro' ), + 'double' => esc_html__( 'Double', 'elementor-pro' ), + 'dotted' => esc_html__( 'Dotted', 'elementor-pro' ), + 'dashed' => esc_html__( 'Dashed', 'elementor-pro' ), + ], + 'default' => 'solid', + 'condition' => $divider_condition, + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-divider-style: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'menu_divider_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'condition' => $divider_condition, + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-divider-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'menu_divider_height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + '%' => [ + 'min' => 1, + 'max' => 100, + ], + ], + 'condition' => $divider_condition, + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-divider-height: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'menu_divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'condition' => $divider_condition, + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-divider-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); // End Normal Tab + + $this->start_controls_tab( + 'menu_item_title_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'menu_item_title_text_color_hover', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} ' => '--n-menu-title-color-hover: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'menu_item_title_text_shadow_hover', + 'selector' => "{$heading_selector} > .e-n-menu-title:hover:not( .e-current )", + 'fields_options' => [ + 'text_shadow_type' => [ + 'label' => esc_html_x( 'Shadow', 'Text Shadow Control', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'menu_item_title_background_color_hover', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => "{$heading_selector} > .e-n-menu-title:hover:not( .e-current )", + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'menu_item_title_box_border_hover', + 'selector' => "{$heading_selector} > .e-n-menu-title:hover:not( .e-current )", + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + ], + 'width' => [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'menu_item_title_box_shadow_hover', + 'selector' => "{$heading_selector} > .e-n-menu-title:hover:not( .e-current )", + ] + ); + + $this->add_control( + 'hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + ] + ); + + $this->add_control( + 'menu_item_title_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-title-transition: {{SIZE}}{{UNIT}}', + ], + 'default' => [ + 'unit' => 'ms', + 'size' => 300, + ], + ] + ); + $this->end_controls_tab(); // End Hover Tab + + $this->start_controls_tab( + 'menu_item_title_active', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'menu_item_title_text_color_active', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} ' => '--n-menu-title-color-active: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'menu_item_title_text_shadow_active', + 'selector' => "{$heading_selector} > .e-n-menu-title.e-current", + 'fields_options' => [ + 'text_shadow_type' => [ + 'label' => esc_html_x( 'Shadow', 'Text Shadow Control', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'menu_item_title_background_color_active', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => "{$heading_selector} > .e-n-menu-title.e-current", + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'menu_item_title_box_border_active', + 'selector' => "{$heading_selector} > .e-n-menu-title.e-current", + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + ], + 'width' => [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'menu_item_title_box_shadow_active', + 'selector' => "{$heading_selector} > .e-n-menu-title.e-current", + ] + ); + + $this->end_controls_tab(); // End Active Tab + + $this->end_controls_tabs(); + + $this->add_responsive_control( + 'menu_item_title_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + "{$heading_selector} > .e-n-menu-title" => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'menu_item_title_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-title-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + // Icon Style + $this->start_controls_section( 'icon_section_style', [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] ); + + $styling_block_start = '--n-menu-title-direction: column; --n-menu-icon-order: initial; --n-menu-icon-align-items: flex-end; --n-menu-title-justify-content: center; --n-menu-title-align-items-toggle: initial;'; + $styling_inline_end = '--n-menu-title-direction: row; --n-menu-icon-order: 1; --n-menu-icon-align-items: initial; --n-menu-title-justify-content: initial; --n-menu-title-align-items-toggle: center;'; + $styling_block_end = '--n-menu-title-direction: column; --n-menu-icon-order: 1; --n-menu-icon-align-items: flex-start; --n-menu-title-justify-content: center; --n-menu-title-align-items-toggle: initial;'; + $styling_inline_start = '--n-menu-title-direction: row; --n-menu-icon-order: initial; --n-menu-icon-align-items: initial; --n-menu-title-justify-content: initial; --n-menu-title-align-items-toggle: center;'; + + $this->add_responsive_control( 'icon_position', [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'block-start' => [ + 'title' => esc_html__( 'Above', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'inline-end' => [ + 'title' => esc_html__( 'After', 'elementor-pro' ), + 'icon' => 'eicon-h-align-' . $end, + ], + 'block-end' => [ + 'title' => esc_html__( 'Below', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + 'inline-start' => [ + 'title' => esc_html__( 'Before', 'elementor-pro' ), + 'icon' => 'eicon-h-align-' . $start, + ], + ], + 'selectors_dictionary' => [ + 'block-start' => $styling_block_start, + 'inline-end' => $styling_inline_end, + 'block-end' => $styling_block_end, + 'inline-start' => $styling_inline_start, + // Styling duplication for BC reasons. + 'top' => $styling_block_start, + 'end' => $styling_inline_end, + 'bottom' => $styling_block_end, + 'start' => $styling_inline_start, + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + ] ); + + $this->add_responsive_control( 'icon_size', [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'min' => 0, + 'max' => 10, + ], + 'rem' => [ + 'min' => 0, + 'max' => 10, + ], + ], + 'default' => [ + 'unit' => 'px', + 'size' => 16, + ], + 'size_units' => [ 'px', 'em', 'rem' ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-icon-size: {{SIZE}}{{UNIT}}', + ], + ] ); + + $this->add_responsive_control( 'icon_spacing', [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 400, + ], + 'em' => [ + 'max' => 40, + ], + 'rem' => [ + 'max' => 40, + ], + 'vw' => [ + 'min' => 0, + 'max' => 50, + 'step' => 0.1, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-icon-gap: {{SIZE}}{{UNIT}}', + ], + ] ); + + $this->start_controls_tabs( 'icon_style_states' ); + + $this->start_controls_tab( + 'icon_section_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( 'icon_color', [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-icon-color: {{VALUE}};', + ], + ] ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'icon_section_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( 'icon_color_hover', [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-icon-color-hover: {{VALUE}};', + ], + ] ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'icon_section_active', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( 'icon_color_active', [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-icon-color-active: {{VALUE}};', + ], + ] ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( 'section_dropdown_indicator_style', [ + 'label' => esc_html__( 'Dropdown Indicator', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'menu_item_icon[value]', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'menu_item_icon_active[value]', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ] ); + + $this->add_responsive_control( + 'style_dropdown_indicator_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-dropdown-indicator-size: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'style_dropdown_indicator_rotate', + [ + 'label' => esc_html__( 'Rotate', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'deg', 'grad', 'rad', 'turn', 'custom' ], + 'default' => [ + 'unit' => 'deg', + ], + 'tablet_default' => [ + 'unit' => 'deg', + ], + 'mobile_default' => [ + 'unit' => 'deg', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-dropdown-indicator-rotate: rotate({{SIZE}}{{UNIT}})', + ], + ] + ); + + $this->add_responsive_control( + 'style_dropdown_indicator_space', + [ + 'label' => esc_html__( 'Space', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-dropdown-indicator-space: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->start_controls_tabs( 'style_menu_dropdown_indicator' ); + + foreach ( array( 'normal', 'hover', 'active' ) as $state ) { + $this->add_dropdown_indicator_state_based_style_controls( $state ); + } + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( 'section_menu_toggle_style', [ + 'label' => esc_html__( 'Menu Toggle', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] ); + + $this->add_control( + 'style_menu_toggle_icon_heading', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Toggle Icon', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'style_menu_toggle_icon_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 20, + ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-toggle-icon-size: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->start_controls_tabs( 'style_menu_toggle_tabs' ); + + $this->start_controls_tab( 'style_menu_toggle_tab_normal', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $this->add_control( 'menu_toggle_icon_color_normal', [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'render_type' => 'ui', + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-toggle-icon-color: {{VALUE}};', + ], + ] ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'menu_toggle_icon_background_color_normal', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => $this->get_control_selector_class( 'menu_toggle_icon', '[aria-expanded="false"]:not( :hover )' ), + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'selectors' => [ + '{{SELECTOR}}' => 'background: {{VALUE}}', + ], + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'menu_toggle_icon_border_normal', + 'selector' => $this->get_control_selector_class( 'menu_toggle_icon', '[aria-expanded="false"]:not( :hover )' ), + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + ], + 'width' => [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'menu_toggle_icon_box_shadow_normal', + 'selector' => $this->get_control_selector_class( 'menu_toggle_icon', '[aria-expanded="false"]:not( :hover )' ), + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'style_menu_toggle_hover', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] ); + + $this->add_control( 'menu_toggle_icon_color_hover', [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'render_type' => 'ui', + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-toggle-icon-color-hover: {{VALUE}};', + ], + ] ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'menu_toggle_icon_background_color_hover', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => $this->get_control_selector_class( 'menu_toggle_icon', ':hover:is( [aria-expanded="true"], [aria-expanded="false"] )' ), + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'selectors' => [ + '{{SELECTOR}}' => 'background: {{VALUE}}', + ], + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'menu_toggle_icon_border_hover', + 'selector' => $this->get_control_selector_class( 'menu_toggle_icon', ':hover:is( [aria-expanded="true"], [aria-expanded="false"] )' ), + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + ], + 'width' => [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'menu_toggle_icon_box_shadow_hover', + 'selector' => $this->get_control_selector_class( 'menu_toggle_icon', ':hover:is( [aria-expanded="true"], [aria-expanded="false"] )' ), + ] + ); + + $this->add_control( + 'menu_toggle_icon_animation_duration', + [ + 'label' => esc_html__( 'Animation Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + 'size' => 500, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-toggle-icon-hover-duration: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'style_menu_toggle_active', [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] ); + + $this->add_control( 'menu_toggle_icon_color_active', [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'render_type' => 'ui', + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-toggle-icon-color-active: {{VALUE}};', + ], + ] ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'menu_toggle_icon_background_color_active', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => $this->get_control_selector_class( 'menu_toggle_icon', '[aria-expanded="true"]' ), + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'selectors' => [ + '{{SELECTOR}}' => 'background: {{VALUE}}', + ], + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'menu_toggle_icon_border_active', + 'selector' => $this->get_control_selector_class( 'menu_toggle_icon', '[aria-expanded="true"]' ), + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + ], + 'width' => [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'menu_toggle_icon_box_shadow_active', + 'selector' => $this->get_control_selector_class( 'menu_toggle_icon', '[aria-expanded="true"]' ), + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_responsive_control( + 'menu_toggle_icon_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-toggle-icon-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'menu_toggle_icon__padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-toggle-icon-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( 'menu_toggle_icon_distance_from_dropdown', [ + 'label' => esc_html__( 'Distance from dropdown', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'default' => [ + 'size' => 0, + ], + 'placeholder' => [ + 'size' => 0, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-toggle-icon-distance-from-dropdown: {{SIZE}}{{UNIT}}', + ], + ] ); + + $this->end_controls_section(); + + $this->start_controls_section( 'section_content_style', [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'content_background_color', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => $this->get_control_selector_class( 'active_content_container' ), + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'content_border', + 'selector' => $this->get_control_selector_class( 'active_content_container' ), + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + ], + 'width' => [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_responsive_control( + 'content_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + $this->get_control_selector_class( 'active_content_container' ) => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'content_shadow', + 'selector' => $this->get_control_selector_class( 'active_content_container' ), + ] + ); + + $this->add_responsive_control( + 'content_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + $this->get_control_selector_class( 'active_content_container' ) => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};', + // Todo: Remove in version 3.21.0: https://elementor.atlassian.net/browse/ED-11888. + // Remove together with support for physical properties inside the container widget. + ':where( [data-core-v316-plus="true"] .elementor-element.elementor-widget-n-menu > .elementor-widget-container > .e-n-menu > .e-n-menu-wrapper > .e-n-menu-content ) > .e-con' => "--padding-block-start: {{TOP}}{{UNIT}}; --padding-inline-end: $logical_dimensions_inline_end; --padding-block-end: {{BOTTOM}}{{UNIT}}; --padding-inline-start: $logical_dimensions_inline_start;", + ], + 'separator' => 'before', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( 'section_dropdown_menu_style', [ + 'label' => esc_html__( 'Dropdown Menu', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] ); + + $this->add_control( + 'dropdown_menu_items_title', + [ + 'label' => esc_html__( 'Menu Items', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'dropdown_menu_items_description', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'Styles apply to items when the menu switches to dropdown layout', 'elementor-pro' ), + 'content_classes' => 'elementor-control-field-description', + ] + ); + + $this->start_controls_tabs( 'menu_dropdown_states_section' ); + + $this->start_controls_tab( + 'normal_menu_dropdown_states', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( 'dropdown_menu_item_text_color_normal', [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-title-normal-color-dropdown: {{VALUE}};', + ], + ] ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'dropdown_menu_item_background_color_normal', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} > .elementor-widget-container > .e-n-menu[data-layout="dropdown"] > .e-n-menu-wrapper > .e-n-menu-heading > .e-n-menu-title:not( .e-current )', + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'selectors' => [ + '{{SELECTOR}}' => 'background: {{VALUE}}', + ], + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'dropdown_menu_item_box_shadow_normal', + 'selector' => '{{WRAPPER}} > .elementor-widget-container > .e-n-menu[data-layout="dropdown"] > .e-n-menu-wrapper > .e-n-menu-heading > .e-n-menu-title:not( .e-current )', + + ] + ); + + $this->end_controls_tab(); // Normal tab end + + $this->start_controls_tab( + 'active_menu_dropdown_states', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( 'dropdown_menu_item_text_color_active', [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-title-active-color-dropdown: {{VALUE}};', + ], + ] ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'dropdown_menu_item_background_color_active', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} > .elementor-widget-container > .e-n-menu[data-layout="dropdown"] > .e-n-menu-wrapper > .e-n-menu-heading > .e-n-menu-title.e-current', + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'dropdown_menu_item_box_shadow_active', + 'selector' => '{{WRAPPER}} > .elementor-widget-container > .e-n-menu[data-layout="dropdown"] > .e-n-menu-wrapper > .e-n-menu-heading > .e-n-menu-title.e-current', + + ] + ); + + $this->end_controls_tab(); // Active tab end + + $this->end_controls_tabs(); + + $this->add_control( + 'menu_dropdown_box_title', + [ + 'label' => esc_html__( 'Dropdown Box', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'menu_dropdown_box_description', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'Style the dropdown box that contains menu items.', 'elementor-pro' ), + 'content_classes' => 'elementor-control-field-description', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'menu_dropdown_box_border', + 'fields_options' => [ + 'border' => [ + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-dropdown-content-box-border-style: {{VALUE}}', + ], + ], + 'color' => [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-dropdown-content-box-border-color: {{VALUE}}', + ], + ], + 'width' => [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'selectors' => [ + '{{WRAPPER}}' => "--n-menu-dropdown-content-box-border-block-start-width: {{TOP}}{{UNIT}}; --n-menu-dropdown-content-box-border-inline-end-width: $logical_dimensions_inline_end; --n-menu-dropdown-content-box-border-block-end-width: {{BOTTOM}}{{UNIT}}; --n-menu-dropdown-content-box-border-inline-start-width: $logical_dimensions_inline_start;", + ], + ], + ], + ] + ); + + $this->add_control( + 'menu_dropdown_box_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-dropdown-content-box-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'menu_dropdown_box_shadow', + 'fields_options' => [ + 'box_shadow' => [ + 'selectors' => [ + '{{WRAPPER}}' => '--n-menu-dropdown-content-box-shadow-horizontal: {{HORIZONTAL}}px; --n-menu-dropdown-content-box-shadow-vertical: {{VERTICAL}}px; --n-menu-dropdown-content-box-shadow-blur: {{BLUR}}px; --n-menu-dropdown-content-box-shadow-spread: {{SPREAD}}px; --n-menu-dropdown-content-box-shadow-color: {{COLOR}}; --n-menu-dropdown-content-box-shadow-position: {{box_shadow_position.VALUE}};', + ], + ], + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + $menu_titles = ''; + $menu_containers = ''; + + foreach ( $settings['menu_items'] as $index => $item ) { + $menu_titles .= $this->render_menu_titles_html( $index, $item ); + + ob_start(); + $item_dropdown_id = 'e-n-menu-dropdown-icon-' . $this->get_widget_number() . ( $index + 1 ); + $this->print_child( $index, 'yes' === $item['item_dropdown_content'], $item_dropdown_id ); + $menu_containers .= ob_get_clean(); + } + + ?> + + add_render_attribute( 'e-n-menu', [ + 'class' => [ 'e-n-menu' ], + 'data-widget-number' => ! empty( $element_uid ) ? $element_uid : $this->get_widget_number(), + 'aria-label' => esc_html__( 'Menu', 'elementor-pro' ), + ] ); + + $this->print_render_attribute_string( 'e-n-menu' ); + } + + protected function render_menu_wrapper_attributes() { + $this->add_render_attribute( 'e-n-menu-wrapper', [ + 'class' => 'e-n-menu-wrapper', + 'id' => 'menubar-' . $this->get_widget_number(), + 'aria-labelledby' => 'menu-toggle-' . $this->get_widget_number(), + ] ); + + $this->print_render_attribute_string( 'e-n-menu-wrapper' ); + } + + protected function render_menu_toggle( $settings ) { + $menu_toggle_hover_animation = ! empty( $settings['menu_toggle_icon_hover_animation'] ) + ? ' elementor-animation-' . $settings['menu_toggle_icon_hover_animation'] + : ''; + + $this->add_render_attribute( 'menu-toggle', [ + 'class' => 'e-n-menu-toggle' . $menu_toggle_hover_animation, + 'id' => 'menu-toggle-' . $this->get_widget_number(), + 'aria-haspopup' => 'true', + 'aria-expanded' => 'false', + 'aria-controls' => 'menubar-' . $this->get_widget_number(), + 'aria-label' => esc_html__( 'Menu Toggle', 'elementor-pro' ), + ] ); + + $open_class = 'e-n-menu-toggle-icon e-open'; + $close_class = 'e-n-menu-toggle-icon e-close'; + + $normal_icon = ! empty( $settings['menu_toggle_icon_normal']['value'] ) + ? $settings['menu_toggle_icon_normal'] + : [ + 'library' => 'eicons', + 'value' => 'eicon-menu-bar', + ]; + + $active_icon = ! empty( $settings['menu_toggle_icon_active']['value'] ) + ? $settings['menu_toggle_icon_active'] + : [ + 'library' => 'eicons', + 'value' => 'eicon-close', + ]; + ?> + + + <# + const menuToggleKey = 'e-n-menu-toggle-' + elementUid, + iconHoverAnimation = !! settings.menu_toggle_icon_hover_animation + ? 'elementor-animation-' + settings.menu_toggle_icon_hover_animation + : '', + openClass = 'e-n-menu-toggle-icon e-open', + closeClass = 'e-n-menu-toggle-icon e-close', + iconNormal = !! settings.menu_toggle_icon_normal.value ? settings.menu_toggle_icon_normal : '', + iconActive = !! settings.menu_toggle_icon_active.value ? settings.menu_toggle_icon_active : ''; + + view.addRenderAttribute( menuToggleKey, { + 'class': [ 'e-n-menu-toggle', 'elementor-clickable', iconHoverAnimation ], + 'id': 'menu-toggle-' + elementUid, + 'aria-haspopup': 'true', + 'aria-expanded': 'false', + 'aria-controls': 'menubar-' + elementUid, + 'aria-label': '', + } ); + #> + + get_current_menu_item_class( $item['item_link']['url'] ); + $items_open_on_click = 'click' === $this->get_settings_for_display( 'open_on' ); + + if ( ! empty( $current_class ) ) { + $classes[] = $current_class; + } + + if ( $items_open_on_click && $this->item_has_dropdown_with_content( $index, $this->get_children(), 'yes' === $item['item_dropdown_content'] ) ) { + $classes[] = 'e-click'; + } + + return array_filter( $classes ); + } + + protected function render_menu_titles_html( $index, $item ) { + $settings = $this->get_settings_for_display(); + $is_focusable_class = 'yes' === $item['item_dropdown_content'] ? 'e-focus' : ''; + $item_class = $this->merge_menu_title_classes( $index, $item, [ 'e-n-menu-title' ] ); + $item_dropdown_class = [ 'e-n-menu-dropdown-icon' ]; + + if ( ! empty( $is_focusable_class ) ) { + $item_dropdown_class[] = $is_focusable_class; + } + + $icon_html = Icons_Manager::try_get_icon_html( $settings['menu_item_icon'], [ 'aria-hidden' => 'true' ] ); + $icon_active_html = Icons_Manager::try_get_icon_html( $settings['menu_item_icon_active'], [ 'aria-hidden' => 'true' ] ); + $display_index = $index + 1; + $has_dropdown_content = 'yes' === $settings['menu_items'][ $index ]['item_dropdown_content']; + $menu_item_id = empty( $item['element_id'] ) ? 'e-n-menu-title-' . $this->get_widget_number() . $display_index : $item['element_id']; + $item_dropdown_id = 'e-n-menu-dropdown-icon-' . $this->get_widget_number() . $display_index; + $key = $this->get_repeater_setting_key( 'item_title', 'menu_items', $display_index ); + $menu_item = $settings['menu_items'][ $index ]; + $menu_item_icon = Icons_Manager::try_get_icon_html( $menu_item['item_icon'], [ 'aria-hidden' => 'true' ] ); + $menu_item_active_icon = $this->is_active_icon_exist( $menu_item ) + ? Icons_Manager::try_get_icon_html( $item['item_icon_active'], [ 'aria-hidden' => 'true' ] ) + : $menu_item_icon; + + if ( ! empty( $settings['hover_animation'] ) ) { + $item_class[] = 'elementor-animation-' . $settings['hover_animation']; + } + + $this->add_attributes_to_item( $key, $item_class, $menu_item_id, $display_index ); + $this->add_attributes_to_item_dropdown( $key . '_link', $item_dropdown_class, $item_dropdown_id, $display_index, $has_dropdown_content, $item['item_title'] ); + + ob_start(); + ?> +
  • get_render_attribute_string( $key ) ); ?> > +
    get_title_container_opening_tag( $item, $item['item_link']['url'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> + + + + + + + get_title_link_opening_tag( $item, $item['item_link']['url'], $display_index ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + + get_title_link_closing_tag( $item['item_link']['url'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> +
    + + + +
  • + add_render_attribute( $key, [ + 'id' => $menu_item_id, + 'class' => $classes, + 'style' => '--n-menu-title-order: ' . $display_index . ';', + ] ); + } + + public function add_attributes_to_item_dropdown( $key, $classes, $item_dropdown_id, $display_index, $has_dropdown_content = false, $title = '' ) { + $this->add_render_attribute( $key, [ + 'id' => $item_dropdown_id, + 'class' => $classes, + 'data-tab-index' => $display_index, + 'aria-haspopup' => $has_dropdown_content ? 'true' : 'false', + 'aria-expanded' => 'false', + 'aria-controls' => 'e-n-menu-content-' . $this->get_widget_number() . $display_index, + ] ); + } + + protected function get_current_menu_item_class( $menu_link_url ) { + $menu_link_url = trim( $menu_link_url ); + + if ( str_contains( $menu_link_url, '#' ) ) { + return 'e-anchor'; + } + + $permalink_url = $this->get_permalink_for_current_page(); + + if ( empty( $menu_link_url ) || empty( $permalink_url ) ) { + return ''; + } + + $permalink_array = $this->parse_url( $permalink_url ); + $menu_item_url_array = $this->parse_url( $menu_link_url ); + $has_equal_urls = $permalink_array === $menu_item_url_array; + + return $has_equal_urls ? 'e-current' : ''; + } + + /** + * Print the content area. + * + * @param int $index + * @param boolean $has_dropdown_content + * @param string $menu_item_id + */ + public function print_child( $index, $has_dropdown_content = false, $menu_item_id = '' ) { + $children = $this->get_children(); + $menu_index = $index + 1; + $child_ids = []; + + foreach ( $children as $child ) { + $child_ids[] = $child->get_id(); + } + + $add_attribute_to_container = function ( $should_render, $container ) use ( $menu_item_id, $menu_index, $child_ids ) { + if ( in_array( $container->get_id(), $child_ids ) ) { + $this->set_container_attributes( $container, $menu_index, $menu_item_id ); + } + + return $should_render; + }; + + if ( $this->item_has_dropdown_with_content( $index, $children, $has_dropdown_content ) ) { + add_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container, 10, 3 ); + + $children[ $index ]->print_element(); + + remove_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container ); + } + } + + protected function set_container_attributes( $container, $menu_index, $menu_item_id ) { + $container->add_render_attribute( '_wrapper', [ + 'id' => 'e-n-menu-content-' . $this->get_widget_number() . $menu_index, + 'data-tab-index' => $menu_index, + 'aria-labelledby' => $menu_item_id, + 'style' => '--n-menu-title-order: ' . $menu_index . ';', + ] ); + } + + protected function item_has_dropdown_with_content( $index, $children, $has_dropdown_content = false ) { + $data = ! empty( $children[ $index ] ) ? $children[ $index ]->get_data() : []; + $elements = empty( $data['elements'] ) ? [] : $data['elements']; + + return ! empty( $children[ $index ] ) && ! empty( $elements ) && $has_dropdown_content; + } + + private function get_title_container_opening_tag( $item, $url ) { + $title_container_id = 'e-n-menu-title-container-' . $item['_id']; + + $this->remove_render_attribute( $title_container_id ); + $this->add_render_attribute( $title_container_id, [ + 'class' => [ 'e-n-menu-title-container' ], + ] ); + + $current_class = $this->get_current_menu_item_class( $url ); + + if ( ! empty( $current_class ) ) { + $this->add_render_attribute( $title_container_id, 'aria-current', 'page' ); + } + + return $this->get_render_attribute_string( $title_container_id ); + } + + private function get_title_link_opening_tag( $item, $url, $display_index ) { + $link_id = 'e-n-menu-title-text-' . $item['_id']; + $link_classes = [ 'e-n-menu-title-text', 'e-link' ]; + + if ( ! empty( $url ) ) { + $link_classes[] = 'e-focus'; + } + + $this->remove_render_attribute( $link_id ); + $this->add_render_attribute( $link_id, [ + 'class' => $link_classes, + ] ); + $this->add_link_attributes( $link_id, $item['item_link'] ); + + $opening_tag = ''; + + $tag_content = $this->get_render_attribute_string( $link_id ); + + if ( ! empty( $url ) ) { + $opening_tag = ''; + } + + return $opening_tag; + } + + private function get_title_link_closing_tag( $url ) { + $closing_tag = ''; + + if ( $url ) { + $closing_tag = ''; + } + + return $closing_tag; + } + + /** + * @param $item + * @return bool + */ + private function is_active_icon_exist( $item ) { + return array_key_exists( 'item_icon_active', $item ) && ! empty( $item['item_icon_active'] ) && ! empty( $item['item_icon_active']['value'] ); + } + + /** + * @param string $state + * @param $css_prefix + * @return void + */ + private function add_dropdown_indicator_state_based_style_controls( string $state ) { + $label = esc_html__( 'Normal', 'elementor-pro' ); + $selector = '--n-menu-dropdown-indicator-color-normal: {{VALUE}};'; + if ( 'hover' === $state ) { + $label = esc_html__( 'Hover', 'elementor-pro' ); + $selector = '--n-menu-dropdown-indicator-color-hover: {{VALUE}};'; + } + if ( 'active' === $state ) { + $label = esc_html__( 'Active', 'elementor-pro' ); + $selector = '--n-menu-dropdown-indicator-color-active: {{VALUE}};'; + } + $this->start_controls_tab('style_menu_dropdown_indicator_' . $state, [ + 'label' => $label, + ]); + + $this->add_control( + 'menu_dropdown_indicator_color_' . $state, + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => $selector, + ], + ] + ); + + $this->end_controls_tab(); + } + + protected function content_template() { + ?> + <# if ( settings['menu_items'] ) { + const menuItemIcon = elementor.helpers.renderIcon( view, settings['menu_item_icon'], { 'aria-hidden': true }, 'i' , 'object' ) ?? '', + menuItemIconActive = elementor.helpers.renderIcon( view, settings['menu_item_icon_active'], { 'aria-hidden': true }, 'i' , 'object' ) ?? '', + elementUid = view.getIDInt().toString().substr( 0, 3 ), + permalinkUrl = 'get_permalink_for_current_page() ); ?>'; + #> + + <# } #> + experiments->is_feature_active( 'e_nested_atomic_repeaters' ) ) { + return array_merge( parent::get_initial_config(), [ + 'support_improved_repeaters' => true, + 'target_container' => [ '.e-n-menu-heading' ], + 'node' => 'li', + ] ); + } + + return parent::get_initial_config(); + } + + protected function content_template_single_repeater_item() { + ?> + <# + const menuItemIcon = elementor.helpers.renderIcon( view, view.container.settings.attributes.menu_item_icon, { 'aria-hidden': true }, 'i' , 'object' ) ?? '', + menuItemIconActive = elementor.helpers.renderIcon( view, view.container.settings.attributes.menu_item_icon_active, { 'aria-hidden': true }, 'i' , 'object' ) ?? '', + elementUid = view.getIDInt().toString().substr( 0, 3 ), + permalinkUrl = 'get_permalink_for_current_page() ); ?>', + menuItemCount = view.collection.length + 1, + menuItemUid = view.getIDInt().toString().substr( 0, 3 ) + menuItemCount, + menuItemWrapperKey = menuItemUid, + menuItemTitleKey = 'menu-title-' + menuItemUid, + menuItemTitleContainerLinkKey = 'e-n-menu-title-container-' + menuItemUid, + menuItemDropdownIconKey = 'e-n-menu-dropdown-icon-' + menuItemUid, + menuItemIconKey = 'menu-icon-' + menuItemUid, + menuIcon = elementor.helpers.renderIcon( view, data.item_icon, { 'aria-hidden': true }, 'i' , 'object' ) ?? '', + menuIconActive = '' === data.item_icon_active.value + ? menuIcon + : elementor.helpers.renderIcon( view, data.item_icon_active, { 'aria-hidden': true }, 'i' , 'object' ), + menuItemLink = 'string' === typeof data['item_link'] ? data['item_link'] : data['item_link']['url'], + hasDropdownContent = 'yes' === data['item_dropdown_content'], + currentPageClass = elementorPro.modules.megaMenu.getCurrentMenuItemClass( menuItemLink, permalinkUrl ), + dropdownFocusClass = hasDropdownContent ? 'e-focus' : '', + menuItemClassList = ['e-n-menu-title']; + let menuItemId = 'e-n-menu-title-' + menuItemUid, + menuItemLinkClasses = [ 'e-n-menu-title-text' ]; + + if ( '' !== data.element_id ) { + menuItemId = data.element_id; + } + + if ( ! hasDropdownContent ) { + menuItemClassList.push( 'link-only' ); + } else { + menuItemClassList.push( 'e-click' ); + } + + if ( !! currentPageClass ) { + menuItemClassList.push( currentPageClass ); + } + + view.addRenderAttribute( menuItemWrapperKey, { + 'id': menuItemId, + 'class': menuItemClassList, + 'style': '--n-menu-title-order: ' + menuItemCount + ';', + }, null, true ); + + if ( !! data.item_link.url ) { + menuItemLinkClasses.push( 'e-link' ); + menuItemLinkClasses.push( 'e-focus' ); + } + + view.addRenderAttribute( menuItemTitleKey, { + 'class': menuItemLinkClasses, + 'href': data.item_link.url, + 'data-binding-type': 'repeater-item', + 'data-binding-repeater-name': 'menu_items', + 'data-binding-setting': ['item_title'], + 'data-binding-index': menuItemCount, + }, null, true ); + + view.addRenderAttribute( menuItemTitleContainerLinkKey, { + 'class': [ 'e-n-menu-title-container' ], + 'aria-current': 'page', + }, null, true ); + + view.addRenderAttribute( menuItemDropdownIconKey, { + 'id': 'e-n-menu-dropdown-icon-' + menuItemUid, + 'class': [ 'e-n-menu-dropdown-icon', 'e-focus' ], + 'data-tab-index': menuItemCount, + 'aria-haspopup': hasDropdownContent ? 'true' : 'false', + 'aria-expanded': 'false', + 'aria-controls': 'e-n-menu-content-' + menuItemUid, + }, null, true ); + #> +
  • +
    + + <# if (menuIcon.value) { #> + + {{{ menuIconActive.value }}} + {{{ menuIcon.value }}} + + <# } #> + + <# if ( menuItemLink ) { #> + + <# } else { #> + + <# } #> + + {{{ data.item_title }}} + + <# if ( menuItemLink ) { #> + + <# } else { #> + + <# } #> +
    + + <# if ( hasDropdownContent ) { #> + + <# } #> +
  • + [ + 'label' => esc_html__( 'Scrolling Effects', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'render_type' => 'ui', + 'frontend_available' => true, + ], + ]; + + $this->prepare_effects( 'scrolling', $fields ); + + $transform_origin_conditions = [ + 'terms' => [ + [ + 'name' => 'motion_fx_scrolling', + 'value' => 'yes', + ], + [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'rotateZ_effect', + 'value' => 'yes', + ], + [ + 'name' => 'scale_effect', + 'value' => 'yes', + ], + ], + ], + ], + ]; + + $fields['transform_origin_x'] = [ + 'label' => esc_html__( 'X Anchor Point', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'center', + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-h-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'conditions' => $transform_origin_conditions, + 'toggle' => false, + 'render_type' => 'ui', + 'selectors' => [ + '{{SELECTOR}}' => '--e-transform-origin-x: {{VALUE}}', + ], + ]; + + $fields['transform_origin_y'] = [ + 'label' => esc_html__( 'Y Anchor Point', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'center', + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'conditions' => $transform_origin_conditions, + 'selectors' => [ + '{{SELECTOR}}' => '--e-transform-origin-y: {{VALUE}}', + ], + 'toggle' => false, + ]; + + // TODO: Once Core 3.4.0 is out, get the active devices using Breakpoints/Manager::get_active_devices_list(). + $active_breakpoint_instances = Plugin::elementor()->breakpoints->get_active_breakpoints(); + // Devices need to be ordered from largest to smallest. + $active_devices = array_reverse( array_keys( $active_breakpoint_instances ) ); + + // Add desktop in the correct position. + if ( in_array( 'widescreen', $active_devices, true ) ) { + $active_devices = array_merge( array_slice( $active_devices, 0, 1 ), [ 'desktop' ], array_slice( $active_devices, 1 ) ); + } else { + $active_devices = array_merge( [ 'desktop' ], $active_devices ); + } + + $devices_options = []; + + foreach ( $active_devices as $device_key ) { + $device_label = 'desktop' === $device_key ? esc_html__( 'Desktop', 'elementor-pro' ) : $active_breakpoint_instances[ $device_key ]->get_label(); + + $devices_options[ $device_key ] = $device_label; + } + + $fields['devices'] = [ + 'label' => esc_html__( 'Apply Effects On', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'label_block' => true, + 'default' => $active_devices, + 'options' => $devices_options, + 'condition' => [ + 'motion_fx_scrolling' => 'yes', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ]; + + $fields['range'] = [ + 'label' => esc_html__( 'Effects Relative To', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Default', 'elementor-pro' ), + 'viewport' => esc_html__( 'Viewport', 'elementor-pro' ), + 'page' => esc_html__( 'Entire Page', 'elementor-pro' ), + ], + 'condition' => [ + 'motion_fx_scrolling' => 'yes', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ]; + + $fields['motion_fx_mouse'] = [ + 'label' => esc_html__( 'Mouse Effects', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'separator' => 'before', + 'render_type' => 'none', + 'frontend_available' => true, + ]; + + $this->prepare_effects( 'mouse', $fields ); + + return $fields; + } + + protected function get_default_options() { + return [ + 'popover' => false, + ]; + } + + private function get_scrolling_effects() { + return [ + 'translateY' => [ + 'label' => esc_html__( 'Vertical Scroll', 'elementor-pro' ), + 'fields' => [ + 'direction' => [ + 'label' => esc_html__( 'Direction', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Up', 'elementor-pro' ), + 'negative' => esc_html__( 'Down', 'elementor-pro' ), + ], + ], + 'speed' => [ + 'label' => esc_html__( 'Speed', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 4, + ], + 'range' => [ + 'px' => [ + 'max' => 10, + 'step' => 0.1, + ], + ], + ], + 'affectedRange' => [ + 'label' => esc_html__( 'Viewport', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'sizes' => [ + 'start' => 0, + 'end' => 100, + ], + 'unit' => '%', + ], + 'labels' => [ + __( 'Bottom', 'elementor-pro' ), + __( 'Top', 'elementor-pro' ), + ], + 'scales' => 1, + 'handles' => 'range', + ], + ], + ], + 'translateX' => [ + 'label' => esc_html__( 'Horizontal Scroll', 'elementor-pro' ), + 'fields' => [ + 'direction' => [ + 'label' => esc_html__( 'Direction', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'To Left', 'elementor-pro' ), + 'negative' => esc_html__( 'To Right', 'elementor-pro' ), + ], + ], + 'speed' => [ + 'label' => esc_html__( 'Speed', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 4, + ], + 'range' => [ + 'px' => [ + 'max' => 10, + 'step' => 0.1, + ], + ], + ], + 'affectedRange' => [ + 'label' => esc_html__( 'Viewport', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'sizes' => [ + 'start' => 0, + 'end' => 100, + ], + 'unit' => '%', + ], + 'labels' => [ + __( 'Bottom', 'elementor-pro' ), + __( 'Top', 'elementor-pro' ), + ], + 'scales' => 1, + 'handles' => 'range', + ], + ], + ], + 'opacity' => [ + 'label' => esc_html__( 'Transparency', 'elementor-pro' ), + 'fields' => [ + 'direction' => [ + 'label' => esc_html__( 'Direction', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'out-in', + 'options' => [ + 'out-in' => 'Fade In', + 'in-out' => 'Fade Out', + 'in-out-in' => 'Fade Out In', + 'out-in-out' => 'Fade In Out', + ], + ], + 'level' => [ + 'label' => esc_html__( 'Level', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 10, + ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 10, + 'step' => 0.1, + ], + ], + ], + 'range' => [ + 'label' => esc_html__( 'Viewport', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'sizes' => [ + 'start' => 20, + 'end' => 80, + ], + 'unit' => '%', + ], + 'labels' => [ + __( 'Bottom', 'elementor-pro' ), + __( 'Top', 'elementor-pro' ), + ], + 'scales' => 1, + 'handles' => 'range', + ], + ], + ], + 'blur' => [ + 'label' => esc_html__( 'Blur', 'elementor-pro' ), + 'fields' => [ + 'direction' => [ + 'label' => esc_html__( 'Direction', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'out-in', + 'options' => [ + 'out-in' => 'Fade In', + 'in-out' => 'Fade Out', + 'in-out-in' => 'Fade Out In', + 'out-in-out' => 'Fade In Out', + ], + ], + 'level' => [ + 'label' => esc_html__( 'Level', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 7, + ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 15, + ], + ], + ], + 'range' => [ + 'label' => esc_html__( 'Viewport', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'sizes' => [ + 'start' => 20, + 'end' => 80, + ], + 'unit' => '%', + ], + 'labels' => [ + __( 'Bottom', 'elementor-pro' ), + __( 'Top', 'elementor-pro' ), + ], + 'scales' => 1, + 'handles' => 'range', + ], + ], + ], + 'rotateZ' => [ + 'label' => esc_html__( 'Rotate', 'elementor-pro' ), + 'fields' => [ + 'direction' => [ + 'label' => esc_html__( 'Direction', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'To Left', 'elementor-pro' ), + 'negative' => esc_html__( 'To Right', 'elementor-pro' ), + ], + ], + 'speed' => [ + 'label' => esc_html__( 'Speed', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 1, + ], + 'range' => [ + 'px' => [ + 'max' => 10, + 'step' => 0.1, + ], + ], + ], + 'affectedRange' => [ + 'label' => esc_html__( 'Viewport', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'sizes' => [ + 'start' => 0, + 'end' => 100, + ], + 'unit' => '%', + ], + 'labels' => [ + __( 'Bottom', 'elementor-pro' ), + __( 'Top', 'elementor-pro' ), + ], + 'scales' => 1, + 'handles' => 'range', + ], + ], + ], + 'scale' => [ + 'label' => esc_html__( 'Scale', 'elementor-pro' ), + 'fields' => [ + 'direction' => [ + 'label' => esc_html__( 'Direction', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'out-in', + 'options' => [ + 'out-in' => 'Scale Up', + 'in-out' => 'Scale Down', + 'in-out-in' => 'Scale Down Up', + 'out-in-out' => 'Scale Up Down', + ], + ], + 'speed' => [ + 'label' => esc_html__( 'Speed', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 4, + ], + 'range' => [ + 'px' => [ + 'min' => -10, + 'max' => 10, + ], + ], + ], + 'range' => [ + 'label' => esc_html__( 'Viewport', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'sizes' => [ + 'start' => 20, + 'end' => 80, + ], + 'unit' => '%', + ], + 'labels' => [ + __( 'Bottom', 'elementor-pro' ), + __( 'Top', 'elementor-pro' ), + ], + 'scales' => 1, + 'handles' => 'range', + ], + ], + ], + ]; + } + + private function get_mouse_effects() { + return [ + 'mouseTrack' => [ + 'label' => esc_html__( 'Mouse Track', 'elementor-pro' ), + 'fields' => [ + 'direction' => [ + 'label' => esc_html__( 'Direction', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'Opposite', 'elementor-pro' ), + 'negative' => esc_html__( 'Direct', 'elementor-pro' ), + ], + ], + 'speed' => [ + 'label' => esc_html__( 'Speed', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 1, + ], + 'range' => [ + 'px' => [ + 'max' => 10, + 'step' => 0.1, + ], + ], + ], + ], + ], + 'tilt' => [ + 'label' => esc_html__( '3D Tilt', 'elementor-pro' ), + 'fields' => [ + 'direction' => [ + 'label' => esc_html__( 'Direction', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'Direct', 'elementor-pro' ), + 'negative' => esc_html__( 'Opposite', 'elementor-pro' ), + ], + ], + 'speed' => [ + 'label' => esc_html__( 'Speed', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 4, + ], + 'range' => [ + 'px' => [ + 'max' => 10, + 'step' => 0.1, + ], + ], + ], + ], + ], + ]; + } + + private function prepare_effects( $effects_group, array &$fields ) { + $method_name = "get_{$effects_group}_effects"; + + $effects = $this->$method_name(); + + foreach ( $effects as $effect_name => $effect_args ) { + $args = [ + 'label' => $effect_args['label'], + 'type' => Controls_Manager::POPOVER_TOGGLE, + 'condition' => [ + 'motion_fx_' . $effects_group => 'yes', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ]; + + if ( ! empty( $effect_args['separator'] ) ) { + $args['separator'] = $effect_args['separator']; + } + + $fields[ $effect_name . '_effect' ] = $args; + + $effect_fields = $effect_args['fields']; + + $first_field = & $effect_fields[ key( $effect_fields ) ]; + + $first_field['popover']['start'] = true; + + end( $effect_fields ); + + $last_field = & $effect_fields[ key( $effect_fields ) ]; + + $last_field['popover']['end'] = true; + + reset( $effect_fields ); + + foreach ( $effect_fields as $field_name => $field ) { + $field = array_merge( $field, [ + 'condition' => [ + 'motion_fx_' . $effects_group => 'yes', + $effect_name . '_effect' => 'yes', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ] ); + + $fields[ $effect_name . '_' . $field_name ] = $field; + } + } + } +} diff --git a/modules/motion-fx/module.php b/modules/motion-fx/module.php new file mode 100644 index 0000000..721061c --- /dev/null +++ b/modules/motion-fx/module.php @@ -0,0 +1,141 @@ +add_actions(); + } + /** + * Get module name. + * + * Retrieve the module name. + * + * @since 2.5.0 + * @access public + * + * @return string Module name. + */ + public function get_name() { + return 'motion-fx'; + } + + public function register_controls_group( Controls_Manager $controls_manager ) { + $controls_manager->add_group_control( Controls_Group::get_type(), new Controls_Group() ); + } + + public function add_controls_group_to_element( Element_Base $element ) { + $exclude = []; + + $selector = '{{WRAPPER}}'; + + if ( $element instanceof Element_Section ) { + $exclude[] = 'motion_fx_mouse'; + } elseif ( $element instanceof Element_Column ) { + $selector .= ' > .elementor-widget-wrap'; + } elseif ( $element instanceof Widget_Base ) { + $selector .= ' > .elementor-widget-container'; + } + + $element->add_group_control( + Controls_Group::get_type(), + [ + 'name' => 'motion_fx', + 'selector' => $selector, + 'exclude' => $exclude, + ] + ); + } + + public function add_controls_group_to_element_background( Element_Base $element ) { + $element->start_injection( [ + 'of' => 'background_bg_width_mobile', + ] ); + + $element->add_group_control( + Controls_Group::get_type(), + [ + 'name' => 'background_motion_fx', + 'exclude' => [ + 'rotateZ_effect', + 'tilt_effect', + 'transform_origin_x', + 'transform_origin_y', + ], + ] + ); + + $options = [ + 'separator' => 'before', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'background_background', + 'value' => 'classic', + ], + [ + 'terms' => [ + [ + 'name' => 'background_background', + 'value' => 'gradient', + ], + [ + 'name' => 'background_color', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'background_color_b', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ]; + + $element->update_control( 'background_motion_fx_motion_fx_scrolling', $options ); + + $element->update_control( 'background_motion_fx_motion_fx_mouse', $options ); + + $element->end_injection(); + } + + /** + * @deprecated 3.1.0 + */ + public function localize_settings() { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); + + return []; + } + + private function add_actions() { + add_action( 'elementor/controls/register', [ $this, 'register_controls_group' ] ); + + add_action( 'elementor/element/section/section_effects/after_section_start', [ $this, 'add_controls_group_to_element' ] ); + add_action( 'elementor/element/container/section_effects/after_section_start', [ $this, 'add_controls_group_to_element' ] ); + add_action( 'elementor/element/column/section_effects/after_section_start', [ $this, 'add_controls_group_to_element' ] ); + add_action( 'elementor/element/common/section_effects/after_section_start', [ $this, 'add_controls_group_to_element' ] ); + + add_action( 'elementor/element/section/section_background/before_section_end', [ $this, 'add_controls_group_to_element_background' ] ); + add_action( 'elementor/element/container/section_background/before_section_end', [ $this, 'add_controls_group_to_element_background' ] ); + add_action( 'elementor/element/column/section_style/before_section_end', [ $this, 'add_controls_group_to_element_background' ] ); + } +} diff --git a/modules/nav-menu/module.php b/modules/nav-menu/module.php new file mode 100644 index 0000000..bd890eb --- /dev/null +++ b/modules/nav-menu/module.php @@ -0,0 +1,21 @@ +nav_menu_index++; + } + + private function get_available_menus() { + $menus = wp_get_nav_menus(); + + $options = []; + + foreach ( $menus as $menu ) { + $options[ $menu->slug ] = $menu->name; + } + + return $options; + } + + protected function register_controls() { + + $this->start_controls_section( + 'section_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + ] + ); + + $menus = $this->get_available_menus(); + + if ( ! empty( $menus ) ) { + $this->add_control( + 'menu', + [ + 'label' => esc_html__( 'Menu', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $menus, + 'default' => array_keys( $menus )[0], + 'save_default' => true, + 'separator' => 'after', + 'description' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'Go to the %1$sMenus screen%2$s to manage your menus.', 'elementor-pro' ), + sprintf( '', admin_url( 'nav-menus.php' ) ), + '' + ), + ] + ); + } else { + $this->add_control( + 'menu', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'heading' => esc_html__( 'There are no menus in your site.', 'elementor-pro' ), + 'content' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'Go to the %1$sMenus screen%2$s to create one.', 'elementor-pro' ), + sprintf( '', admin_url( 'nav-menus.php?action=edit&menu=0' ) ), + '' + ), + 'separator' => 'after', + ] + ); + } + + $this->add_control( + 'layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'horizontal', + 'options' => [ + 'horizontal' => esc_html__( 'Horizontal', 'elementor-pro' ), + 'vertical' => esc_html__( 'Vertical', 'elementor-pro' ), + 'dropdown' => esc_html__( 'Dropdown', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $start = is_rtl() ? 'end' : 'start'; + $end = is_rtl() ? 'start' : 'end'; + + $this->add_control( + 'align_items', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => "eicon-align-$start-h", + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-align-center-h', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => "eicon-align-$end-h", + ], + 'justify' => [ + 'title' => esc_html__( 'Stretch', 'elementor-pro' ), + 'icon' => 'eicon-align-stretch-h', + ], + ], + // For BC + 'classes_dictionary' => [ + 'left' => is_rtl() ? 'end' : 'start', + 'right' => is_rtl() ? 'start' : 'end', + ], + 'prefix_class' => 'elementor-nav-menu__align-', + 'condition' => [ + 'layout!' => 'dropdown', + ], + ] + ); + + $this->add_control( + 'pointer', + [ + 'label' => esc_html__( 'Pointer', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'underline', + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'underline' => esc_html__( 'Underline', 'elementor-pro' ), + 'overline' => esc_html__( 'Overline', 'elementor-pro' ), + 'double-line' => esc_html__( 'Double Line', 'elementor-pro' ), + 'framed' => esc_html__( 'Framed', 'elementor-pro' ), + 'background' => esc_html__( 'Background', 'elementor-pro' ), + 'text' => esc_html__( 'Text', 'elementor-pro' ), + ], + 'style_transfer' => true, + 'condition' => [ + 'layout!' => 'dropdown', + ], + ] + ); + + $this->add_control( + 'animation_line', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'fade', + 'options' => [ + 'fade' => 'Fade', + 'slide' => 'Slide', + 'grow' => 'Grow', + 'drop-in' => 'Drop In', + 'drop-out' => 'Drop Out', + 'none' => 'None', + ], + 'condition' => [ + 'layout!' => 'dropdown', + 'pointer' => [ 'underline', 'overline', 'double-line' ], + ], + ] + ); + + $this->add_control( + 'animation_framed', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'fade', + 'options' => [ + 'fade' => 'Fade', + 'grow' => 'Grow', + 'shrink' => 'Shrink', + 'draw' => 'Draw', + 'corners' => 'Corners', + 'none' => 'None', + ], + 'condition' => [ + 'layout!' => 'dropdown', + 'pointer' => 'framed', + ], + ] + ); + + $this->add_control( + 'animation_background', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'fade', + 'options' => [ + 'fade' => 'Fade', + 'grow' => 'Grow', + 'shrink' => 'Shrink', + 'sweep-left' => 'Sweep Left', + 'sweep-right' => 'Sweep Right', + 'sweep-up' => 'Sweep Up', + 'sweep-down' => 'Sweep Down', + 'shutter-in-vertical' => 'Shutter In Vertical', + 'shutter-out-vertical' => 'Shutter Out Vertical', + 'shutter-in-horizontal' => 'Shutter In Horizontal', + 'shutter-out-horizontal' => 'Shutter Out Horizontal', + 'none' => 'None', + ], + 'condition' => [ + 'layout!' => 'dropdown', + 'pointer' => 'background', + ], + ] + ); + + $this->add_control( + 'animation_text', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'grow', + 'options' => [ + 'grow' => 'Grow', + 'shrink' => 'Shrink', + 'sink' => 'Sink', + 'float' => 'Float', + 'skew' => 'Skew', + 'rotate' => 'Rotate', + 'none' => 'None', + ], + 'condition' => [ + 'layout!' => 'dropdown', + 'pointer' => 'text', + ], + ] + ); + + $icon_prefix = Icons_Manager::is_migration_allowed() ? 'fas ' : 'fa '; + + $this->add_control( + 'submenu_icon', + [ + 'label' => esc_html__( 'Submenu Indicator', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'separator' => 'before', + 'default' => [ + 'value' => $icon_prefix . 'fa-caret-down', + 'library' => 'fa-solid', + ], + 'recommended' => [ + 'fa-solid' => [ + 'chevron-down', + 'angle-down', + 'caret-down', + 'plus', + ], + ], + 'label_block' => false, + 'skin' => 'inline', + 'exclude_inline_options' => [ 'svg' ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'heading_mobile_dropdown', + [ + 'label' => esc_html__( 'Mobile Dropdown', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'layout!' => 'dropdown', + ], + ] + ); + + // TODO: For Pro 3.6.0, convert this to the breakpoints utility method introduced in core 3.5.0. + $breakpoints = Plugin::elementor()->breakpoints->get_active_breakpoints(); + $dropdown_options = []; + $excluded_breakpoints = [ + 'laptop', + 'widescreen', + ]; + + foreach ( $breakpoints as $breakpoint_key => $breakpoint_instance ) { + // Do not include laptop and widscreen in the options since this feature is for mobile devices. + if ( in_array( $breakpoint_key, $excluded_breakpoints, true ) ) { + continue; + } + + $dropdown_options[ $breakpoint_key ] = sprintf( + /* translators: 1: Breakpoint label, 2: `>` character, 3: Breakpoint value. */ + esc_html__( '%1$s (%2$s %3$dpx)', 'elementor-pro' ), + $breakpoint_instance->get_label(), + '>', + $breakpoint_instance->get_value() + ); + } + + $dropdown_options['none'] = esc_html__( 'None', 'elementor-pro' ); + + $this->add_control( + 'dropdown', + [ + 'label' => esc_html__( 'Breakpoint', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'tablet', + 'options' => $dropdown_options, + 'prefix_class' => 'elementor-nav-menu--dropdown-', + 'condition' => [ + 'layout!' => 'dropdown', + ], + ] + ); + + $this->add_control( + 'full_width', + [ + 'label' => esc_html__( 'Full Width', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'description' => esc_html__( 'Stretch the dropdown of the menu to full width.', 'elementor-pro' ), + 'prefix_class' => 'elementor-nav-menu--', + 'return_value' => 'stretch', + 'frontend_available' => true, + 'condition' => [ + 'dropdown!' => 'none', + ], + ] + ); + + $this->add_control( + 'text_align', + [ + 'label' => esc_html__( 'Text Align', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'aside', + 'options' => [ + 'aside' => esc_html__( 'Aside', 'elementor-pro' ), + 'center' => esc_html__( 'Center', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-nav-menu__text-align-', + 'condition' => [ + 'dropdown!' => 'none', + ], + ] + ); + + $this->add_control( + 'toggle', + [ + 'label' => esc_html__( 'Toggle Button', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'burger', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'burger' => esc_html__( 'Hamburger', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-nav-menu--toggle elementor-nav-menu--', + 'render_type' => 'template', + 'frontend_available' => true, + 'condition' => [ + 'dropdown!' => 'none', + ], + ] + ); + + $this->start_controls_tabs( 'nav_icon_options' ); + + $this->start_controls_tab( 'nav_icon_normal_options', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + 'condition' => [ + 'toggle' => 'burger', + ], + ] ); + + $this->add_control( + 'toggle_icon_normal', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'skin' => 'inline', + 'label_block' => false, + 'skin_settings' => [ + 'inline' => [ + 'none' => [ + 'label' => esc_html__( 'Default', 'elementor-pro' ), + 'icon' => 'eicon-menu-bar', + ], + 'icon' => [ + 'icon' => 'eicon-star', + ], + ], + ], + 'recommended' => [ + 'fa-solid' => [ + 'plus-square', + 'plus', + 'plus-circle', + 'bars', + ], + 'fa-regular' => [ + 'plus-square', + ], + ], + 'condition' => [ + 'toggle' => 'burger', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'nav_icon_hover_options', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + 'condition' => [ + 'toggle' => 'burger', + ], + ] ); + + $this->add_control( + 'toggle_icon_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + 'frontend_available' => true, + 'render_type' => 'template', + 'condition' => [ + 'toggle' => 'burger', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'nav_icon_active_options', [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + 'condition' => [ + 'toggle' => 'burger', + ], + ] ); + + $this->add_control( + 'toggle_icon_active', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'skin' => 'inline', + 'label_block' => false, + 'skin_settings' => [ + 'inline' => [ + 'none' => [ + 'label' => esc_html__( 'Default', 'elementor-pro' ), + 'icon' => 'eicon-close', + ], + 'icon' => [ + 'icon' => 'eicon-star', + ], + ], + ], + 'recommended' => [ + 'fa-solid' => [ + 'window-close', + 'times-circle', + 'times', + 'minus-square', + 'minus-circle', + 'minus', + ], + 'fa-regular' => [ + 'window-close', + 'times-circle', + 'minus-square', + ], + ], + 'condition' => [ + 'toggle' => 'burger', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'toggle_align', + [ + 'label' => esc_html__( 'Toggle Align', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'center', + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-h-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'selectors_dictionary' => [ + 'left' => 'margin-right: auto', + 'center' => 'margin: 0 auto', + 'right' => 'margin-left: auto', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-toggle' => '{{VALUE}}', + ], + 'condition' => [ + 'toggle!' => '', + 'dropdown!' => 'none', + ], + 'separator' => 'before', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_main-menu', + [ + 'label' => esc_html__( 'Main Menu', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'layout!' => 'dropdown', + ], + + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'menu_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-nav-menu .elementor-item', + ] + ); + + $this->start_controls_tabs( 'tabs_menu_item_style' ); + + $this->start_controls_tab( + 'tab_menu_item_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'color_menu_item', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--main .elementor-item' => 'color: {{VALUE}}; fill: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_menu_item_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'color_menu_item_hover', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--main .elementor-item:hover, + {{WRAPPER}} .elementor-nav-menu--main .elementor-item.elementor-item-active, + {{WRAPPER}} .elementor-nav-menu--main .elementor-item.highlighted, + {{WRAPPER}} .elementor-nav-menu--main .elementor-item:focus' => 'color: {{VALUE}}; fill: {{VALUE}};', + ], + 'condition' => [ + 'pointer!' => 'background', + ], + ] + ); + + $this->add_control( + 'color_menu_item_hover_pointer_bg', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#fff', + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--main .elementor-item:hover, + {{WRAPPER}} .elementor-nav-menu--main .elementor-item.elementor-item-active, + {{WRAPPER}} .elementor-nav-menu--main .elementor-item.highlighted, + {{WRAPPER}} .elementor-nav-menu--main .elementor-item:focus' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'pointer' => 'background', + ], + ] + ); + + $this->add_control( + 'pointer_color_menu_item_hover', + [ + 'label' => esc_html__( 'Pointer Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--main:not(.e--pointer-framed) .elementor-item:before, + {{WRAPPER}} .elementor-nav-menu--main:not(.e--pointer-framed) .elementor-item:after' => 'background-color: {{VALUE}}', + '{{WRAPPER}} .e--pointer-framed .elementor-item:before, + {{WRAPPER}} .e--pointer-framed .elementor-item:after' => 'border-color: {{VALUE}}', + ], + 'condition' => [ + 'pointer!' => [ 'none', 'text' ], + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_menu_item_active', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'color_menu_item_active', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--main .elementor-item.elementor-item-active' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'pointer_color_menu_item_active', + [ + 'label' => esc_html__( 'Pointer Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--main:not(.e--pointer-framed) .elementor-item.elementor-item-active:before, + {{WRAPPER}} .elementor-nav-menu--main:not(.e--pointer-framed) .elementor-item.elementor-item-active:after' => 'background-color: {{VALUE}}', + '{{WRAPPER}} .e--pointer-framed .elementor-item.elementor-item-active:before, + {{WRAPPER}} .e--pointer-framed .elementor-item.elementor-item-active:after' => 'border-color: {{VALUE}}', + ], + 'condition' => [ + 'pointer!' => [ 'none', 'text' ], + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $divider_condition = [ + 'nav_menu_divider' => 'yes', + 'layout' => 'horizontal', + ]; + + $this->add_control( + 'nav_menu_divider', + [ + 'label' => esc_html__( 'Divider', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'condition' => [ + 'layout' => 'horizontal', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-nav-menu-divider-content: "";', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'nav_menu_divider_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'solid' => esc_html__( 'Solid', 'elementor-pro' ), + 'double' => esc_html__( 'Double', 'elementor-pro' ), + 'dotted' => esc_html__( 'Dotted', 'elementor-pro' ), + 'dashed' => esc_html__( 'Dashed', 'elementor-pro' ), + ], + 'default' => 'solid', + 'condition' => $divider_condition, + 'selectors' => [ + '{{WRAPPER}}' => '--e-nav-menu-divider-style: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'nav_menu_divider_weight', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'condition' => $divider_condition, + 'selectors' => [ + '{{WRAPPER}}' => '--e-nav-menu-divider-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'nav_menu_divider_height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + '%' => [ + 'min' => 1, + 'max' => 100, + ], + ], + 'condition' => $divider_condition, + 'selectors' => [ + '{{WRAPPER}}' => '--e-nav-menu-divider-height: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'nav_menu_divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'condition' => $divider_condition, + 'selectors' => [ + '{{WRAPPER}}' => '--e-nav-menu-divider-color: {{VALUE}}', + ], + ] + ); + + /* This control is required to handle with complicated conditions */ + $this->add_control( + 'hr', + [ + 'type' => Controls_Manager::DIVIDER, + ] + ); + + $this->add_responsive_control( + 'pointer_width', + [ + 'label' => esc_html__( 'Pointer Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 30, + ], + 'em' => [ + 'max' => 3, + ], + 'rem' => [ + 'max' => 3, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .e--pointer-framed .elementor-item:before' => 'border-width: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .e--pointer-framed.e--animation-draw .elementor-item:before' => 'border-width: 0 0 {{SIZE}}{{UNIT}} {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .e--pointer-framed.e--animation-draw .elementor-item:after' => 'border-width: {{SIZE}}{{UNIT}} {{SIZE}}{{UNIT}} 0 0', + '{{WRAPPER}} .e--pointer-framed.e--animation-corners .elementor-item:before' => 'border-width: {{SIZE}}{{UNIT}} 0 0 {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .e--pointer-framed.e--animation-corners .elementor-item:after' => 'border-width: 0 {{SIZE}}{{UNIT}} {{SIZE}}{{UNIT}} 0', + '{{WRAPPER}} .e--pointer-underline .elementor-item:after, + {{WRAPPER}} .e--pointer-overline .elementor-item:before, + {{WRAPPER}} .e--pointer-double-line .elementor-item:before, + {{WRAPPER}} .e--pointer-double-line .elementor-item:after' => 'height: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'pointer' => [ 'underline', 'overline', 'double-line', 'framed' ], + ], + ] + ); + + $this->add_responsive_control( + 'padding_horizontal_menu_item', + [ + 'label' => esc_html__( 'Horizontal Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--main .elementor-item' => 'padding-left: {{SIZE}}{{UNIT}}; padding-right: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'padding_vertical_menu_item', + [ + 'label' => esc_html__( 'Vertical Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--main .elementor-item' => 'padding-top: {{SIZE}}{{UNIT}}; padding-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'menu_space_between', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-nav-menu-horizontal-menu-item-margin: calc( {{SIZE}}{{UNIT}} / 2 );', + '{{WRAPPER}} .elementor-nav-menu--main:not(.elementor-nav-menu--layout-horizontal) .elementor-nav-menu > li:not(:last-child)' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'border_radius_menu_item', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-item:before' => 'border-radius: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .e--animation-shutter-in-horizontal .elementor-item:before' => 'border-radius: {{SIZE}}{{UNIT}} {{SIZE}}{{UNIT}} 0 0', + '{{WRAPPER}} .e--animation-shutter-in-horizontal .elementor-item:after' => 'border-radius: 0 0 {{SIZE}}{{UNIT}} {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .e--animation-shutter-in-vertical .elementor-item:before' => 'border-radius: 0 {{SIZE}}{{UNIT}} {{SIZE}}{{UNIT}} 0', + '{{WRAPPER}} .e--animation-shutter-in-vertical .elementor-item:after' => 'border-radius: {{SIZE}}{{UNIT}} 0 0 {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'pointer' => 'background', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_dropdown', + [ + 'label' => esc_html__( 'Dropdown', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'dropdown_description', + [ + 'raw' => esc_html__( 'On desktop, this will affect the submenu. On mobile, this will affect the entire menu.', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + ] + ); + + $this->start_controls_tabs( 'tabs_dropdown_item_style' ); + + $this->start_controls_tab( + 'tab_dropdown_item_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'color_dropdown_item', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--dropdown a, {{WRAPPER}} .elementor-menu-toggle' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'background_color_dropdown_item', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--dropdown' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_dropdown_item_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'color_dropdown_item_hover', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--dropdown a:hover, + {{WRAPPER}} .elementor-nav-menu--dropdown a.elementor-item-active, + {{WRAPPER}} .elementor-nav-menu--dropdown a.highlighted, + {{WRAPPER}} .elementor-menu-toggle:hover' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'background_color_dropdown_item_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--dropdown a:hover, + {{WRAPPER}} .elementor-nav-menu--dropdown a.elementor-item-active, + {{WRAPPER}} .elementor-nav-menu--dropdown a.highlighted' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_dropdown_item_active', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'color_dropdown_item_active', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--dropdown a.elementor-item-active' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'background_color_dropdown_item_active', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--dropdown a.elementor-item-active' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'dropdown_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'exclude' => [ 'line_height' ], + 'selector' => '{{WRAPPER}} .elementor-nav-menu--dropdown .elementor-item, {{WRAPPER}} .elementor-nav-menu--dropdown .elementor-sub-item', + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'dropdown_border', + 'selector' => '{{WRAPPER}} .elementor-nav-menu--dropdown', + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'dropdown_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--dropdown' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + '{{WRAPPER}} .elementor-nav-menu--dropdown li:first-child a' => 'border-top-left-radius: {{TOP}}{{UNIT}}; border-top-right-radius: {{RIGHT}}{{UNIT}};', + '{{WRAPPER}} .elementor-nav-menu--dropdown li:last-child a' => 'border-bottom-right-radius: {{BOTTOM}}{{UNIT}}; border-bottom-left-radius: {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'dropdown_box_shadow', + 'exclude' => [ + 'box_shadow_position', + ], + 'selector' => '{{WRAPPER}} .elementor-nav-menu--main .elementor-nav-menu--dropdown, {{WRAPPER}} .elementor-nav-menu__container.elementor-nav-menu--dropdown', + ] + ); + + $this->add_responsive_control( + 'padding_horizontal_dropdown_item', + [ + 'label' => esc_html__( 'Horizontal Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'vw' => [ + 'min' => 0, + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--dropdown a' => 'padding-left: {{SIZE}}{{UNIT}}; padding-right: {{SIZE}}{{UNIT}}', + ], + 'separator' => 'before', + + ] + ); + + $this->add_responsive_control( + 'padding_vertical_dropdown_item', + [ + 'label' => esc_html__( 'Vertical Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + 'vh' => [ + 'min' => 0, + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--dropdown a' => 'padding-top: {{SIZE}}{{UNIT}}; padding-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'heading_dropdown_divider', + [ + 'label' => esc_html__( 'Divider', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'dropdown_divider', + 'selector' => '{{WRAPPER}} .elementor-nav-menu--dropdown li:not(:last-child)', + 'exclude' => [ 'width' ], + ] + ); + + $this->add_control( + 'dropdown_divider_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--dropdown li:not(:last-child)' => 'border-bottom-width: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'dropdown_divider_border!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'dropdown_top_distance', + [ + 'label' => esc_html__( 'Distance', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => -100, + 'max' => 100, + ], + 'em' => [ + 'min' => -10, + 'max' => 10, + ], + 'rem' => [ + 'min' => -10, + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-nav-menu--main > .elementor-nav-menu > li > .elementor-nav-menu--dropdown, {{WRAPPER}} .elementor-nav-menu__container.elementor-nav-menu--dropdown' => 'margin-top: {{SIZE}}{{UNIT}} !important', + ], + 'separator' => 'before', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( 'style_toggle', + [ + 'label' => esc_html__( 'Toggle Button', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'toggle!' => '', + 'dropdown!' => 'none', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_toggle_style' ); + + $this->start_controls_tab( + 'tab_toggle_style_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'toggle_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} div.elementor-menu-toggle' => 'color: {{VALUE}}', // Harder selector to override text color control + '{{WRAPPER}} div.elementor-menu-toggle svg' => 'fill: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'toggle_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-toggle' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_toggle_style_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'toggle_color_hover', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} div.elementor-menu-toggle:hover' => 'color: {{VALUE}}', // Harder selector to override text color control + '{{WRAPPER}} div.elementor-menu-toggle:hover svg' => 'fill: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'toggle_background_color_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-toggle:hover' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_responsive_control( + 'toggle_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 15, + ], + 'em' => [ + 'max' => 1.5, + ], + 'rem' => [ + 'max' => 1.5, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--nav-menu-icon-size: {{SIZE}}{{UNIT}}', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'toggle_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-toggle' => 'border-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'toggle_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-toggle' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + } + + public function get_frontend_settings() { + $frontend_settings = parent::get_frontend_settings(); + + // If the saved value is FA4, but the user has upgraded to FA5, the value needs to be converted to FA5. + if ( 'fa ' === substr( $frontend_settings['submenu_icon']['value'], 0, 3 ) && Icons_Manager::is_migration_allowed() ) { + $frontend_settings['submenu_icon']['value'] = str_replace( 'fa ', 'fas ', $frontend_settings['submenu_icon']['value'] ); + } + + // Determine the submenu icon markup. + if ( Plugin::elementor()->experiments->is_feature_active( 'e_font_icon_svg' ) ) { + $icon_classes = []; + + if ( false !== strpos( $frontend_settings['submenu_icon']['value'], 'chevron-down' ) ) { + $icon_classes['class'] = 'fa-svg-chevron-down'; + } + + $icon_content = Icons_Manager::render_font_icon( $frontend_settings['submenu_icon'], $icon_classes ); + } else { + $icon_content = sprintf( '', $frontend_settings['submenu_icon']['value'] ); + } + + // Passing the entire icon markup to the frontend settings because it can be either or tag. + $frontend_settings['submenu_icon']['value'] = $icon_content; + + return $frontend_settings; + } + + protected function render() { + $available_menus = $this->get_available_menus(); + + if ( ! $available_menus ) { + return; + } + + $settings = $this->get_active_settings(); + + $args = [ + 'echo' => false, + 'menu' => $settings['menu'], + 'menu_class' => 'elementor-nav-menu', + 'menu_id' => 'menu-' . $this->get_nav_menu_index() . '-' . $this->get_id(), + 'fallback_cb' => '__return_empty_string', + 'container' => '', + ]; + + if ( 'vertical' === $settings['layout'] ) { + $args['menu_class'] .= ' sm-vertical'; + } + + // Add custom filter to handle Nav Menu HTML output. + add_filter( 'nav_menu_link_attributes', [ $this, 'handle_link_classes' ], 10, 4 ); + add_filter( 'nav_menu_link_attributes', [ $this, 'handle_link_tabindex' ], 10, 4 ); + add_filter( 'nav_menu_submenu_css_class', [ $this, 'handle_sub_menu_classes' ] ); + add_filter( 'nav_menu_item_id', '__return_empty_string' ); + + // General Menu. + $menu_html = wp_nav_menu( $args ); + + // Dropdown Menu. + $args['menu_id'] = 'menu-' . $this->get_nav_menu_index() . '-' . $this->get_id(); + $args['menu_type'] = 'dropdown'; + $dropdown_menu_html = wp_nav_menu( $args ); + + // Remove all our custom filters. + remove_filter( 'nav_menu_link_attributes', [ $this, 'handle_link_classes' ] ); + remove_filter( 'nav_menu_link_attributes', [ $this, 'handle_link_tabindex' ] ); + remove_filter( 'nav_menu_submenu_css_class', [ $this, 'handle_sub_menu_classes' ] ); + remove_filter( 'nav_menu_item_id', '__return_empty_string' ); + + if ( empty( $menu_html ) ) { + return; + } + + $is_migrated = isset( $settings['__fa4_migrated']['submenu_icon'] ); + + if ( 'dropdown' !== $settings['layout'] ) : + $this->add_render_attribute( 'main-menu', 'class', [ + 'elementor-nav-menu--main', + 'elementor-nav-menu__container', + 'elementor-nav-menu--layout-' . $settings['layout'], + ] ); + + if ( $settings['pointer'] ) : + $this->add_render_attribute( 'main-menu', 'class', 'e--pointer-' . $settings['pointer'] ); + + foreach ( $settings as $key => $value ) : + if ( 0 === strpos( $key, 'animation' ) && $value ) : + $this->add_render_attribute( 'main-menu', 'class', 'e--animation-' . $value ); + + break; + endif; + endforeach; + endif; ?> + + render_menu_toggle( $settings ); + ?> + + classes ) ) { + $classes .= ' elementor-item-active'; + } + + if ( $is_anchor ) { + $classes .= ' elementor-item-anchor'; + } + + if ( empty( $atts['class'] ) ) { + $atts['class'] = $classes; + } else { + $atts['class'] .= ' ' . $classes; + } + + return $atts; + } + + public function handle_link_tabindex( $atts, $item, $args ) { + $settings = $this->get_active_settings(); + + // Add `tabindex = -1` to the links if it's a dropdown, for A11y. + $is_dropdown = 'dropdown' === $settings['layout']; + $is_dropdown = $is_dropdown || ( isset( $args->menu_type ) && 'dropdown' === $args->menu_type ); + + if ( $is_dropdown ) { + $atts['tabindex'] = '-1'; + } + + return $atts; + } + + public function handle_sub_menu_classes( $classes ) { + $classes[] = 'elementor-nav-menu--dropdown'; + + return $classes; + } + + private function render_menu_toggle( $settings ) { + if ( ! isset( $settings['toggle'] ) || 'burger' !== $settings['toggle'] ) { + return; + } + + $this->add_render_attribute( 'menu-toggle', [ + 'class' => 'elementor-menu-toggle', + 'role' => 'button', + 'tabindex' => '0', + 'aria-label' => esc_html__( 'Menu Toggle', 'elementor-pro' ), + 'aria-expanded' => 'false', + ] ); + + if ( Plugin::elementor()->editor->is_edit_mode() ) { + $this->add_render_attribute( 'menu-toggle', [ + 'class' => 'elementor-clickable', + ] ); + } + + ?> +
    print_render_attribute_string( 'menu-toggle' ); ?>> + 'eicons', + 'value' => 'eicon-menu-bar', + ]; + + $is_normal_icon_svg = 'svg' === $normal_icon['library']; + + if ( $is_normal_icon_svg ) { + echo ''; + } + + Icons_Manager::render_icon( + $normal_icon, + [ + 'aria-hidden' => 'true', + 'role' => 'presentation', + 'class' => $open_class, + ] + ); + + if ( $is_normal_icon_svg ) { + echo ''; + } + + $active_icon = ! empty( $settings['toggle_icon_active']['value'] ) + ? $settings['toggle_icon_active'] + : [ + 'library' => 'eicons', + 'value' => 'eicon-close', + ]; + + $is_active_icon_svg = 'svg' === $active_icon['library']; + + if ( $is_active_icon_svg ) { + echo ''; + } + + Icons_Manager::render_icon( + $active_icon, + [ + 'aria-hidden' => 'true', + 'role' => 'presentation', + 'class' => $close_class, + ] + ); + + if ( $is_active_icon_svg ) { + echo ''; + } + ?> + +
    + term_id ?? 0; + + if ( ! empty( $menu_id ) ) { + $element['settings']['menu_id'] = $menu_id; + } + + return $element; + } + + /** + * When importing a menu, if the menu has a slug that already exists, we add "-duplicate" to the slug of the imported menu. + * Upon importing a menu widget, we replace the slug to the correct one by fetching it from the correct ID in the $data array. + * + * Please take note that this function overrides On_Import_Trait::on_import_update_dynamic_content(). + * + * @param array $element_config + * @param array $data + * @param $controls + * + * @return array + */ + public static function on_import_update_dynamic_content( array $element_config, array $data, $controls = null ) : array { + $old_menu_id = $element_config['settings']['menu_id'] ?? 0; + + if ( empty( $old_menu_id ) ) { + return $element_config; + } + + $new_menu_id = $data['term_ids'][ $old_menu_id ] ?? 0; + $new_slug = wp_get_nav_menu_object( $new_menu_id )->slug ?? ''; + + if ( ! empty( $new_slug ) ) { + $element_config['settings']['menu'] = $new_slug; + } + + unset( $element_config['settings']['menu_id'] ); + + return $element_config; + } +} diff --git a/modules/nested-carousel/module.php b/modules/nested-carousel/module.php new file mode 100644 index 0000000..d02a768 --- /dev/null +++ b/modules/nested-carousel/module.php @@ -0,0 +1,29 @@ +experiments->is_feature_active( 'nested-elements' ); + } +} diff --git a/modules/nested-carousel/widgets/nested-carousel.php b/modules/nested-carousel/widgets/nested-carousel.php new file mode 100644 index 0000000..b05b027 --- /dev/null +++ b/modules/nested-carousel/widgets/nested-carousel.php @@ -0,0 +1,465 @@ + 'container', + 'settings' => [ + '_title' => __( 'Slide #1', 'elementor-pro' ), + ], + ], + [ + 'elType' => 'container', + 'settings' => [ + '_title' => __( 'Slide #2', 'elementor-pro' ), + ], + ], + [ + 'elType' => 'container', + 'settings' => [ + '_title' => __( 'Slide #3', 'elementor-pro' ), + ], + ], + ]; + } + + protected function get_default_repeater_title_setting_key() { + return 'slide_title'; + } + + protected function get_default_children_title() { + return esc_html__( 'Slide #%d', 'elementor-pro' ); + } + + protected function get_default_children_placeholder_selector() { + return '.swiper-wrapper'; + } + + protected function get_html_wrapper_class() { + return 'elementor-widget-n-carousel'; + } + + protected function register_controls() { + $low_specificity_slider_container_selector = ':where( {{WRAPPER}} .swiper-slide ) > .e-con'; + + $this->start_controls_section( + 'section_slides', + [ + 'label' => esc_html__( 'Slides', 'elementor-pro' ), + ] + ); + + $repeater = new Repeater(); + + $repeater->add_control( + 'slide_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Slide Title', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Slide Title', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'label_block' => true, + ] + ); + + $this->add_control( + 'carousel_items', + [ + 'label' => esc_html__( 'Carousel Items', 'elementor-pro' ), + 'type' => Control_Nested_Repeater::CONTROL_TYPE, + 'fields' => $repeater->get_controls(), + 'default' => [ + [ + 'slide_title' => esc_html__( 'Slide #1', 'elementor-pro' ), + ], + [ + 'slide_title' => esc_html__( 'Slide #2', 'elementor-pro' ), + ], + [ + 'slide_title' => esc_html__( 'Slide #3', 'elementor-pro' ), + ], + ], + 'frontend_available' => true, + 'title_field' => '{{{ slide_title }}}', + ] + ); + + $this->add_carousel_layout_controls( [ + 'css_prefix' => 'e-n-carousel-', + 'slides_to_show_custom_settings' => [ + 'separator' => 'before', + 'tablet_default' => '2', + 'mobile_default' => '1', + 'frontend_available' => true, + 'render_type' => 'template', + 'selectors' => [ + '{{WRAPPER}}' => '--e-n-carousel-swiper-slides-to-display: {{VALUE}}', + ], + ], + 'slides_to_scroll_custom_settings' => [], + 'equal_height_custom_settings' => [ + 'selectors' => [ + '{{WRAPPER}}' => '--e-n-carousel-slide-height: auto; --e-n-carousel-slide-container-height: 100%;', + ], + ], + 'slides_on_display' => 8, + ] ); + + $this->end_controls_section(); + + $this->add_carousel_settings_controls( [ + 'css_prefix' => 'e-n-carousel-', + 'autoplay_custom_settings' => [ + 'description' => esc_html__( 'The Autoplay is inactive while editing. Preview your page to see it in action.', 'elementor-pro' ), + ], + 'infinite_custom_settings' => [ + 'description' => esc_html__( 'Infinite scroll is inactive while editing. Preview your page to see it in action.', 'elementor-pro' ), + ], + 'offset_sides_custom_settings' => [ + 'description' => esc_html__( 'Offset is inactive while editing. Preview your page to see it in action.', 'elementor-pro' ), + ], + ] ); + + $this->add_carousel_navigation_controls( [ + 'css_prefix' => 'e-n-carousel-', + ] ); + + $this->add_carousel_pagination_controls( [ + 'css_prefix' => 'e-n-carousel-', + ] ); + + $this->start_controls_section( + 'section_slides_style', + [ + 'label' => esc_html__( 'Slides', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'image_spacing_custom', + [ + 'label' => esc_html__( 'Gap between slides', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 400, + ], + 'em' => [ + 'max' => 40, + ], + 'rem' => [ + 'max' => 40, + ], + ], + 'default' => [ + 'size' => 10, + ], + 'frontend_available' => true, + 'render_type' => 'none', + 'selectors' => [ + '{{WRAPPER}}' => '--e-n-carousel-swiper-slides-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'content_background', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => $low_specificity_slider_container_selector, + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'content_border', + 'selector' => $low_specificity_slider_container_selector, + 'fields_options' => [ + 'color' => [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + ], + 'width' => [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_responsive_control( + 'border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + $low_specificity_slider_container_selector => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + // Todo: Remove in version 3.21.0: https://elementor.atlassian.net/browse/ED-11888. + // Remove together with support for physical properties inside the container widget. + $logical_dimensions_inline_start = is_rtl() ? '{{RIGHT}}{{UNIT}}' : '{{LEFT}}{{UNIT}}'; + $logical_dimensions_inline_end = is_rtl() ? '{{LEFT}}{{UNIT}}' : '{{RIGHT}}{{UNIT}}'; + + $this->add_responsive_control( + 'content_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + $low_specificity_slider_container_selector => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};', + // Todo: Remove in version 3.21.0: https://elementor.atlassian.net/browse/ED-11888. + // Remove together with support for physical properties inside the container widget. + ':where( [data-core-v316-plus="true"] .elementor-element.elementor-widget-n-carousel .swiper-slide ) > .e-con' => "--padding-block-start: {{TOP}}{{UNIT}}; --padding-inline-end: $logical_dimensions_inline_end; --padding-block-end: {{BOTTOM}}{{UNIT}}; --padding-inline-start: $logical_dimensions_inline_start;", + ], + 'separator' => 'before', + ] + ); + + $this->end_controls_section(); + + $this->add_carousel_navigation_styling_controls( [ + 'css_prefix' => 'e-n-carousel-', + 'navigation_styling_custom_settings' => [ + 'condition' => [ + 'arrows' => 'yes', + ], + ], + ] ); + + $this->add_carousel_pagination_style_controls( [ + 'css_prefix' => 'e-n-carousel-', + ] ); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + $this->num_of_carousel_items = count( $settings['carousel_items'] ?? [] ); + $slides = $settings['carousel_items']; + $swiper_wrapper_class = Plugin::elementor()->experiments->is_feature_active( 'e_swiper_latest' ) ? 'swiper' : 'swiper-container'; + $direction = $settings['direction']; + $has_autoplay_enabled = 'yes' === $settings['autoplay']; + $outside_wrapper_classes = [ 'e-n-carousel', $swiper_wrapper_class ]; + + $this->add_render_attribute( [ + 'carousel-outside-wrapper' => [ + 'class' => $outside_wrapper_classes, + ], + 'carousel-inside-wrapper' => [ + 'class' => 'swiper-wrapper', + 'aria-live' => $has_autoplay_enabled ? 'off' : 'polite', + ], + ] ); + + if ( ! empty( $direction ) ) { + $this->add_render_attribute( 'carousel-outside-wrapper', 'dir', $direction ); + } + ?> +
    print_render_attribute_string( 'carousel-outside-wrapper' ); ?>> +
    print_render_attribute_string( 'carousel-inside-wrapper' ); ?>> + $slide ) { + $slide_count = $index + 1; + $slide_setting_key = $this->get_repeater_setting_key( 'slide_wrapper', 'slide', $index ); + + $this->add_render_attribute( $slide_setting_key, [ + 'class' => 'swiper-slide', + 'data-slide' => $slide_count, + 'role' => 'group', + 'aria-roledescription' => 'slide', + 'aria-label' => $slide_count . ' ' . esc_html__( 'of', 'elementor-pro' ) . ' ' . count( $slides ), + ] ); + ?> +
    print_render_attribute_string( $slide_setting_key ); ?>> + print_child( $index ); ?> +
    + +
    +
    + render_carousel_footer( $settings ); + } + + protected function get_initial_config(): array { + if ( Plugin::elementor()->experiments->is_feature_active( 'e_nested_atomic_repeaters' ) ) { + return array_merge( parent::get_initial_config(), [ + 'support_improved_repeaters' => true, + 'target_container' => [ '.e-n-carousel > .swiper-wrapper' ], + 'node' => 'div', + ] ); + } + + return parent::get_initial_config(); + } + + protected function get_default_children_container_placeholder_selector() { + return '.swiper-slide'; + } + + protected function content_template_single_repeater_item() { + ?> + <# + const elementUid = view.getIDInt().toString().substr( 0, 3 ), + numOfSlides = view.collection.length + 1; + + const slideCount = numOfSlides, + slideUid = elementUid + slideCount, + slideWrapperKey = slideUid; + + const slideWrapperKeyItem = { + 'class': 'swiper-slide', + 'data-slide': slideCount, + 'role': 'group', + 'aria-roledescription': 'slide', + 'aria-label': slideCount + ' ' + numOfSlides, + }; + + view.addRenderAttribute( 'single-slide', slideWrapperKeyItem, null, true ); + #> +
    + + <# if ( settings['carousel_items'] ) { + const elementUid = view.getIDInt().toString().substr( 0, 3 ), + carouselOutsideWrapperKey = 'carousel-' + elementUid, + carouselInsideWrapperKey = 'carousel-inside-' + elementUid, + swiperWrapperClass = elementorFrontend.config.swiperClass, + hasAutoplayEnabled = 'yes' === settings['autoplay'], + outsideWrapperClasses = ['e-n-carousel',swiperWrapperClass] + shouldRenderPaginationAndArrows = 1 < settings['carousel_items'].length; + + view.addRenderAttribute( carouselOutsideWrapperKey, { + 'class': outsideWrapperClasses, + } ); + + view.addRenderAttribute( carouselInsideWrapperKey, { + 'class': 'swiper-wrapper', + 'aria-live': hasAutoplayEnabled ? 'off' : 'polite', + } ); + + if ( !! settings['direction'] ) { + view.addRenderAttribute( carouselOutsideWrapperKey, 'dir', settings['direction'] ); + } + #> +
    +
    + <# _.each( settings['carousel_items'], function( slide, index ) { + const slideCount = index + 1, + slideUid = elementUid + slideCount, + slideWrapperKey = slideUid; + + view.addRenderAttribute( slideWrapperKey, { + 'class': 'swiper-slide', + 'data-slide': slideCount, + 'role': 'group', + 'aria-roledescription': 'slide', + 'aria-label': slideCount + ' ' + settings['carousel_items'].length, + } ); + #> +
    + <# } ); #> +
    +
    + <# if ( 'yes' === settings['arrows'] && shouldRenderPaginationAndArrows ) { #> + content_template_navigation_arrows(); ?> + <# } #> + <# if ( settings['pagination'] && shouldRenderPaginationAndArrows ) { #> +
    + <# } #> + <# } #> + +
    + <# + const iconSettingsPrevious = settings['navigation_previous_icon'], + iconPreviousHTML = elementor.helpers.renderIcon( view, iconSettingsPrevious, { 'aria-hidden': true }, 'i' , 'object' ); + + if ( 'eicon-chevron-left' === iconSettingsPrevious['value'] ) { #> + 'eicons', + 'value' => 'eicon-chevron-left', + ] + ); ?> + <# } else if ( !! iconSettingsPrevious['value'] ) { #> + {{{ iconPreviousHTML.value }}} + <# } #> +
    +
    + <# + const iconSettingsNext = settings['navigation_next_icon'], + iconNextHTML = elementor.helpers.renderIcon( view, iconSettingsNext, { 'aria-hidden': true }, 'i' , 'object' ); + + if ( 'eicon-chevron-right' === iconSettingsNext['value'] ) { #> + 'eicons', + 'value' => 'eicon-chevron-right', + ] + ); ?> + <# } else if ( !! iconSettingsNext['value'] ) { #> + {{{ iconNextHTML.value }}} + <# } #> +
    + add_node( [ + 'id' => 'elementor_notes', + 'title' => esc_html__( 'Notes', 'elementor-pro' ), + 'href' => '#', // Click event is handled by JS. + ] ); + }, 200 ); // Before "Elementor Debugger". + } +} diff --git a/modules/notes/admin-page.php b/modules/notes/admin-page.php new file mode 100644 index 0000000..901bb33 --- /dev/null +++ b/modules/notes/admin-page.php @@ -0,0 +1,146 @@ +register_admin_menu(); + }, 206 ); // After Elementor. + + add_action( 'admin_head', function () { + $this->hide_menu_item(); + } ); + } + + /** + * Register the admin page (will be removed later from the menu). + * + * @return void + */ + protected function register_admin_menu() { + add_submenu_page( + Settings::PAGE_ID, + esc_html__( 'Notes Proxy', 'elementor-pro' ), + esc_html__( 'Notes Proxy', 'elementor-pro' ), + 'read', + static::PAGE_ID + ); + + add_action( 'current_screen', function ( \WP_Screen $current_screen ) { + if ( static::PAGE_ID !== $current_screen->id ) { + return; + } + + $this->on_page_load(); + } ); + } + + /** + * Hide the menu item, since it shouldn't be visible to users in the UI. + * + * @return void + */ + protected function hide_menu_item() { + remove_submenu_page( Settings::PAGE_ID, static::PAGE_ID ); + } + + /** + * Run the actual proxy page. + * + * @return void + */ + public function on_page_load() { + // No need for nonce check since it's not a user action, and it's safe. + if ( empty( $_GET['note-id'] ) || ! is_numeric( $_GET['note-id'] ) ) { // phpcs:ignore: WordPress.Security.NonceVerification.Recommended + $this->safe_redirect( get_site_url() ); + return; + } + + $note = Note::query()->find( (int) $_GET['note-id'] ); // phpcs:ignore: WordPress.Security.NonceVerification.Recommended + + if ( ! $note ) { + $message = esc_html__( 'The note you are looking for was not found.', 'elementor-pro' ); + + $go_to_dashboard = '' . esc_html__( 'Go to WP Dashboard', 'elementor-pro' ) . ''; + $view_site = '' . esc_html__( 'View Site', 'elementor-pro' ) . ''; + + $this->message_and_die( " + {$message} +

    + {$go_to_dashboard} +  |  + {$view_site} + " ); + + return; + } + + if ( ! current_user_can( Capabilities::READ_NOTES, $note ) ) { + $this->message_and_die( esc_html__( 'You are not autorized to view this Note. Please contact your admin.', 'elementor-pro' ) ); + return; + } + + $this->redirect_to_note( $note ); + } + + /** + * Redirect to a note - Used for testing. + * + * @param Note $note + * + * @return void + */ + protected function redirect_to_note( Note $note ) { + $url = $note->get_url( false ); + + // Note: The URL is safe. + // `header()` is used since `wp_safe_redirect()` filters chars like `{}` which are required in this case. + header( 'Location:' . $url, true, 302 ); + die; + } + + /** + * Safe redirect to a page - Used for testing. + * + * @param string $url + * + * @return void + */ + protected function safe_redirect( $url ) { + wp_safe_redirect( $url ); + die; + } + + /** + * Show a message to the user and die - Used for testing. + * + * @param string $message + * + * @return void + */ + protected function message_and_die( $message ) { + wp_die( $message ); // phpcs:ignore WordPress.Security.EscapeOutput + } +} diff --git a/modules/notes/assets/images/elementor-logo-orange.png b/modules/notes/assets/images/elementor-logo-orange.png new file mode 100644 index 0000000000000000000000000000000000000000..13bf88f03c688cfc87ea3cf64998a32e072c979e GIT binary patch literal 3936 zcmV-m51;UfP)00009a7bBm000&x z000&x0ZCFM@Bjb+0drDELIAGL9O(c600d`2O+f$vv5yPhgmPW+y!69~S6 zyh$vLhHzDNg9JeUUHBw9(?362(hLYR`lGtKx~e)rkEDH?sgyTNaz#OJubYk^msgnLTdqO1>>Cl z8lhj7R#1KikH_QLBaH>*fP%3|g7=7mU+^nD9*<`as0Cy?rRDP(zNWuE! z9Ct}+!{&;FRALo;VC>G&nWCHvF&d44+kq887>>!B&P>lMvv!+;ju*% z_UGc(cZ)gQ`H6Am332#@xFcnK4v)vviS!@%J3b&H<%2_nBbZQORz~LZ{~vKn*%FX; zmv~%1$1Y6pjAppY;J>%G0X?3!*r1OYSRh`@Ca9;t_7Xe5C5{af)#LGani7bnIKy$Q z827?jKw9^i5XVP!+wCMCk7pP*OYES<()t=t=nl#H-zmPr_7a`bnLR->Lb}fnjlN;D z$fnQf4YA)4!9NdwSN+h+%9{aQ!$|p^hJGK}5WI)K;(z$fhrRZPwSC^FF>f|p1FGBi z`1MfgSOg?#U|IggX57V_e8(g$^lj0O5&SzRKoz8)pber$j$w-wZ8Z>McEtXevZH=& zfA<*11u2k>L^1vAa-aU1GN|v+${<)9-^0?fn%tZ!TduuleMA#=@~Zb<+r3hpXPy#L z7Dzc_8XOlc_bn;IU7C!|5|Qm^oA<%G-aBQ3TBq@Yv~##_=d)aSQ|a56xI(OPIn%6k zs@h=dowI%iTMp2Br&zekVe_?aJU;@(ijl5ymb<3COUnRhd-ERGRGt9a4u__WO~2%} z{0XmA_kKvi_SErCACOelQLfGT6Cx`X73NzQTSPNX5EcaF*LK${BcYdjfCi9&WDW8S znsY(V8f-_qLl&bYb%J^Dv%8V|@`JRo^Sr}9Lo*U8!Mskmq@PkftV9Ua6hH>d-wI`7 z_p$GxsXtSO7p1>4rl1Rj8(QP|{k0I{RZw#`w*@_KUOD*8z27+RTNqRFUHv=P*WF`T znMLRyXTR*21&sUUqUybJ&-jX-ae;bbD(lv2)3(jGRKMrNi!&Q!gkvXdA>cK)HU41V zQccBZS5H&w^q16lFURoPlulw|=VxdRYf1}x-#0QFmk|7mG@R72{GoxI*E+1g<}yXl zK$EPUw70Mm@IooPO#FVud+gksBgf7pIAV@=KEN~5td~;%CCHL7P(-GE*lxFm?_61T z`@)W&mmS6r6WaHBQ=5%Lhc9GO$Qybdu0q|QqNH~bFTUTX}y@x!Zz z=H|O{vV07)vQ-Y7^JoktsOxLoMyE1)8)51g-)w^%ZU@-RdUK2ize_Iyavf3B@8mHR zRUY^PJ!2=L8Tuo%(Mk_@1UfIVCr$x5qyN9K!Y4u-te-2{)?U-!7idSVeOfffp!L1W zVQt3lQC{dm)5;zT4h)-yGPMiQYIGNAdYblUj*^S%wRn&+_p6m zQ1qPQaUn69)*IU{11ft;8k1Qi&7h%Zm*-vN4>_-rfclQB4pXV``hI#+7~`u(tA>T# zegia7ajoaM_8CCi1-SvmKw;3SzqMMWL>qt0t2CvsjSd!#5>@ zN@wSKFYUV7aJs8Xyqw_A+rp8N&45qHj=XZD1T>loPhcBc*@W)3f`+83kI}PVsNXw~ zU0)H9;(?Hx=%@{NiRzkUmeh9^P+}*Z$bBKz!-VQ~_x-r6-^tFmrf-)AiM&NmIFDR7 zD*DhM1L@E%;s=o=dClP8wAe6Y#0HMt9dR%Ra-%nz&jn0@vWr@NIQ#F*SsjUQ9DHDk#7G$(N1 zXWGhKMUy@~BcT5tNL{W(7+-+H5rlv{9}I_|qt*m2W#j?qe%fsG&55#n~C=~oFf{7!9frn)l3K(K)9 z&7fI9i1A1;1{1s_En*XBzdtmy^*K#aGu*y`S`F{yx{OPygvbpQyX;Y5cck|UcN3ku zdmN1QF45?=opKVzHM)%)H$zXP<8i83)tL5;(3SKC)tiPM-&ed8_RK_OXg(HJy=agL z#v(YP!Lu5%UWVoae#3YqIA)qE0-8Rpwb7^Q&6flN!AtC3jhkC!{%cJz4e|`_=;K`y zxHYbwlu3JmCpY&}krR+U@JU{CTu~cqY+E&{!)_7HY}IBJ7uH@uc5n_8RHUNQZ6L0O zOO|}fyEQh^ZU0L~PNex$vkqpRf7=Miq^C?ie2>U+&1eaFYdMufxAhn|2!Hxx4r|*= z;2y{v3G>Y@pIjoPFf(WpnJ{-majHz(BC*n%h#~ei_*v~w+ZdyN^qtLQKg8&Ri86V? z1Pg7hK}Pc8kx-?{=zH|V|M6O7^Wn@c4b#p^Ss&A8)W@wHosWT7agC(AYuwIM*NxNs z9WtENsgQ?p-JeY#+4$pLDKokA=3zx0$^GuAW8QV+wI#+6l7+(Rh>m+LW2JCB)xbjM zu1cfkOfZ~(Zb8pp6tzj~R>K3!xQFDA80hH?m0a9uTYu>VbGKRiS&TL&R?LDWe?`B+gJ6gkq2uO}bP`^~3D}*$#HeBxtmB!H`9FX7C z7c!N2(Ow3zH1H!)|L4lD%sAXFcg70~o^bcZxxasqdhVxTIT~f9580E?=3JFz-Px}z z?|&mT$cBAB%vM?Jddq%Z`2Z6Fci_wOW@9E;=c(ML*hd09ZIDkPv_7^vd*e2XA(z+8 zB=@&Dn!;(+uc@(QP0t>x+B(U2?f*XwW zb$r%q)Qiz_VjKhSM9Em73wO#*4t4^Xp`S5yA!*=7JZpZ}&G?;kmIQ|n=gb>?mr&9n zanj%Jjhx7AdJhjItA*%I(O@X+$6y<8^}l zk%-OF3rCN~Ga{-(yJUmmoe0T&xB!{0{XJ~Rcs!oljV@F@N29ZdyHR$Yvd81`j7DPt znL)P6FX`JHZ`pS^l^2X2kLMt0Eg&<*XKI44V3HbyXoK+`T+rv07mOZ{=TPV@AoJMd us-M~On(G{FH;&1`k3)tFdORLaTmAqW%2_QfIoLJ;0000user_transformer = new User_Transformer(); + } + + public function register_endpoints() { + $this->register_endpoint( new Read_Status_Endpoint( $this ) ); + $this->register_endpoint( new Summary_Endpoint( $this ) ); + $this->register_endpoint( new Users_Endpoint( $this ) ); + + $this->index_endpoint->register_item_route( \WP_REST_Server::READABLE, [ + 'id' => [ + 'type' => 'integer', + 'description' => 'Note ID to find.', + 'required' => true, + ], + ] ); + + $this->index_endpoint->register_items_route( \WP_REST_Server::CREATABLE, [ + 'post_id' => [ + 'type' => 'integer', + 'description' => 'The id of the post where the note was created at (can be template, post, page, etc.).', + 'required' => true, + 'validate_callback' => function ( $value ) { + return Plugin::elementor()->documents->get( $value ); + }, + ], + 'element_id' => [ + 'type' => 'string', + 'description' => 'Each note must be attached to an elementor element.', + 'required' => true, + 'sanitize_callback' => function( $value ) { + return trim( $value ); + }, + 'validate_callback' => function ( $value ) { + return (bool) preg_match( '/^[a-z0-9]{7,9}$/', $value ); + }, + ], + 'content' => [ + 'type' => 'string', + 'description' => 'The content of the note.', + 'required' => true, + 'sanitize_callback' => function ( $value ) { + return $this->sanitize_content( $value ); + }, + 'validate_callback' => function ( $value ) { + return ! empty( $value ); + }, + ], + 'position' => [ + 'type' => 'object', + 'properties' => [ + 'x' => [ + 'required' => true, + 'type' => 'number', + ], + 'y' => [ + 'required' => true, + 'type' => 'number', + ], + ], + 'required' => true, + 'description' => 'The position of the note.', + ], + 'mentioned_usernames' => [ + 'type' => 'array', + 'description' => 'List of user names that have been mentioned in the note\'s content.', + 'default' => [], + 'items' => [ + 'type' => 'string', + 'sanitize_callback' => function ( $value ) { + return wp_strip_all_tags( $value, true ); + }, + ], + 'required' => false, + ], + 'route_post_id' => [ + 'description' => 'The ID of the post that\'s associated with the route (doesn\'t always exist, e.g: home page, archive)', + 'required' => false, + 'validate_callback' => function ( $value ) { + if ( ! $value ) { + return true; + } + + return is_numeric( $value ) && Plugin::elementor()->documents->get( $value ); + }, + 'sanitize_callback' => function ( $value ) { + if ( ! $value ) { + return null; + } + + return intval( $value ); + }, + ], + 'route_url' => [ + 'type' => 'string', + 'description' => 'The URL of the route where the note was created at.', + 'required' => false, + 'validate_callback' => function ( $value ) { + return Utils::validate_url_or_relative_url( $value ); + }, + 'sanitize_callback' => function ( $value ) { + return Utils::clean_url( $value ); + }, + ], + 'route_title' => [ + 'type' => 'string', + 'description' => 'The title of the route where the note was created at.', + 'required' => false, + 'sanitize_callback' => function ( $value ) { + return wp_strip_all_tags( $value, true ); + }, + ], + 'parent_id' => [ + 'type' => 'integer', + 'description' => 'If the new note is a reply to another note, the parent_id should be the thread\'s id.', + 'required' => false, + 'default' => 0, + ], + 'is_public' => [ + 'type' => 'boolean', + 'description' => 'Should this note be visible for everyone or just for its author.', + 'required' => false, + ], + ] ); + + $this->index_endpoint->register_item_route( \WP_REST_Server::EDITABLE, [ + 'id' => [ + 'type' => 'integer', + 'description' => 'The id the note.', + 'required' => true, + ], + 'content' => [ + 'type' => 'string', + 'description' => 'The content of the note.', + 'required' => false, + 'sanitize_callback' => function ( $value ) { + return $this->sanitize_content( $value ); + }, + ], + 'mentioned_usernames' => [ + 'type' => 'array', + 'description' => 'List of user names that have been mentioned in the note\'s content.', + 'items' => [ + 'type' => 'string', + 'sanitize_callback' => function ( $value ) { + return wp_strip_all_tags( $value, true ); + }, + ], + 'required' => false, + ], + 'status' => [ + 'type' => 'string', + 'description' => 'Note status can be draft or publish.', + 'required' => false, + 'enum' => [ + Note::STATUS_PUBLISH, + Note::STATUS_DRAFT, + ], + ], + 'is_public' => [ + 'type' => 'boolean', + 'description' => 'Should this note be visible for everyone or just for its author.', + 'required' => false, + ], + 'is_resolved' => [ + 'type' => 'boolean', + 'description' => 'Is this note resolved and should be hidden.', + 'required' => false, + ], + ] ); + + $this->index_endpoint->register_item_route( \WP_REST_Server::DELETABLE, [ + 'id' => [ + 'type' => 'integer', + 'description' => 'The id of the note.', + 'required' => true, + ], + 'force' => [ + 'type' => 'boolean', + 'description' => 'Determine if it should be deleted permanently or change the status to trash.', + 'default' => false, + 'required' => false, + ], + ] ); + } + + /** + * Notes index route params. + * + * @return array[] + */ + public function get_collection_params() { + return [ + 'route_url' => [ + 'type' => 'string', + 'description' => 'The URL of the route where the note was created at.', + 'required' => false, + 'validate_callback' => function ( $value ) { + return Utils::validate_url_or_relative_url( $value ); + }, + 'sanitize_callback' => function ( $value ) { + return Utils::clean_url( $value ); + }, + ], + 'status' => [ + 'type' => 'string', + 'description' => 'The note status (e.g. "publish", "draft").', + 'required' => false, + 'enum' => [ + Note::STATUS_PUBLISH, + Note::STATUS_DRAFT, + ], + 'default' => Note::STATUS_PUBLISH, + ], + 'is_resolved' => [ + 'type' => 'boolean', + 'description' => 'Whether the note is resolved or not.', + 'required' => false, + ], + 'parent_id' => [ + 'type' => 'integer', + 'description' => 'The note\'s parent id (use 0 for top-level).', + 'required' => false, + ], + 'post_id' => [ + 'type' => 'integer', + 'description' => 'The ID of the post that the note is attached to.', + 'required' => false, + 'validate_callback' => function ( $value ) { + return Plugin::elementor()->documents->get( $value ); + }, + ], + 'only_unread' => [ + 'type' => 'boolean', + 'description' => 'Show only unread notes (represents an unread thread if one of its replies is unread).', + 'required' => false, + ], + 'only_relevant' => [ + 'type' => 'boolean', + 'description' => 'Show only notes that are relevant to the current user.', + 'required' => false, + ], + 'order_by' => [ + 'type' => 'string', + 'description' => 'A column to order the results by.', + 'required' => false, + 'default' => 'last_activity_at', + 'enum' => [ + 'last_activity_at', + 'created_at', + ], + ], + 'order' => [ + 'type' => 'string', + 'description' => 'Results order direction.', + 'required' => false, + 'default' => 'desc', + 'enum' => [ + 'asc', + 'desc', + ], + ], + ]; + } + + /** + * Get all Notes by filters. + * + * GET `/notes` + * + * @param \WP_REST_Request $request + * + * @return array + */ + public function get_items( $request ) { + $user_id = get_current_user_id(); + + $notes_query = Note::query() + ->with_replies_count() + ->with_unread_replies_count( $user_id ) + ->with_is_read( $user_id ) + ->with_author() + ->only_visible( $user_id ) + ->order_by( + $request->get_param( 'order_by' ), + $request->get_param( 'order' ) + ); + + foreach ( $this->get_filters() as $param => $callback ) { + if ( $request->has_param( $param ) ) { + call_user_func( $callback, $notes_query, $request->get_param( $param ) ); + } + } + + $notes = $notes_query->get()->filter( function ( Note $note ) { + return current_user_can( Capabilities::READ_NOTES, $note ); + } )->map( function ( Note $note ) { + return $this->transform_users( $note ); + } ); + + return [ + 'data' => $notes, + 'meta' => [], + ]; + } + + /** + * Get a single note. + * + * GET `/notes/{id}` + * + * @param \WP_REST_Request $request + * + * @return array + */ + public function get_item( $request ) { + $user_id = get_current_user_id(); + + /** + * @var $note Note|null + */ + $note = Note::query() + ->where( 'id', '=', $request->get_param( 'id' ) ) + ->with_replies( function ( Note_Query_Builder $q ) use ( $user_id ) { + $q->with_author()->with_is_read( $user_id )->with_readers(); + } ) + ->with_replies_count() + ->with_unread_replies_count( $user_id ) + ->with_is_read( $user_id ) + ->with_author() + ->with_readers() + ->with_document() + ->first(); + + if ( ! $note ) { + throw new Error_404(); + } + + $note = $this->transform_users( $note ); + $note->attach_user_capabilities( $user_id ); + + return [ + 'data' => $note, + 'meta' => [], + ]; + } + + /** + * Run all user models in the note through user transformer. + * + * @param Note $note + * + * @return Note + */ + protected function transform_users( Note $note ) { + if ( ! empty( $note->author ) ) { + $note->author = $this->user_transformer->transform( $note->author ); + } + + if ( ! $note->readers->is_empty() ) { + $note->readers = $note->readers->map( function ( User $user ) { + return $this->user_transformer->transform( $user ); + } ); + } + + // If the note has replies, recursively run the function for each reply note. + if ( ! $note->replies->is_empty() ) { + $note->replies = $note->replies->map( function ( Note $reply ) { + return $this->transform_users( $reply ); + } ); + } + + return $note; + } + + /** + * Create a note. + * + * POST `/notes` + * + * @param \WP_REST_Request $request + * + * @return array + * @throws \Exception + */ + public function create_items( $request ) { + $this->validate_create_items( $request ); + + $now = gmdate( 'Y-m-d H:i:s' ); + + $values = ( new Collection( $request->get_body_params() ) ) + ->only( [ + 'post_id', + 'element_id', + 'content', + 'route_post_id', + 'route_url', + 'route_title', + 'status', + 'parent_id', + 'is_public', + ] ) + ->merge( [ + 'author_id' => get_current_user_id(), + 'created_at' => $now, + 'updated_at' => $now, + 'last_activity_at' => $now, + 'position' => wp_json_encode( $request->get_param( 'position' ) ), + ] ) + ->all(); + + $id = Note::query()->insert( $values ); + + /** @var Note $note */ + $note = Note::query()->with_author()->find( $id ); + + $note = $this->transform_users( $note ); + + $mentioned = $note->sync_mentions( + $request->get_param( 'mentioned_usernames' ), + 'user_nicename' + ); + + // Set the note as read by its author. + $note->add_readers( [ get_current_user_id() ] ); + + // If it's a reply, the thread's `last_activity_at` should be updated as well. + if ( $note->is_reply() ) { + Note::query() + ->where( 'id', '=', $note->parent_id ) + ->update( [ 'last_activity_at' => $now ] ); + } + + // TODO: Use events system. + $this->on_note_created( [ + 'note' => $note, + 'mentioned' => $mentioned, + 'actor' => User::from_wp_user( wp_get_current_user() ), + ] ); + + return [ + 'data' => $note, + 'meta' => [], + ]; + } + + /** + * Update a note. + * + * PATCH `/notes/{id}` + * + * @param \WP_REST_Request $request + * + * @return array + */ + public function update_item( $request ) { + $this->validate_update_items( $request ); + + $now = gmdate( 'Y-m-d H:i:s' ); + + $values = ( new Collection( $request->get_params() ) ) + ->only( [ + 'content', + 'status', + 'is_public', + 'is_resolved', + ] ) + ->merge( [ + 'updated_at' => $now, + ] ) + ->merge( $request->has_param( 'is_resolved' ) ? [ + 'last_activity_at' => $now, + ] : [] ) + ->all(); + + Note::query() + ->where( 'id', '=', $request->get_param( 'id' ) ) + ->update( $values ); + + // Need to refetch the note after update + /** @var Note $note */ + $note = Note::query()->with_author()->find( $request->get_param( 'id' ) ); + + if ( $request->has_param( 'mentioned_usernames' ) ) { + $mentioned = $note->sync_mentions( + $request->get_param( 'mentioned_usernames' ), + 'user_nicename' + ); + } + + // TODO: Use events system. + $this->on_note_updated( [ + 'note' => $note, + 'actor' => User::from_wp_user( wp_get_current_user() ), + 'mentioned' => isset( $mentioned ) ? $mentioned : null, + 'resolved' => ! ! $request->get_param( 'is_resolved' ), + ] ); + + return [ + 'data' => $note, + 'meta' => [], + ]; + } + + /** + * Delete a note. + * + * DELETE `/notes/{id}` + * + * @param \WP_REST_Request $request + * + * @return \WP_REST_Response + * @throws \Elementor\Data\V2\Base\Exceptions\Error_404 + */ + public function delete_item( $request ) { + /** @var Note $note */ + $note = Note::query()->find( $request->get_param( 'id' ) ); + + if ( ! $note ) { + throw new Error_404(); + } + + Note::query() + ->where( 'id', '=', $note->id ) + ->when( + $request->get_param( 'force' ), + function ( Note_Query_Builder $query ) { + $query->delete( true ); + }, + function ( Note_Query_Builder $query ) { + $query->trash(); + } + ); + + // TODO: Should return status 204 when the $e.data will support it + return new \WP_REST_Response( [], Http_Status::OK ); + } + + /** + * @inheritDoc + */ + public function get_permission_callback( $request ) { + $capability = null; + $id = $request->get_param( 'id' ); + + switch ( $request->get_method() ) { + case 'GET': + $capability = Capabilities::READ_NOTES; + break; + case 'POST': + // When creating a note it checks if the user can create note for the parent note if 'parent_id' is provided. + $id = $request->get_param( 'parent_id' ); + $capability = Capabilities::CREATE_NOTES; + break; + case 'PUT': + case 'PATCH': + $capability = Capabilities::EDIT_NOTES; + break; + case 'DELETE': + $capability = Capabilities::DELETE_NOTES; + break; + } + + return $capability && current_user_can( $capability, $id ); + } + + /** + * Get the Notes filters. + * + * @return array + */ + public function get_filters() { + return [ + 'route_url' => function ( Note_Query_Builder $q, $url ) { + $q->where( 'route_url', '=', $url ); + }, + 'is_resolved' => function ( Note_Query_Builder $q, $is_resolved ) { + $q->where( 'is_resolved', '=', $is_resolved ); + }, + 'parent_id' => function ( Note_Query_Builder $q, $parent_id ) { + $q->where( 'parent_id', '=', $parent_id ); + }, + 'post_id' => function ( Note_Query_Builder $q, $post_id ) { + $q->where( 'post_id', '=', $post_id ); + }, + 'only_unread' => function ( Note_Query_Builder $q ) { + $q->only_unread( get_current_user_id() ); + }, + 'only_relevant' => function ( Note_Query_Builder $q ) { + $q->only_relevant( get_current_user_id() ); + }, + ]; + } + + /** + * Validates the create items endpoint. + * + * @param \WP_REST_Request $request + * + * @throws Data_Exception + * @throws Error_404 + */ + private function validate_create_items( \WP_REST_Request $request ) { + $parent_id = $request->get_param( 'parent_id' ); + + if ( ! $parent_id ) { + // The validation is related only if the new note should be reply. + return; + } + + /** @var Note $parent */ + $parent = Note::query()->find( $parent_id ); + + if ( ! $parent ) { + throw new Error_404(); + } + + if ( $parent->is_reply() ) { + throw new Data_Exception( + 'Cannot create reply on reply.', + 'rest_invalid_param', + [ 'status' => Http_Status::BAD_REQUEST ] + ); + } + + if ( $request->has_param( 'is_public' ) ) { + throw new Data_Exception( + "Cannot update 'is_public' on reply.", + 'rest_invalid_param', + [ 'status' => Http_Status::BAD_REQUEST ] + ); + } + } + + /** + * Validates the update item endpoint. + * + * @param \WP_REST_Request $request + * + * @throws Data_Exception + * @throws Error_404 + */ + private function validate_update_items( \WP_REST_Request $request ) { + /** @var Note $note */ + $note = Note::query()->find( $request->get_param( 'id' ) ); + + if ( ! $note ) { + throw new Error_404(); + } + + $has_invalid_reply_attributes = $request->has_param( 'is_resolved' ) || $request->has_param( 'is_public' ); + + if ( $note->is_reply() && $has_invalid_reply_attributes ) { + throw new Data_Exception( + "Cannot update 'is_resolved' or 'is_public' on reply.", + 'rest_invalid_param', + [ 'status' => Http_Status::BAD_REQUEST ] + ); + } + + // For notifications - To make sure that there are no redundant resolve notifications. + if ( $note->is_resolved === $request->get_param( 'is_resolved' ) ) { + throw new Data_Exception( + "'is_resolved' was already set on '{$note->is_resolved}'.", + 'rest_invalid_param', + [ 'status' => Http_Status::BAD_REQUEST ] + ); + } + } + + /** + * Handle note creation side-effects. + * + * @param array $event + * + * @return void + */ + protected function on_note_created( array $event ) { + foreach ( $event['mentioned'] as $user ) { + $user->notify( new User_Mentioned_Notification( $event['note'], $event['actor'] ) ); + } + + if ( $event['note']->is_reply() ) { + $relevant = User::query()->only_relevant_to_note( $event['note'] )->get(); + + foreach ( $relevant as $user ) { + $user->notify( new User_Replied_Notification( + $event['note'], + $event['actor'], + $event['mentioned']->pluck( 'ID' )->all() + ) ); + } + } + } + + /** + * Handle note update side-effects. + * + * @param array $event + * + * @return void + */ + protected function on_note_updated( array $event ) { + if ( ! empty( $event['mentioned'] ) ) { + foreach ( $event['mentioned'] as $user ) { + $user->notify( new User_Mentioned_Notification( $event['note'], $event['actor'] ) ); + } + } + + if ( ! empty( $event['resolved'] ) ) { + $relevant = User::query()->only_relevant_to_note( $event['note'] )->get(); + + foreach ( $relevant as $user ) { + $user->notify( new User_Resolved_Notification( + $event['note'], + $event['actor'] + ) ); + } + } + } + + /** + * Sanitize note content. + * + * - Trims empty lines & spaces from start/end of the string. + * - Encodes HTML entities. + * + * @param string $raw_content + * + * @return string + */ + private function sanitize_content( $raw_content ) { + return htmlentities( preg_replace( '/(^[\n\s]+|[\n\s]+$)/', '', $raw_content ) ); + } +} diff --git a/modules/notes/data/endpoints/read-status-endpoint.php b/modules/notes/data/endpoints/read-status-endpoint.php new file mode 100644 index 0000000..40207e1 --- /dev/null +++ b/modules/notes/data/endpoints/read-status-endpoint.php @@ -0,0 +1,131 @@ + [ + 'type' => 'array', + 'description' => 'The id\'s of the notes.', + 'items' => [ + 'type' => 'integer', + ], + 'required' => true, + ], + ]; + + $this->register_items_route( \WP_REST_Server::CREATABLE, $args ); + $this->register_items_route( \WP_REST_Server::DELETABLE, $args ); + } + + /** + * Mark notes as read by the current user. + * + * @param \WP_REST_Request $request + * + * @return \WP_REST_Response + */ + protected function create_items( $request ) { + $user_id = get_current_user_id(); + $notes = $this->get_notes( + $request->get_param( 'ids' ), + true + ); + + /** @var Note $note */ + foreach ( $notes as $note ) { + $reader = $note->readers->find( function ( User $user ) use ( $user_id ) { + return $user->ID === $user_id; + } ); + + if ( ! $reader ) { + $note->add_readers( [ $user_id ] ); + } + } + + return new \WP_REST_Response( [], Http_Status::CREATED ); + } + + /** + * Mark notes as unread by the current user. + * + * @param \WP_REST_Request $request + * + * @return \WP_REST_Response + */ + protected function delete_items( $request ) { + $user_id = get_current_user_id(); + $notes = $this->get_notes( $request->get_param( 'ids' ) ); + + /** @var Note $note */ + foreach ( $notes as $note ) { + $note->remove_readers( [ $user_id ] ); + } + + // TODO: Should return status 204 when the $e.data will support it + return new \WP_REST_Response( [], Http_Status::OK ); + } + + /** + * @inheritDoc + */ + public function get_permission_callback( $request ) { + $can_read_notes = false; + + foreach ( $this->get_notes( $request->get_param( 'ids' ) ) as $note ) { + $can_read_notes = current_user_can( Capabilities::READ_NOTES, $note ); + + if ( false === $can_read_notes ) { + break; + } + } + + return $can_read_notes; + } + + /** + * Get notes by their ids. + * + * @param array $ids + * @param bool $with_readers + * + * @return Collection + */ + private function get_notes( array $ids, $with_readers = false ) { + return Note::query() + ->where_in( 'id', $ids ) + ->when( $with_readers, function ( Note_Query_Builder $builder ) { + return $builder->with_readers(); + } ) + ->get(); + } +} diff --git a/modules/notes/data/endpoints/summary-endpoint.php b/modules/notes/data/endpoints/summary-endpoint.php new file mode 100644 index 0000000..c608877 --- /dev/null +++ b/modules/notes/data/endpoints/summary-endpoint.php @@ -0,0 +1,67 @@ +register_items_route( + \WP_REST_Server::READABLE, + $this->get_controller()->get_collection_params() + ); + } + + /** + * Index route. + * + * GET `/notes/summary` + * + * @param \WP_REST_Request $request + * + * @return array + */ + protected function get_items( $request ) { + $user_id = get_current_user_id(); + + $query = Note_Summary::query() + ->only_visible( $user_id ) + ->only_visible_posts( $user_id ); + + foreach ( $this->get_controller()->get_filters() as $param => $callback ) { + if ( $request->has_param( $param ) ) { + call_user_func( $callback, $query, $request->get_param( $param ) ); + } + } + + return [ + 'data' => $query->get(), + 'meta' => [], + ]; + } + + /** + * @inheritDoc + */ + public function get_permission_callback( $request ) { + return current_user_can( Capabilities::READ_NOTES ); + } +} diff --git a/modules/notes/data/endpoints/users-endpoint.php b/modules/notes/data/endpoints/users-endpoint.php new file mode 100644 index 0000000..0b8c7f1 --- /dev/null +++ b/modules/notes/data/endpoints/users-endpoint.php @@ -0,0 +1,131 @@ +register_items_route( + \WP_REST_Server::READABLE, + [ + 'limit' => [ + 'type' => 'integer', + 'description' => 'Limit the results.', + 'required' => false, + ], + 'order_by' => [ + 'type' => 'string', + 'description' => 'A column to order the results by.', + 'required' => false, + 'default' => 'display_name', + 'enum' => [ + 'user_nicename', + 'display_name', + 'user_registered', + ], + ], + 'order' => [ + 'type' => 'string', + 'description' => 'Results order direction.', + 'required' => false, + 'default' => 'asc', + 'enum' => [ + 'asc', + 'desc', + ], + ], + 'search' => [ + 'type' => 'string', + 'description' => 'Filter users by a search term.', + 'required' => false, + 'sanitize_callback' => function ( $value ) { + return wp_strip_all_tags( $value, true ); + }, + ], + ] + ); + } + + /** + * Index route. + * + * GET `/notes/users` + * + * @param \WP_REST_Request $request + * + * @return array + */ + protected function get_items( $request ) { + $users = User::query() + ->order_by( + $request->get_param( 'order_by' ), + $request->get_param( 'order' ) + ); + + foreach ( $this->get_filters() as $param => $callback ) { + if ( $request->has_param( $param ) ) { + call_user_func( $callback, $users, $request->get_param( $param ) ); + } + } + + $transformer = new User_Transformer(); + $transform_dependencies = []; + + if ( ! empty( $_GET['post_id'] ) ) { + $transform_dependencies['post_id'] = (int) $_GET['post_id']; + } + + return [ + 'data' => $users->get()->map( function ( User $user ) use ( $transformer, $transform_dependencies ) { + return $transformer->transform( $user, $transform_dependencies ); + } ), + 'meta' => [], + ]; + } + + /** + * @inheritDoc + */ + public function get_permission_callback( $request ) { + return current_user_can( Capabilities::CREATE_NOTES ); + } + + /** + * Get the Users filters. + * + * @return array + */ + protected function get_filters() { + return [ + 'limit' => function ( User_Query_Builder $q, $limit ) { + $q->limit( $limit ); + }, + 'search' => function ( User_Query_Builder $q, $search ) { + $q->where( 'user_nicename', 'LIKE', '%' . $search . '%' ) + ->or_where( 'user_email', 'LIKE', '%' . $search . '%' ) + ->or_where( 'display_name', 'LIKE', '%' . $search . '%' ); + }, + ]; + } +} diff --git a/modules/notes/database/migrations/add-author-display-name.php b/modules/notes/database/migrations/add-author-display-name.php new file mode 100644 index 0000000..8c3e350 --- /dev/null +++ b/modules/notes/database/migrations/add-author-display-name.php @@ -0,0 +1,27 @@ +add_columns( Module::TABLE_NOTES, [ + 'author_display_name' => 'varchar(250) null comment "Save the author name when the author was deleted." AFTER `author_id`', + ] ); + } + + /** + * @inheritDoc + */ + public function down() { + $this->drop_columns( Module::TABLE_NOTES, [ 'author_display_name' ] ); + } +} diff --git a/modules/notes/database/migrations/add-capabilities.php b/modules/notes/database/migrations/add-capabilities.php new file mode 100644 index 0000000..43dc282 --- /dev/null +++ b/modules/notes/database/migrations/add-capabilities.php @@ -0,0 +1,41 @@ +add_cap( $capability ); + } + } + } + + /** + * @inheritDoc + */ + public function down() { + $roles = array_values( wp_roles()->role_objects ); + + foreach ( $roles as $role ) { + if ( ! ( $role instanceof \WP_Role ) ) { + continue; + } + + foreach ( Capabilities::all() as $cap ) { + $role->remove_cap( $cap ); + } + } + } +} diff --git a/modules/notes/database/migrations/add-note-position.php b/modules/notes/database/migrations/add-note-position.php new file mode 100644 index 0000000..4d52623 --- /dev/null +++ b/modules/notes/database/migrations/add-note-position.php @@ -0,0 +1,27 @@ +add_columns( Module::TABLE_NOTES, [ + 'position' => 'text null comment "A JSON string that represents the position of the note inside the element in percentages. e.g. {x:10, y:15}" AFTER `status`', + ] ); + } + + /** + * @inheritDoc + */ + public function down() { + $this->drop_columns( Module::TABLE_NOTES, [ 'position' ] ); + } +} diff --git a/modules/notes/database/migrations/add-route-post-id.php b/modules/notes/database/migrations/add-route-post-id.php new file mode 100644 index 0000000..946ee3c --- /dev/null +++ b/modules/notes/database/migrations/add-route-post-id.php @@ -0,0 +1,27 @@ +add_columns( Module::TABLE_NOTES, [ + 'route_post_id' => 'bigint(20) unsigned null comment "The post id of the route that the note was created on." AFTER `route_title`', + ] ); + } + + /** + * @inheritDoc + */ + public function down() { + $this->drop_columns( Module::TABLE_NOTES, [ 'route_post_id' ] ); + } +} diff --git a/modules/notes/database/migrations/initial.php b/modules/notes/database/migrations/initial.php new file mode 100644 index 0000000..a261191 --- /dev/null +++ b/modules/notes/database/migrations/initial.php @@ -0,0 +1,72 @@ +create_table( Module::TABLE_NOTES, [ + 'id' => 'bigint(20) unsigned auto_increment primary key', + 'route_url' => 'text null comment "Clean url where the note was created."', + 'route_title' => 'varchar(255) null', + 'post_id' => 'bigint(20) unsigned null', + 'element_id' => 'varchar(60) null comment "The Elementor element ID the note is attached to."', + 'parent_id' => 'bigint(20) unsigned default 0 not null', + 'author_id' => 'bigint(20) unsigned null', + 'status' => 'varchar(20) default "publish" not null', + 'content' => 'longtext null', + 'is_resolved' => 'tinyint(1) default 0 not null', + 'is_public' => 'tinyint(1) default 1 not null', + 'last_activity_at' => 'datetime null', + 'created_at' => 'datetime not null', + 'updated_at' => 'datetime not null', + ] ); + + $this->create_indexes( + Module::TABLE_NOTES, + [ + 'route_url', + 'post_id', + 'element_id', + 'parent_id', + 'author_id', + 'status', + 'is_resolved', + 'is_public', + 'created_at', + 'updated_at', + 'last_activity_at', + ] + ); + + $this->create_table( Module::TABLE_NOTES_USERS_RELATIONS, [ + 'id' => 'bigint(20) unsigned auto_increment primary key', + 'type' => 'varchar(60) not null comment "The relation type between user and note (e.g mention, watch, read)."', + 'note_id' => 'bigint(20) unsigned not null', + 'user_id' => 'bigint(20) unsigned not null', + 'created_at' => 'datetime not null', + 'updated_at' => 'datetime not null', + ] ); + + $this->create_indexes( + Module::TABLE_NOTES_USERS_RELATIONS, + [ 'type', 'note_id', 'user_id' ] + ); + } + + /** + * @inheritDoc + */ + public function down() { + $this->drop_table( Module::TABLE_NOTES ); + $this->drop_table( Module::TABLE_NOTES_USERS_RELATIONS ); + } +} diff --git a/modules/notes/database/models/document.php b/modules/notes/database/models/document.php new file mode 100644 index 0000000..cf1bef4 --- /dev/null +++ b/modules/notes/database/models/document.php @@ -0,0 +1,97 @@ + self::TYPE_INTEGER, + ]; + + /** + * Override the default Query Builder. + * + * @param \wpdb|null $connection + * + * @return Query_Builder + */ + public static function query( \wpdb $connection = null ) { + // PHPCS has an error without any reason, the method 'query' is not related to the `wpdb` object. + return parent::query( $connection ) // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + ->select( [ + 'ID', + 'type' => 'postmeta.meta_value', + ] ) + ->left_join( function ( Join_Clause $j ) { + return $j->table( 'postmeta' ) + ->on_column( 'posts.ID', '=', 'postmeta.post_id' ) + ->on( 'postmeta.meta_key', '=', OriginalDocument::TYPE_META_KEY ); + } ); + } + + /** + * Get the posts table name. + * + * @return string + */ + public static function get_table() { + return 'posts'; + } + + /** + * Get the label of a document. + * + * @return string|null + */ + public function get_type_title() { + if ( ! $this->type ) { + return null; + } + + $type_classname = Plugin::elementor()->documents->get_document_type( $this->type ); + + return $type_classname ? $type_classname::get_title() : null; + } + + /** + * Return a JSON serialized representation of the User. + * + * @return array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() { + return [ + 'id' => $this->ID, + 'type' => $this->type, + 'type_title' => $this->get_type_title(), + ]; + } +} diff --git a/modules/notes/database/models/note-summary.php b/modules/notes/database/models/note-summary.php new file mode 100644 index 0000000..20ed439 --- /dev/null +++ b/modules/notes/database/models/note-summary.php @@ -0,0 +1,81 @@ + self::TYPE_INTEGER, + ]; + + /** + * @inheritDoc + */ + public function __construct( array $fields ) { + // Make sure each model comes with the full url alongside with the relative url. + $fields['full_url'] = get_site_url( null, $fields['url'] ); + + parent::__construct( $fields ); + } + + /** + * Override the default Query Builder. + * + * @param \wpdb|null $connection + * + * @return Note_Query_Builder + */ + public static function query( \wpdb $connection = null ) { + $table_name = static::get_table(); + + return ( new Note_Query_Builder( $connection ) ) + ->set_model( static::class ) + ->from( $table_name ) + ->select( [ + 'title' => 'route_title', + 'url' => 'route_url', + ] ) + ->add_count_select( "{$table_name}.id", 'notes_count' ) + ->group_by( 'route_url' ); + } + + /** + * Get the notes table name. + * + * @return string + */ + public static function get_table() { + return Module::TABLE_NOTES; + } +} diff --git a/modules/notes/database/models/note.php b/modules/notes/database/models/note.php new file mode 100644 index 0000000..b10e1cc --- /dev/null +++ b/modules/notes/database/models/note.php @@ -0,0 +1,471 @@ + 0, + 'y' => 0, + ]; + + /** + * Note's content. + * + * @var null|string + */ + public $content = null; + + /** + * Note's resolve status. + * + * @var bool + */ + public $is_resolved = false; + + /** + * Note's public status. + * + * @var bool + */ + public $is_public = true; + + /** + * Is the note read by the user. + * + * @var boolean + */ + public $is_read = false; + + /** + * Note's replies. + * + * @var Collection + */ + public $replies; + + /** + * Note's mentions. + * + * @var Collection + */ + public $mentions; + + /** + * Note's author. + * + * @var User + */ + public $author; + + /** + * Note's document + * + * @var Document + */ + public $document; + + /** + * Note's replies count. + * + * @var int + */ + public $replies_count = 0; + + /** + * Note's unread replies count. + * + * @var int + */ + public $unread_replies_count = 0; + + /** + * Note's readers. + * + * @var Collection + */ + public $readers; + + /** + * Note's creation time. + * + * @var \DateTime + */ + public $created_at; + + /** + * Note's last update time. + * + * @var \DateTime + */ + public $updated_at; + + /** + * Note's last activity time. + * + * @var \DateTime + */ + public $last_activity_at; + + /** + * User's capabilities for the current note. + * [ + * 'edit' => boolean, + * 'delete' => boolean, + * ] + * + * @var array + */ + public $user_can = []; + + /** + * Casts array. + * + * @var array + */ + protected static $casts = [ + 'id' => self::TYPE_INTEGER, + 'post_id' => self::TYPE_INTEGER, + 'route_post_id' => self::TYPE_INTEGER, + 'parent_id' => self::TYPE_INTEGER, + 'author_id' => self::TYPE_INTEGER, + 'position' => self::TYPE_JSON, + 'is_resolved' => self::TYPE_BOOLEAN, + 'is_public' => self::TYPE_BOOLEAN, + 'is_read' => self::TYPE_BOOLEAN, + 'replies' => self::TYPE_COLLECTION, + 'mentions' => self::TYPE_COLLECTION, + 'readers' => self::TYPE_COLLECTION, + 'replies_count' => self::TYPE_INTEGER, + 'unread_replies_count' => self::TYPE_INTEGER, + 'created_at' => self::TYPE_DATETIME_GMT, + 'updated_at' => self::TYPE_DATETIME_GMT, + 'last_activity_at' => self::TYPE_DATETIME_GMT, + ]; + + public function __construct( array $fields ) { + // Defaults must be empty collection, when there is no replies or mentions it should remain empty. + $this->replies = new Collection( [] ); + $this->mentions = new Collection( [] ); + $this->readers = new Collection( [] ); + + parent::__construct( $fields ); + } + + /** + * Override the default Query Builder. + * + * @param \wpdb|null $connection + * + * @return Note_Query_Builder + */ + public static function query( \wpdb $connection = null ) { + return ( new Note_Query_Builder( $connection ) )->from( static::get_table() ); + } + + /** + * Get the notes table name. + * + * @return string + */ + public static function get_table() { + return Module::TABLE_NOTES; + } + + /** + * Is the current note is top level note. + * + * @return bool + */ + public function is_thread() { + return 0 === $this->parent_id; + } + + /** + * Determine if the current note is a reply. + * + * @return bool + */ + public function is_reply() { + return ! $this->is_thread(); + } + + /** + * Get the thread ID of the current note. + * + * @return int + */ + public function get_thread_id() { + return $this->is_thread() ? $this->id : $this->parent_id; + } + + /** + * Get the note deep link. + * + * @param bool $force_auth - Whether to force authentication. Defaults to `true`. + * + * @return string + */ + public function get_url( $force_auth = true ) { + // NOTICE: Make sure that the returned URL is not dynamic! + return static::generate_url( + $this->get_thread_id(), + $this->route_url, + $force_auth + ); + } + + /** + * Generate a note deep link URL. + * + * @param string|int $id - Note ID. + * @param string $route_url - Note route URL. Required if `$force_auth = false`. + * @param bool $force_auth - Whether to force authentication. Defaults to `true`. Used in cases where the user + * should be passed through the proxy in order to force their authentication (since the + * "Notes" feature and the Web-CLI are available only for logged-in users). + * + * @return string + */ + public static function generate_url( $id = null, $route_url = '', $force_auth = true ) { + // Add a placeholder if ID doesn't exist (used for passing the note URL as a pattern). + $id = ( null === $id ) ? '{{NOTE_ID}}' : (int) $id; + + if ( $force_auth ) { + $page = sprintf( 'admin.php?page=%s¬e-id=%s', Admin_Page::PAGE_ID, $id ); + + return admin_url( $page ); + } + + $command = sprintf( '#e:run:notes/open?{"id":%s}', $id ); + $base_url = get_site_url( null, Utils::clean_url( $route_url ) ); + + return $base_url . $command; + } + + /** + * @shortcut `$this->add_user_relation()` + */ + public function add_readers( $user_ids = [] ) { + $this->add_user_relation( static::USER_RELATION_READ, $user_ids ); + } + + /** + * @shortcut `$this->remove_user_relation()` + */ + public function remove_readers( $user_ids = [] ) { + $this->remove_user_relation( static::USER_RELATION_READ, $user_ids ); + } + + /** + * @shortcut `$this->sync_user_relation()` + */ + public function sync_mentions( $user_keys = [], $key = 'ID' ) { + return $this->sync_user_relation( static::USER_RELATION_MENTION, $user_keys, $key ); + } + + /** + * @shortcut `$this->add_user_relation()` + */ + public function add_mentions( $user_ids = [] ) { + $this->add_user_relation( static::USER_RELATION_MENTION, $user_ids ); + } + + /** + * Remove old relations and add new ones. + * + * @param $type + * @param array $user_keys + * @param string $key + * + * @return Collection Only users with a newly created relation (excluding the existing ones). + */ + public function sync_user_relation( $type, array $user_keys, $key = 'ID' ) { + $users = User::query() + ->where_in( $key, $user_keys ) + ->get(); + + $already_has_relation = ( new Query_Builder() ) + ->select( [ 'user_id' ] ) + ->from( Module::TABLE_NOTES_USERS_RELATIONS ) + ->where( 'type', '=', $type ) + ->where( 'note_id', '=', $this->id ) + ->get() + ->pluck( 'user_id' ); + + $should_have_relation = $users + ->pluck( 'ID' ) + ->unique(); + + $should_remove = $already_has_relation->diff( $should_have_relation )->values(); + $should_insert = $should_have_relation->diff( $already_has_relation )->values(); + + // Delete all the previous relations. + $this->remove_user_relation( $type, $should_remove ); + + // Only the users that not already in relation. + $this->add_user_relation( $type, $should_insert ); + + // Return only the users that were inserted in the users_relations DB table. + return $users->filter( function ( User $user ) use ( $should_insert ) { + return in_array( $user->ID, $should_insert, true ); + } ); + } + + /** + * Remove user relation. + * + * @param $type + * @param array $user_ids + */ + public function remove_user_relation( $type, array $user_ids ) { + ( new Query_Builder() ) + ->table( Module::TABLE_NOTES_USERS_RELATIONS ) + ->where( 'note_id', '=', $this->id ) + ->where( 'type', '=', $type ) + ->where_in( 'user_id', $user_ids ) + ->delete(); + } + + /** + * Add user relation. + * + * @param $type + * @param array $user_ids + * + * @throws \Exception + */ + public function add_user_relation( $type, array $user_ids ) { + $now = gmdate( 'Y-m-d H:i:s' ); + + foreach ( $user_ids as $user_id ) { + ( new Query_Builder() ) + ->table( Module::TABLE_NOTES_USERS_RELATIONS ) + ->insert( [ + 'note_id' => $this->id, + 'user_id' => $user_id, + 'type' => $type, + 'created_at' => $now, + 'updated_at' => $now, + ] ); + } + } + + /** + * Add user capabilities to the Note and its replies. + * + * @param integer $user_id - User ID to use. + * @param bool $recursive - Whether to add the capabilities also to the replies. + * + * @return Note + */ + public function attach_user_capabilities( $user_id, $recursive = true ) { + $this->user_can = [ + 'edit' => user_can( $user_id, Capabilities::EDIT_NOTES, $this ), + 'delete' => user_can( $user_id, Capabilities::DELETE_NOTES, $this ), + ]; + + // Add the capabilities also to the replies. + if ( $recursive ) { + $this->replies = $this->replies->map( function ( Note $reply ) use ( $user_id ) { + $reply->user_can = [ + 'edit' => user_can( $user_id, Capabilities::EDIT_NOTES, $reply ), + 'delete' => user_can( $user_id, Capabilities::DELETE_NOTES, $reply ), + ]; + + return $reply; + } ); + } + + return $this; + } +} diff --git a/modules/notes/database/models/user.php b/modules/notes/database/models/user.php new file mode 100644 index 0000000..68768e0 --- /dev/null +++ b/modules/notes/database/models/user.php @@ -0,0 +1,130 @@ + self::TYPE_INTEGER, + ]; + + /** + * Initialize a new `User` object from a `WP_User` object. + * + * @param \WP_User $user - WP_User object. + * + * @return static + */ + public static function from_wp_user( \WP_User $user ) { + return new static( (array) $user->data ); + } + + /** + * Override the default Query Builder. + * + * @param \wpdb|null $connection + * + * @return \ElementorPro\Modules\Notes\Database\Query\User_Query_Builder() + */ + public static function query( \wpdb $connection = null ) { + return ( new User_Query_Builder( $connection ) )->from( static::get_table() ); + } + + /** + * Get the model's table name. + * + * @return string + */ + public static function get_table() { + return 'users'; + } + + /** + * Generate avatars urls based on user id. + * + * @param $id + * + * @return Collection + */ + public static function generate_avatars_urls( $id ) { + return ( new Collection( [ 24, 48, 96 ] ) )->map_with_keys( function ( $size ) use ( $id ) { + return [ $size => get_avatar_url( $id, [ 'size' => $size ] ) ]; + } ); + } + + /** + * Get the user's avatars. + * + * @return Collection + */ + public function get_avatars() { + return static::generate_avatars_urls( $this->ID ); + } +} diff --git a/modules/notes/database/notes-database-updater.php b/modules/notes/database/notes-database-updater.php new file mode 100644 index 0000000..11d337e --- /dev/null +++ b/modules/notes/database/notes-database-updater.php @@ -0,0 +1,45 @@ + new Initial(), + 2 => new Add_Capabilities(), + 3 => new Add_Author_Display_Name(), + 4 => new Add_Route_Post_Id(), + 5 => new Add_Note_Position(), + ]; + } + + /** + * @inheritDoc + */ + protected function get_db_version() { + return static::DB_VERSION; + } + + /** + * @inheritDoc + */ + protected function get_db_version_option_name() { + return static::OPTION_NAME; + } +} diff --git a/modules/notes/database/query/note-query-builder.php b/modules/notes/database/query/note-query-builder.php new file mode 100644 index 0000000..85431ca --- /dev/null +++ b/modules/notes/database/query/note-query-builder.php @@ -0,0 +1,540 @@ +with_trashed ) { + $this->where( 'status', '!=', Note::STATUS_TRASH ); + } + + return parent::compile_wheres(); + } + + /** + * Set the `with_trashed` flag to `true`. + * + * @return $this + */ + public function with_trashed() { + $this->with_trashed = true; + + return $this; + } + + /** + * Eager load the Note's replies. + * + * @param callable|null $callback - Callback that gets a `Note_Query_Builder` to customize the replies query. + * + * @return $this + */ + public function with_replies( callable $callback = null ) { + $key = 'replies'; + $foreign_key = 'parent_id'; + $local_key = 'id'; + $builder = Note::query(); + + return $this->add_with( $key, function ( Collection $notes ) use ( $callback, $key, $foreign_key, $local_key, $builder ) { + // Get all the replies. + $replies = $builder + ->where_in( + $foreign_key, + $notes->pluck( $local_key )->all() + ) + ->when( $callback, function ( Note_Query_Builder $q, callable $callback ) { + // Execute any user-defined callback. + // Used to extend the query (e.g. add another `where` or eager load relations). + call_user_func( $callback, $q ); + } ) + ->get() + ->group_by( $foreign_key ); // Group the replies by their thread. + + // Add the replies to each Note object. + return $notes->map( function ( $note ) use ( $replies, $key, $local_key ) { + $note[ $key ] = $replies->get( $note[ $local_key ], [] ); + + return $note; + } ); + } ); + } + + /** + * Eager load the Note's replies count. + * + * @return $this + */ + public function with_replies_count() { + return $this->add_sub_select( function ( Query_Builder $q ) { + $q->from( Module::TABLE_NOTES, 'replies' ) + ->select( [ 'replies.id' ], static::COLUMN_COUNT ) + ->where_column( 'replies.parent_id', '=', Module::TABLE_NOTES . '.id' ); + }, 'replies_count' ); + } + + /** + * Eager load the Note's readers. + * + * @return $this + */ + public function with_readers() { + return $this->add_with( 'readers', function ( Collection $notes ) { + $ids = $notes->pluck( 'id' )->all(); + + // Get all relations. + $pivot = ( new Query_Builder() ) + ->from( Module::TABLE_NOTES_USERS_RELATIONS ) + ->select( [ 'note_id', 'user_id' ] ) + ->where( 'type', '=', Note::USER_RELATION_READ ) + ->where_in( 'note_id', $ids ) + ->get(); + + $ids = $pivot->pluck( 'user_id' )->all(); + + // Exit if there are no readers. + if ( empty( $ids ) ) { + return $notes; + } + + // Get all users that are associated with the relations, and map them into `$user_id => User`. + $readers = User::query() + ->where_in( 'ID', $ids ) + ->get() + ->key_by( 'ID' ); + + // Attach a user to each pivot row & group by note id. + $pivot = $pivot->map( function ( $item ) use ( $readers ) { + $item['user'] = $readers->get( $item['user_id'], [] ); + + return $item; + } )->filter( function ( $item ) { + // Make sure that relations with non-existing users won't be returned. + return ! empty( $item['user'] ); + } )->group_by( 'note_id' ); + + // Add the readers to the note. + return $notes->map( function ( $note ) use ( $pivot ) { + $users = $pivot->get( $note['id'], [] ); + + $readers = ( new Collection( $users ) ) + ->unique( 'user_id' ) + ->pluck( 'user' ) + ->all(); + + $note['readers'] = $readers; + + return $note; + } ); + } ); + } + + /** + * Eager load the Note's author. + * + * @return $this + */ + public function with_author() { + return $this->add_with( 'author', function ( Collection $notes ) { + $ids = $notes + ->pluck( 'author_id' ) + ->unique() + ->values(); + + $authors = User::query() + ->where_in( 'ID', $ids ) + ->get() + ->key_by( 'ID' ); + + return $notes->map( function ( $note ) use ( $authors ) { + $note['author'] = $authors->get( $note['author_id'] ); + + return $note; + } ); + } ); + } + + /** + * Eager load the Note's document. + * + * @return $this + */ + public function with_document() { + return $this->add_with( 'document', function ( Collection $notes ) { + $ids = $notes + ->pluck( 'post_id' ) + ->unique() + ->values(); + + $documents = Document::query() + ->where_in( 'ID', $ids ) + ->get() + ->key_by( 'ID' ); + + return $notes->map( function ( $note ) use ( $documents ) { + $note['document'] = $documents->get( $note['post_id'] ); + + return $note; + } ); + } ); + } + + /** + * Eager load the Note's read state by a user ID. + * + * @param int $user_id - User ID to check. + * + * @return $this + */ + public function with_is_read( $user_id ) { + $alias = 'is_read'; + + if ( $this->is_column_selected( $alias ) ) { + return $this; + } + + // TODO: Maybe use JOIN. + return $this->add_sub_select( function ( Query_Builder $q ) use ( $user_id ) { + $q->from( Module::TABLE_NOTES_USERS_RELATIONS, 'users_relations' ) + ->select( [ 'users_relations.id' ], static::COLUMN_COUNT ) + ->where( 'type', '=', Note::USER_RELATION_READ ) + ->where_column( Module::TABLE_NOTES . '.id', '=', 'users_relations.note_id' ) + ->where( 'user_id', '=', $user_id ); + }, $alias ); + } + + /** + * Make sure that users without permissions to read private notes won't get them. + * + * @param integer $user_id - User ID to check. + * + * @return Note_Query_Builder + */ + public function only_visible( $user_id ) { + // User can read private notes - do nothing. + if ( user_can( $user_id, Capabilities::READ_OTHERS_PRIVATE_NOTES ) ) { + return $this; + } + + // User can read only public or their note. + return $this->where( function ( Query_Builder $q ) use ( $user_id ) { + $q->where( 'is_public', '=', true ) + ->or_where( 'author_id', '=', $user_id ); + } ); + } + + /** + * Filter only notes that their post is visible to the user. + * + * @param $user_id + * + * @return $this + */ + public function only_visible_posts( $user_id ) { + $post_types = ( new Collection( get_post_types() ) ) + ->map( function ( $post_type_value ) { + return get_post_type_object( $post_type_value ); + } ); + + $can_read_unpublished_post_types = $post_types + ->filter( function ( $post_type ) use ( $user_id ) { + return $post_type && user_can( $user_id, $post_type->cap->edit_posts ); + } ) + ->keys(); + + $can_read_private_post_types = $post_types + ->filter( function ( $post_type ) use ( $user_id ) { + return $post_type && user_can( $user_id, $post_type->cap->read_private_posts ); + } ) + ->keys(); + + foreach ( [ 'route_post_id', 'post_id' ] as $column ) { + $table_alias = "posts_{$column}_visible"; + + $this + ->left_join( + function ( Join_Clause $join ) use ( $table_alias, $column ) { + return $join->table( 'posts', $table_alias ) + ->on_column( "{$table_alias}.ID", '=', $column ); + } + ) + ->where( + function ( Query_Builder $query ) use ( $can_read_unpublished_post_types, $table_alias ) { + // If there is no post ID, there is nothing to check, + // OR if the post is published the user can view the post, + // OR if the user is allowed to view unpublished post (for the current `post_type`). + $query->where_null( "{$table_alias}.ID" ) + ->or_where( "{$table_alias}.post_status", '=', BaseDocument::STATUS_PUBLISH ) + ->or_where_in( "{$table_alias}.post_type", $can_read_unpublished_post_types ); + } + ) + ->where( + function ( Query_Builder $query ) use ( $user_id, $can_read_private_post_types, $table_alias ) { + // If there is no post ID, there is nothing to check, + // OR if the post is not private the user can see view post, + // OR if the current user is the author of the private post, so he can view the post, + // OR if the user is allowed to view private posts (for the current `post_type`). + $query->where_null( "{$table_alias}.ID" ) + ->or_where( "{$table_alias}.post_status", '!=', BaseDocument::STATUS_PRIVATE ) + ->or_where( "{$table_alias}.post_author", '=', $user_id ) + ->or_where_in( "{$table_alias}.post_type", $can_read_private_post_types ); + } + ); + } + + return $this; + } + + /** + * Filter only the notes that are relevant to the user. + * + * @param int $user_id - User ID to check. + * + * @return Note_Query_Builder + */ + public function only_relevant( $user_id ) { + // User replied to the thread. + $replied_to_thread = function ( Query_Builder $q ) use ( $user_id ) { + $q->select_raw( [ 1 ] ) + ->from( Module::TABLE_NOTES, 'e_notes_relevant_replies' ) + ->where_column( 'e_notes_relevant_replies.parent_id', '=', Module::TABLE_NOTES . '.id' ) + ->where( 'e_notes_relevant_replies.author_id', '=', $user_id ) + ->where( 'e_notes_relevant_replies.status', '!=', Note::STATUS_TRASH ); + }; + + // User is mentioned in the thread. + $mentioned_in_thread = function ( Query_Builder $q ) use ( $user_id ) { + $q->select_raw( [ 1 ] ) + ->table( Module::TABLE_NOTES_USERS_RELATIONS, 'e_notes_relevant_relation' ) + ->where_column( 'e_notes_relevant_relation.note_id', '=', Module::TABLE_NOTES . '.id' ) + ->where( 'e_notes_relevant_relation.user_id', '=', $user_id ) + ->where( 'e_notes_relevant_relation.type', '=', Note::USER_RELATION_MENTION ); + }; + + // User is mentioned in one of the replies. + $mentioned_in_replies = function ( Query_Builder $q ) use ( $user_id ) { + $q->select_raw( [ 1 ] ) + ->table( Module::TABLE_NOTES_USERS_RELATIONS, 'e_notes_relevant_relation_replies' ) + ->where_in( 'e_notes_relevant_relation_replies.note_id', function ( Query_Builder $q ) use ( $user_id ) { + $q->select( [ 'e_notes_relevant_replies_ids.id' ] ) + ->from( Module::TABLE_NOTES, 'e_notes_relevant_replies_ids' ) + ->where_column( 'e_notes_relevant_replies_ids.parent_id', '=', Module::TABLE_NOTES . '.id' ) + ->where( 'e_notes_relevant_replies_ids.status', '!=', Note::STATUS_TRASH ); + } ) + ->where( 'e_notes_relevant_relation_replies.user_id', '=', $user_id ) + ->where( 'e_notes_relevant_relation_replies.type', '=', Note::USER_RELATION_MENTION ); + }; + + return $this->where( function ( Query_Builder $q ) use ( $user_id, $replied_to_thread, $mentioned_in_thread, $mentioned_in_replies ) { + $q->where( 'author_id', '=', $user_id ) // User created the thread. + ->or_where_exists( $replied_to_thread ) + ->or_where_exists( $mentioned_in_thread ) + ->or_where_exists( $mentioned_in_replies ); + } ); + } + + /** + * Filter only unread notes. + * + * @param integer $user_id - User id that the notes are unread by. + * + * @return Note_Query_Builder + */ + public function only_unread( $user_id ) { + return $this + ->with_unread_replies_count( $user_id ) + ->with_is_read( $user_id ) + ->having_raw( '`unread_replies_count` > 0 OR `is_read` = 0' ); + } + + /** + * Filter only threads. + * + * @return Note_Query_Builder + */ + public function only_threads() { + return $this->where( 'parent_id', '=', 0 ); + } + + /** + * Filter only replies. + * + * @return Note_Query_Builder + */ + public function only_replies() { + return $this->where( 'parent_id', '!=', 0 ); + } + + /** + * Filter only trashed notes. + * + * @return Note_Query_Builder + */ + public function only_trashed() { + return $this->with_trashed() + ->where( 'status', '=', Note::STATUS_TRASH ); + } + + /** + * Eager load the Note's unread replies count by a user ID. + * + * @param int $user_id - User ID to check. + * + * @return Note_Query_Builder + */ + public function with_unread_replies_count( $user_id ) { + $alias = 'unread_replies_count'; + + if ( $this->is_column_selected( $alias ) ) { + return $this; + } + + return $this->add_sub_select( function ( Query_Builder $q ) use ( $user_id ) { + $q->select( [ 'e_replies_count.id' ], static::COLUMN_COUNT ) + ->from( Module::TABLE_NOTES, 'e_replies_count' ) + ->left_join( function ( Join_Clause $j ) use ( $user_id ) { + $j->table( + Module::TABLE_NOTES_USERS_RELATIONS, + 'e_replies_count_user_relations' + ) + ->on_column( + 'e_replies_count_user_relations.note_id', + '=', + 'e_replies_count.id' + ) + ->on( + 'e_replies_count_user_relations.type', + '=', + Note::USER_RELATION_READ + ) + ->on( + 'e_replies_count_user_relations.user_id', + '=', + $user_id + ); + } ) + ->where_column( 'e_replies_count.parent_id', '=', Module::TABLE_NOTES . '.id' ) + ->where_null( 'e_replies_count_user_relations.id' ); + }, $alias ); + } + + /** + * Extends base delete method to allow deleting all the related entities + * of the notes, including 'user relations' and 'replies'. + * + * @param false $include_related_entities + * + * @return bool|int + */ + public function delete( $include_related_entities = false ) { + if ( ! $include_related_entities ) { + return parent::delete(); + } + + // Get all the ids of the notes it wishes to delete. + $notes_ids = $this->select( [ 'id' ] ) + ->with_trashed() + ->get() + ->pluck( 'id' ); + + if ( $notes_ids->is_empty() ) { + return 0; + } + + // Get all the replies ids. + $replies_ids = Note::query() + ->with_trashed() + ->select( [ 'id' ] ) + ->where_in( 'parent_id', $notes_ids->values() ) + ->get() + ->pluck( 'id' ); + + // Merge the thread ids with the replies. + $all_relevant_notes_ids = $notes_ids->merge( $replies_ids ); + + // Delete all the users relations of the notes. + ( new Query_Builder() ) + ->table( Module::TABLE_NOTES_USERS_RELATIONS ) + ->where_in( 'note_id', $all_relevant_notes_ids->values() ) + ->delete(); + + // Delete all the notes. + return Note::query() + ->with_trashed() + ->where_in( 'id', $all_relevant_notes_ids->values() ) + ->delete(); + } + + /** + * Move notes to trash. + * + * @return bool|int + */ + public function trash() { + $now = gmdate( 'Y-m-d H:i:s' ); + + return $this->update( [ + 'status' => Note::STATUS_TRASH, + 'updated_at' => $now, + ] ); + } + + /** + * Restore notes from trash. + * + * @return bool|int + */ + public function restore() { + $now = gmdate( 'Y-m-d H:i:s' ); + + return $this + ->with_trashed() + ->where( 'status', '=', Note::STATUS_TRASH ) + ->update( [ + 'status' => Note::STATUS_PUBLISH, // TODO: It should be the last status + 'updated_at' => $now, + ] ); + } +} diff --git a/modules/notes/database/query/user-query-builder.php b/modules/notes/database/query/user-query-builder.php new file mode 100644 index 0000000..297ad98 --- /dev/null +++ b/modules/notes/database/query/user-query-builder.php @@ -0,0 +1,61 @@ +select( [ 'id', 'author_id' ] ) + ->where( 'id', '=', $note->id ) + ->when( $note->is_thread(), function ( Note_Query_Builder $q ) use ( $note ) { + $q->or_where( 'parent_id', '=', $note->id ); + } ) + ->when( $note->is_reply(), function ( Note_Query_Builder $q ) use ( $note ) { + $q->or_where( 'parent_id', '=', $note->parent_id ) + ->or_where( 'id', '=', $note->parent_id ); + } ) + ->get(); + + return $this->where_exists( function ( Query_Builder $q ) use ( $notes ) { + // User is mentioned in thread or in one of the replies. + $q->select_raw( [ 1 ] ) + ->table( Module::TABLE_NOTES_USERS_RELATIONS, 'relations' ) + ->where_in( 'relations.note_id', $notes->pluck( 'id' )->all() ) + ->where_column( 'relations.user_id', '=', 'users.id' ) + ->where( 'relations.type', '=', Note::USER_RELATION_MENTION ); + } ) + // User created the thread or one of the replies. + ->or_where_in( 'users.id', $notes->pluck( 'author_id' )->all() ); + } +} diff --git a/modules/notes/database/transformers/user-transformer.php b/modules/notes/database/transformers/user-transformer.php new file mode 100644 index 0000000..0e140ab --- /dev/null +++ b/modules/notes/database/transformers/user-transformer.php @@ -0,0 +1,61 @@ +add_capabilities( $this->map_properties( $user ), $dependencies ); + } + + /** + * Maps the user properties to new keys. + * + * @param User $user + * + * @return array + */ + protected function map_properties( User $user ) { + // TODO: This response might be visible to unauthorized users. + // DON'T INCLUDE ANY SENSITIVE DATA. + return [ + 'id' => $user->ID, + 'name' => $user->display_name, + 'url' => $user->user_url, + 'slug' => $user->user_nicename, + 'avatar_urls' => $user->get_avatars()->all(), + ]; + } + + /** + * Add user capabilities to the user object. + * + * @param array $user + * @param array $dependencies + * + * @return array + */ + protected function add_capabilities( array $user, $dependencies ) { + $user['capabilities']['notes']['can_read'] = Capabilities::can_read_notes( $user['id'] ); + + if ( ! empty( $dependencies['post_id'] ) ) { + $user['capabilities']['post']['can_edit'] = Capabilities::can_edit_post( $user['id'], $dependencies['post_id'] ); + } + + return $user; + } +} diff --git a/modules/notes/document-events.php b/modules/notes/document-events.php new file mode 100644 index 0000000..cd95ff6 --- /dev/null +++ b/modules/notes/document-events.php @@ -0,0 +1,122 @@ +clear_after_post_meta_update( $post_id, $meta_key ); + }, 10, 3 ); + + add_action( 'deleted_post_meta', function ( $_, $post_id, $meta_key ) { + $this->clear_after_post_meta_update( $post_id, $meta_key ); + }, 10, 3 ); + + add_action( 'deleted_post', function ( $post_id ) { + $this->clear_after_post_deleted( $post_id ); + } ); + + add_action( 'trashed_post', function ( $post_id ) { + $this->move_to_trash_after_post_trashed( $post_id ); + } ); + + add_action( 'untrashed_post', function ( $post_id ) { + $this->restore_from_trash_after_post_untrashed( $post_id ); + } ); + } + + /** + * Remove all the notes that their elements is not exist in the document anymore. + * + * @param $post_id + * @param $meta_key + */ + private function clear_after_post_meta_update( $post_id, $meta_key ) { + if ( '_elementor_data' !== $meta_key ) { + return; + } + + $document = Plugin::elementor()->documents->get( $post_id ); + + if ( ! $document || $document->is_revision() ) { + return; + } + + $elements_ids = $this->get_elements_ids( + $document->get_elements_data() + ); + + Note::query() + ->where( 'post_id', '=', $document->get_id() ) + ->where_not_in( 'element_id', $elements_ids ) + ->delete( true ); + } + + /** + * Remove all the notes that related to the post that was deleted. + * + * @param $post_id + */ + private function clear_after_post_deleted( $post_id ) { + Note::query() + ->where( 'post_id', '=', $post_id ) + ->delete( true ); + } + + /** + * Move notes to trash when their post trashed. + * + * @param $post_id + */ + private function move_to_trash_after_post_trashed( $post_id ) { + Note::query() + ->where( 'post_id', '=', $post_id ) + ->trash(); + } + + /** + * Restore notes when their post untrashed. + * + * @param $post_id + */ + private function restore_from_trash_after_post_untrashed( $post_id ) { + Note::query() + ->where( 'post_id', '=', $post_id ) + ->restore(); + } + + /** + * Get recursively all the ids of the elements. + * + * @param $elements + * + * @return array + */ + private function get_elements_ids( $elements ) { + if ( empty( $elements ) ) { + return []; + } + + return array_reduce( $elements, function ( $ids, $element ) { + if ( empty( $element['id'] ) ) { + return $ids; + } + + return array_merge( + $ids, + [ $element['id'] ], + empty( $element['elements'] ) ? [] : $this->get_elements_ids( $element['elements'] ) + ); + }, [] ); + } +} diff --git a/modules/notes/module.php b/modules/notes/module.php new file mode 100644 index 0000000..8efca77 --- /dev/null +++ b/modules/notes/module.php @@ -0,0 +1,229 @@ +get_css_assets_url( 'modules/notes/frontend' ), + [ 'elementor-icons' ], + ELEMENTOR_PRO_VERSION + ); + } + + /** + * Enqueue panel scripts. + */ + private function enqueue_main_scripts() { + wp_enqueue_script( + 'elementor-pro-notes', + $this->get_js_assets_url( 'notes/notes' ), + [ + // Change `$e` dependency based on the `Web-CLI` module. + Plugin::elementor()->modules_manager->get_modules( 'web-cli' ) + ? 'elementor-web-cli' + : 'elementor-common', + 'react', + 'react-dom', + ], + ELEMENTOR_PRO_VERSION, + true + ); + + wp_set_script_translations( 'elementor-pro-notes', 'elementor-pro' ); + } + + /** + * Enqueue marks scripts. + * + * @param bool $is_preview + */ + private function enqueue_app_initiator( $is_preview = false ) { + $dependencies = [ + 'react', + 'react-dom', + ]; + + if ( ! $is_preview ) { + // When loading in frontend, the app should be loaded after notes main script. + // There are some listeners that should be initialized before the app script loaded. + $dependencies[] = 'elementor-pro-notes'; + } + + wp_enqueue_script( + 'elementor-pro-notes-app-initiator', + $this->get_js_assets_url( 'notes/notes-app-initiator' ), + $dependencies, + ELEMENTOR_PRO_VERSION, + true + ); + + $this->print_config( 'elementor-pro-notes-app-initiator' ); + } + + /** + * Expose settings to the frontend under 'window.elementorNotesConfig'. + * + * @return void + */ + protected function add_config() { + $queried_object = get_queried_object(); + + $route = [ + 'title' => Utils::get_clean_document_title(), + // PHPCS - The url cleaned inside the clear_url method. + 'url' => Utils::clean_url( $_SERVER['REQUEST_URI'] ?? '' ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash + 'note_url_pattern' => Note::generate_url(), + 'post_id' => null, + 'is_elementor_library' => false, + ]; + + if ( $queried_object instanceof \WP_Post ) { + $route['url'] = Utils::clean_url( home_url( "?p={$queried_object->ID}" ) ); + $route['post_id'] = $queried_object->ID; + $route['is_elementor_library'] = Source_Local::CPT === $queried_object->post_type; + } + + $this->set_settings( 'route', $route ); + $this->set_settings( 'direction', is_rtl() ? 'rtl' : 'ltr' ); + $this->set_settings( 'is_debug', ( defined( 'ELEMENTOR_DEBUG' ) && ELEMENTOR_DEBUG ) ); + + $this->set_settings( 'current_user_can', [ + 'create' => current_user_can( Capabilities::CREATE_NOTES ), + 'create_users' => current_user_can( 'create_users' ), + 'edit_users' => current_user_can( 'edit_users' ), + ] ); + + $this->set_settings( 'urls', [ + 'admin_url_create_user' => get_admin_url( null, 'user-new.php' ), + 'admin_url_edit_user' => get_admin_url( null, 'user-edit.php' ), + 'avatar_defaults' => User::generate_avatars_urls( 0 ), + 'help_notes_features' => 'https://go.elementor.com/app-notes', + ] ); + } + + /** + * Define the module tables in `wpdb`. + * + * @return void + */ + private function define_tables() { + global $wpdb; + + $tables = [ + self::TABLE_NOTES, + self::TABLE_NOTES_USERS_RELATIONS, + ]; + + foreach ( $tables as $table ) { + $wpdb->$table = $wpdb->prefix . $table; + $wpdb->tables[] = $table; + } + } + + private function on_elementor_pro_init() { + $has_license = API::is_license_active() && API::is_licence_has_feature( static::LICENSE_FEATURE_NAME ); + + if ( ! $has_license ) { + return; + } + + // Things that should be happened if the feature is active (not depends on the current user) + $this->define_tables(); + + add_action( 'switch_blog', function () { + // Reinitialize the Notes tables when switching between sites on a multisite, since each site has its own tables prefix. + $this->define_tables(); + } ); + + ( new Capabilities() )->register(); + ( new Preferences() )->register(); + ( new Delete_User() )->register(); + ( new Personal_Data() )->register(); + ( new Notes_Database_Updater() )->register(); + ( new Admin_Bar() )->register(); + ( new Admin_Page() )->register(); + ( new Document_Events() )->register(); + ( new Usage() )->register(); + + Plugin::elementor()->data_manager_v2->register_controller( new Controller() ); + + // Things that should be happened if the current user can read notes. + if ( is_user_logged_in() && current_user_can( Capabilities::READ_NOTES ) ) { + add_action( 'template_redirect', function () { + // Only now the 'queried_object' is available for the config. + $this->add_config(); + } ); + + add_action( 'elementor/frontend/after_register_scripts', function () { + $is_preview = Plugin::elementor()->preview->is_preview(); + + if ( ! $is_preview ) { + $this->enqueue_main_scripts(); + } + + $this->enqueue_styles(); + $this->enqueue_app_initiator( $is_preview ); + } ); + + add_action( 'elementor/editor/before_enqueue_scripts', function () { + $this->enqueue_main_scripts(); + } ); + + add_filter( 'elementor-pro/editor/v2/packages', function ( $packages ) { + $packages[] = 'editor-notes'; + + return $packages; + } ); + } + } + + public function __construct() { + parent::__construct(); + + add_action( 'elementor_pro/init', function() { + $this->on_elementor_pro_init(); + } ); + } +} diff --git a/modules/notes/notifications/base-notes-notification.php b/modules/notes/notifications/base-notes-notification.php new file mode 100644 index 0000000..0aba2e9 --- /dev/null +++ b/modules/notes/notifications/base-notes-notification.php @@ -0,0 +1,94 @@ +note = $note; + $this->exclude = $exclude; + $this->actor = $actor; + } + + /** + * Get the notification payloads. + * + * @param User $notifiable + * + * @return array + */ + public function get_payloads( $notifiable ) { + $exclude = $this->exclude; + $exclude[] = $this->actor->ID; + + if ( in_array( $notifiable->ID, $exclude, true ) ) { + return []; + } + + if ( ! user_can( $notifiable->ID, Capabilities::READ_NOTES, $this->note ) ) { + return []; + } + + if ( ! Preferences::are_notifications_enabled( $notifiable->ID ) ) { + return []; + } + + return [ + $this->create_email_message( $notifiable ), + ]; + } + + /** + * Get the sender email & name. + * + * @return string[] + */ + protected function get_sender() { + return [ + get_bloginfo( 'admin_email' ), + $this->actor->display_name . ' (' . esc_html__( 'via Elementor', 'elementor-pro' ) . ')', + ]; + } + + /** + * Initialize an `Email_Message` for the current notification. + * + * @param $notifiable + * + * @return \ElementorPro\Core\Integrations\Actions\Email\Email_Message + */ + abstract protected function create_email_message( $notifiable ); +} diff --git a/modules/notes/notifications/user-mentioned-notification.php b/modules/notes/notifications/user-mentioned-notification.php new file mode 100644 index 0000000..bb7ebd9 --- /dev/null +++ b/modules/notes/notifications/user-mentioned-notification.php @@ -0,0 +1,35 @@ +note->get_thread_id(), + get_bloginfo( 'name' ), + $this->note->route_title + ); + + return ( new Email_Message() ) + ->from( ...$this->get_sender() ) + ->to( $notifiable->user_email, $notifiable->display_name ) + ->subject( $subject ) + ->view( __DIR__ . '/views/email.php', [ + 'actor' => $this->actor->display_name, + /* translators: 1: User display name, 2: Page name, 3: Site name. */ + 'heading' => __( '%1$s mentioned you on %2$s at %3$s', 'elementor-pro' ), + 'page' => $this->note->route_title, + 'site_name' => get_bloginfo( 'name' ), + 'note_content' => $this->note->content, + 'note_url' => $this->note->get_url(), + ] ); + } +} diff --git a/modules/notes/notifications/user-replied-notification.php b/modules/notes/notifications/user-replied-notification.php new file mode 100644 index 0000000..93c48c4 --- /dev/null +++ b/modules/notes/notifications/user-replied-notification.php @@ -0,0 +1,35 @@ +note->get_thread_id(), + get_bloginfo( 'name' ), + $this->note->route_title + ); + + return ( new Email_Message() ) + ->from( ...$this->get_sender() ) + ->to( $notifiable->user_email, $notifiable->display_name ) + ->subject( $subject ) + ->view( __DIR__ . '/views/email.php', [ + 'actor' => $this->actor->display_name, + /* translators: 1: User display name, 2: Page name, 3: Site name. */ + 'heading' => __( '%1$s replied to a note on %2$s at %3$s', 'elementor-pro' ), + 'page' => $this->note->route_title, + 'site_name' => get_bloginfo( 'name' ), + 'note_content' => $this->note->content, + 'note_url' => $this->note->get_url(), + ] ); + } +} diff --git a/modules/notes/notifications/user-resolved-notification.php b/modules/notes/notifications/user-resolved-notification.php new file mode 100644 index 0000000..3139b87 --- /dev/null +++ b/modules/notes/notifications/user-resolved-notification.php @@ -0,0 +1,35 @@ +note->get_thread_id(), + get_bloginfo( 'name' ), + $this->note->route_title + ); + + return ( new Email_Message() ) + ->from( ...$this->get_sender() ) + ->to( $notifiable->user_email, $notifiable->display_name ) + ->subject( $subject ) + ->view( __DIR__ . '/views/email.php', [ + 'actor' => $this->actor->display_name, + /* translators: 1: User display name, 2: Page name, 3: Site name. */ + 'heading' => __( '%1$s resolved a note on %2$s at %3$s', 'elementor-pro' ), + 'page' => $this->note->route_title, + 'site_name' => get_bloginfo( 'name' ), + 'note_content' => $this->note->content, + 'note_url' => $this->note->get_url(), + ] ); + } +} diff --git a/modules/notes/notifications/views/email.php b/modules/notes/notifications/views/email.php new file mode 100644 index 0000000..00858b3 --- /dev/null +++ b/modules/notes/notifications/views/email.php @@ -0,0 +1,108 @@ + + +
    +
    +

    + %s
    ', + esc_html( $actor ) + ), + sprintf( + '"%s"
    ', + esc_html( $page ) + ), + esc_html( $site_name ) + ); + + // The above resolves to "UserName mentioned you on "*PageName*" at SiteName" + ?> +

    + +

    + "" +

    + + + + +
    + +
    +

    + + +
    + + ', esc_url( admin_url( 'profile.php' ) ) ), + '' + ); + ?> +

    + +

    + +

    + + + <?php echo esc_attr__( 'Elementor Logo', 'elementor-pro' ); ?> + +
    +
    diff --git a/modules/notes/usage.php b/modules/notes/usage.php new file mode 100644 index 0000000..89ec99d --- /dev/null +++ b/modules/notes/usage.php @@ -0,0 +1,317 @@ +get_usage_data(); + + return $params; + } ); + } + + /** + * Get the Notes feature usage data. + * + * @return array + */ + private function get_usage_data() { + return [ + 'threads' => $this->get_threads_usage(), + 'replies' => $this->get_replies_usage(), + 'mentions' => $this->get_mentions_usage(), + 'first_interaction' => $this->get_first_interaction(), + 'last_interaction' => $this->get_last_interaction(), + ]; + } + + /** + * Get the Threads' usage data. + * + * @return array + */ + private function get_threads_usage() { + return [ + 'total' => Note::query() + ->only_threads() + ->count(), + + 'resolved' => Note::query() + ->only_threads() + ->where( 'is_resolved', '=', true ) + ->count(), + + 'trashed' => Note::query() + ->only_threads() + ->only_trashed() + ->count(), + + 'users_used' => $this->get_notes_users_used( static::THREADS ), + + 'document_type' => $this->get_notes_document_types( static::THREADS ), + ]; + } + + /** + * Get the replies' usage data. + * + * @return array + */ + private function get_replies_usage() { + return [ + 'total' => Note::query() + ->only_replies() + ->count(), + + 'trashed' => Note::query() + ->only_replies() + ->only_trashed() + ->count(), + + 'replied_threads' => (int) Note::query() + ->disable_model_initiation() + ->only_replies() + ->select_raw( [ 'COUNT(DISTINCT `parent_id`) AS `count`' ] ) + ->first()['count'], + + 'users_used' => $this->get_notes_users_used( static::REPLIES ), + + 'document_type' => $this->get_notes_document_types( static::REPLIES ), + ]; + } + + /** + * Get the mentions' usage data. + * + * @return array + */ + private function get_mentions_usage() { + return [ + 'total' => ( new Query_Builder() ) + ->table( Module::TABLE_NOTES_USERS_RELATIONS, 'e_notes_relations' ) + ->join( function ( Join_Clause $j ) { + $j->table( Module::TABLE_NOTES, 'e_notes' ) + ->on_column( 'e_notes.id', '=', 'e_notes_relations.note_id' ); + } ) + ->where( 'type', '=', Note::USER_RELATION_MENTION ) + ->where( 'e_notes.status', '!=', Note::STATUS_TRASH ) + ->count(), + + 'users_used' => $this->get_mentions_users_used(), + + 'document_type' => ( new Query_Builder() ) + ->select( [ 'type' => 'postmeta.meta_value' ] ) + ->add_count_select( '*', 'count' ) + ->from( Module::TABLE_NOTES_USERS_RELATIONS, 'e_notes_relations' ) + ->join( function ( Join_Clause $j ) { + $j->table( Module::TABLE_NOTES, 'e_notes' ) + ->on_column( 'e_notes.id', '=', 'e_notes_relations.note_id' ); + } ) + ->join( function ( Join_Clause $j ) { + $j->table( 'posts' ) + ->on_column( 'posts.ID', '=', 'e_notes.post_id' ); + } ) + ->join( function ( Join_Clause $j ) { + $j->table( 'postmeta' ) + ->on_column( 'posts.ID', '=', 'postmeta.post_id' ) + ->on( 'postmeta.meta_key', '=', Document::TYPE_META_KEY ); + } ) + ->where( 'e_notes.status', '!=', Note::STATUS_TRASH ) + ->group_by( 'postmeta.meta_value' ) + ->get() + ->map_with_keys( function ( $row ) { + return [ $row['type'] => (int) $row['count'] ]; + } ) + ->all(), + ]; + } + + /** + * Get the first user interaction with the Notes feature. + * + * @return string|null + */ + private function get_first_interaction() { + $note = Note::query() + ->with_trashed() + ->select( [ 'created_at' ] ) + ->order_by( 'created_at' ) + ->first(); + + return $note && $note->created_at + ? $note->created_at->format( 'Y-m-d\TH:i:sO' ) + : null; + } + + /** + * Get the last user interaction with the Notes feature. + * + * @return string|null + */ + private function get_last_interaction() { + $note = Note::query() + ->with_trashed() + ->select( [ 'last_activity_at' ] ) + ->order_by( 'last_activity_at', 'desc' ) + ->first(); + + return $note && $note->last_activity_at + ? $note->last_activity_at->format( 'Y-m-d\TH:i:sO' ) + : null; + } + + /** + * Get the count of `$type` usages grouped by user role. + * + * e.g. for 10 replies it can be something like: `{ admin: 7, subscriber: 3 }` + * + * @param string $type - Threads or replies. + * + * @return array + */ + private function get_notes_users_used( $type ) { + $query = Note::query() + ->disable_model_initiation() + ->select( [ 'user_used_id' => Module::TABLE_NOTES . '.author_id' ] ) + ->add_count_select( Module::TABLE_NOTES . '.id', 'count' ) + ->group_by( 'user_used_id' ); + + switch ( $type ) { + case static::THREADS: + $query->only_threads(); + break; + + case static::REPLIES: + $query->only_replies(); + break; + } + + return $this->normalize_users_used( $query->get() ); + } + + /** + * Get the count of mentions usages grouped by user role. + * + * e.g. for 10 mentions it can be something like: `{ admin: 7, subscriber: 3 }` + * + * @return array + */ + private function get_mentions_users_used() { + $query = ( new Query_Builder() ) + ->from( Module::TABLE_NOTES_USERS_RELATIONS, 'e_notes_relations' ) + ->select( [ 'user_used_id' => 'e_notes.author_id' ] ) + ->add_count_select( 'e_notes_relations.id', 'count' ) + ->join( function ( Join_Clause $j ) { + $j->table( Module::TABLE_NOTES, 'e_notes' ) + ->on_column( 'e_notes_relations.note_id', '=', 'e_notes.id' ); + } ) + ->where( 'e_notes_relations.type', '=', Note::USER_RELATION_MENTION ) + ->where( 'e_notes.status', '!=', Note::STATUS_TRASH ) + ->group_by( 'e_notes.author_id' ); + + return $this->normalize_users_used( $query->get() ); + } + + /** + * Normalize `users_used` query results into counts array grouped by user role. + * + * @param Collection $query_results + * + * @return array + */ + private function normalize_users_used( Collection $query_results ) { + if ( $query_results->is_empty() ) { + return []; + } + + $results = $query_results->map_with_keys( function ( $row ) { + return [ $row['user_used_id'] => (int) $row['count'] ]; + } ); + + /** + * @type \WP_User[] $users + */ + $users = get_users( [ + 'include' => $results->keys(), + ] ); + + $counts = []; + + foreach ( $users as $user ) { + /** + * WordPress also uses the first role. + * + * @see https://github.com/WordPress/WordPress/blob/57039311720709d55e96e1a074414ebadba64e00/wp-admin/user-edit.php#L419-L435 + */ + $role = reset( $user->roles ); + + if ( ! isset( $counts[ $role ] ) ) { + $counts[ $role ] = 0; + } + + $counts[ $role ] += $results->get( $user->ID ); + } + + return $counts; + } + + /** + * Get the count of `$type` usages grouped by document type. + * + * @param string $type - Threads or replies. + * + * @return array + */ + private function get_notes_document_types( $type ) { + $query = Note::query() + ->disable_model_initiation() + ->select( [ 'type' => 'postmeta.meta_value' ] ) + ->add_count_select( '*', 'count' ) + ->join( function ( Join_Clause $j ) { + $j->table( 'posts' ) + ->on_column( 'posts.ID', '=', Module::TABLE_NOTES . '.post_id' ); + } ) + ->join( function ( Join_Clause $j ) { + $j->table( 'postmeta' ) + ->on_column( 'posts.ID', '=', 'postmeta.post_id' ) + ->on( 'postmeta.meta_key', '=', Document::TYPE_META_KEY ); + } ) + ->group_by( 'postmeta.meta_value' ); + + switch ( $type ) { + case static::THREADS: + $query->only_threads(); + break; + + case static::REPLIES: + $query->only_replies(); + break; + } + + return $query->get() + ->map_with_keys( function ( $row ) { + return [ $row['type'] => (int) $row['count'] ]; + } ) + ->all(); + } +} diff --git a/modules/notes/user/capabilities.php b/modules/notes/user/capabilities.php new file mode 100644 index 0000000..d9fa2a7 --- /dev/null +++ b/modules/notes/user/capabilities.php @@ -0,0 +1,282 @@ +filter( function ( $cap ) use ( $user ) { + return ! user_can( $user, $cap ); + } ) + ->is_empty(); + } + + /** + * Register actions and hooks + */ + public function register() { + add_filter( 'map_meta_cap', function ( $caps, $cap, $user_id, $args ) { + return $this->map_meta_cap( $caps, $cap, $user_id, $args ); + }, 10, 4 ); + + add_action( 'edit_user_profile', function ( \WP_User $user ) { + $this->render_edit_user_profile_options( $user ); + } ); + + add_action( 'edit_user_profile_update', function ( $user_id ) { + $this->update_user_capabilities( $user_id ); + } ); + } + + /** + * Add or remove notes capabilities based on the permission checkbox. + * + * @param $user_id + */ + public function update_user_capabilities( $user_id ) { + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified in `wp_verify_nonce` + $wpnonce = Utils::_unstable_get_super_global_value( $_POST, '_wpnonce' ); + $verified_nonce = wp_verify_nonce( $wpnonce, 'update-user_' . $user_id ); + + if ( ! $verified_nonce ) { + return; + } + + $user = get_user_by( 'id', $user_id ); + + if ( ! $this->can_edit_capabilities_of( $user ) ) { + return; + } + + $should_add_cap = ! empty( $_POST[ static::ENABLE_PERMISSIONS_OPTION ] ); + + foreach ( static::basic() as $cap ) { + if ( $should_add_cap ) { + $user->add_cap( $cap ); + } else { + $user->remove_cap( $cap ); + } + } + } + + /** + * Render the permission checkbox in the user edit page. + * + * @param \WP_User $user + */ + public function render_edit_user_profile_options( \WP_User $user ) { + if ( ! $this->can_edit_capabilities_of( $user ) ) { + return; + } + + $option_name = static::ENABLE_PERMISSIONS_OPTION; + + ?> +
    +

    + + + + + + + modules_manager->get_modules( 'web-cli' ) && // Check if the web-cli is exists (BC support) + $user && + ! in_array( 'administrator', $user->roles, true ); // Admin permissions cannot be changed. + } + + /** + * Handle the capabilities of the notes + * + * @param string[] $caps + * @param string $cap + * @param int $user_id + * @param array $args + * + * @return array + */ + private function map_meta_cap( array $caps, $cap, $user_id, array $args ) { + if ( + ! in_array( $cap, static::all(), true ) || // Handle only elementor notes capabilities. + empty( $args[0] ) // Checking for capability without provide a specific note id. + ) { + return $caps; + } + + $note = $args[0] instanceof Note + ? $args[0] + : Note::query()->find( $args[0] ); + + // When note not found don't let the user do nothing. + if ( ! $note ) { + $caps[] = 'do_not_allow'; + + return $caps; + } + + // When the user doesn't have read access to one of the post_ids (post_id, route_post_id), + // any other permission is not allowed. + $can_read_related_posts = ( new Collection( [ $note->route_post_id, $note->post_id ] ) ) + ->unique() + ->filter( function ( $post_id ) use ( $user_id ) { + if ( ! $post_id ) { + return false; + } + + $post_type = get_post_type_object( + get_post_type( $post_id ) + ); + + return ! $post_type || ! user_can( $user_id, $post_type->cap->read_post, $post_id ); + } ) + ->is_empty(); + + if ( ! $can_read_related_posts ) { + $caps[] = 'do_not_allow'; + + return $caps; + } + + // If the current user is the author of the notes there are + // no extra caps to add. + if ( $note->author_id === $user_id ) { + return $caps; + } + + // If the note is private and the current user is not the author of the note + // It adds "read others private notes" capability. + // Note: when $args[0] is provided on "create note" it refers to the "parent_id" and not + // to the actual new note. + if ( + ! $note->is_public + && in_array( $cap, [ static::READ_NOTES, static::CREATE_NOTES ], true ) + ) { + $caps[] = static::READ_OTHERS_PRIVATE_NOTES; + } + + // When trying to edit a note, and the current user is not the author of the note. + if ( static::EDIT_NOTES === $cap ) { + $caps[] = static::EDIT_OTHERS_NOTES; + } + + // When trying to delete a note, and the current user is not the author of the note. + if ( static::DELETE_NOTES === $cap ) { + $caps[] = static::DELETE_OTHERS_NOTES; + } + + return $caps; + } + + /** + * Check whether a user has access to Notes. + * + * @param int $user_id + * + * @return bool + */ + public static function can_read_notes( $user_id ) { + return user_can( $user_id, static::READ_NOTES ); + } + + /** + * Check whether a user has edit access to specific post. + * + * @param int $user_id + * @param int $post_id + * + * @return bool + */ + public static function can_edit_post( $user_id, $post_id ) { + if ( empty( $user_id ) || empty( $post_id ) ) { + return false; + } + + return user_can( $user_id, static::EDIT_POST, $post_id ); + } +} diff --git a/modules/notes/user/delete-user.php b/modules/notes/user/delete-user.php new file mode 100644 index 0000000..ed15e5b --- /dev/null +++ b/modules/notes/user/delete-user.php @@ -0,0 +1,76 @@ +on_user_delete_form( $users_to_delete ); + }, 10, 2 ); + + add_action( 'delete_user', function ( $id, $reassign, $user ) { + $this->on_delete_user( $user ); + }, 10, 3 ); + } + + /** + * Update `author_display_name` on the note before deleting the author, in order + * to allow showing the author display name in the UI even when the user has been deleted. + * + * @param \WP_User $user + */ + private function on_delete_user( \WP_User $user ) { + Note::query() + ->with_trashed() + ->where( 'author_id', '=', $user->ID ) + ->update( [ 'author_display_name' => $user->display_name ] ); + } + + /** + * Add a note on user deletion form. + * + * @param $users_to_delete + */ + private function on_user_delete_form( $users_to_delete ) { + $notes_count = Note::query() + ->where_in( 'author_id', $users_to_delete ) + ->count(); + + if ( 0 === $notes_count ) { + return; + } + + ?> +

    + +

    +

    + +

    +

    + +

    + add_exporter( $exporters ); + } ); + } + + /** + * Get the data key for the exporter. + * + * @return string + */ + public function get_key() { + return static::WP_KEY; + } + + /** + * Get the exporter friendly name. + * + * @return string + */ + public function get_title() { + return esc_html__( 'Elementor Notes', 'elementor-pro' ); + } + + /** + * Add the Notes' exporter to the list of exporters. + * + * @param array $exporters + * + * @return array + */ + private function add_exporter( array $exporters ) { + $exporters[ $this->get_key() ] = [ + 'exporter_friendly_name' => $this->get_title(), + 'callback' => function ( $email ) { + return $this->export_data( $email ); + }, + ]; + + return $exporters; + } + + /** + * Export all the notes related to specific email. + * + * @param string $email + * + * @return array + */ + private function export_data( $email ) { + $user_id = User::query() + ->where( 'user_email', '=', $email ) + ->pluck( 'ID' ) + ->first(); + + if ( ! $user_id ) { + return [ + 'data' => [], + 'done' => true, + ]; + } + + $data = Note::query() + ->with_trashed() + ->where( 'author_id', '=', $user_id ) + ->get() + ->map( function ( Note $note ) { + return [ + 'group_id' => $this->get_key(), + 'group_label' => $this->get_title(), + 'item_id' => "{$this->get_key()}-{$note->id}", + 'data' => [ + [ + 'name' => esc_html__( 'ID', 'elementor-pro' ), + 'value' => $note->id, + ], + [ + 'name' => esc_html__( 'Parent ID', 'elementor-pro' ), + 'value' => $note->parent_id, + ], + [ + 'name' => esc_html__( 'Status', 'elementor-pro' ), + 'value' => $note->status, + ], + [ + 'name' => esc_html__( 'Content', 'elementor-pro' ), + 'value' => $note->content, + ], + [ + 'name' => esc_html__( 'Created At', 'elementor-pro' ), + 'value' => $note->created_at->format( 'Y-m-d H:i:s' ), + ], + ], + ]; + } ) + ->all(); + + return [ + 'data' => $data, + 'done' => true, + ]; + } +} diff --git a/modules/notes/user/preferences.php b/modules/notes/user/preferences.php new file mode 100644 index 0000000..40de379 --- /dev/null +++ b/modules/notes/user/preferences.php @@ -0,0 +1,113 @@ +add_personal_options_settings( $user ); + } ); + + add_action( 'personal_options_update', function ( $user_id ) { + $this->update_personal_options_settings( $user_id ); + } ); + } + + /** + * Determine if notifications are enabled for a user. + * + * @param int $user_id - User ID. + * + * @return bool + */ + public static function are_notifications_enabled( $user_id ) { + return ! ! Utils::get_user_option_with_default( static::ENABLE_NOTIFICATIONS, $user_id, true ); + } + + /** + * Add settings to the "Personal Options". + * + * @param \WP_User $user - User object. + * + * @return void + */ + protected function add_personal_options_settings( \WP_User $user ) { + if ( ! $this->has_permissions_to_edit_user( $user->ID ) ) { + return; + } + + $option_name = static::ENABLE_NOTIFICATIONS; + $value = Utils::get_user_option_with_default( $option_name, $user->ID, '1' ); + + ?> +

    + + + + + + + has_permissions_to_edit_user( $user_id ) ) { + return; + } + + $option_name = static::ENABLE_NOTIFICATIONS; + $value = empty( $_POST[ $option_name ] ) ? '0' : '1'; + + update_user_option( $user_id, $option_name, sanitize_text_field( $value ) ); + } + + /** + * Determine if the current user has permission to view/change notes preferences of a user. + * + * @param int $user_id + * + * @return bool + */ + protected function has_permissions_to_edit_user( $user_id ) { + return ( + current_user_can( Capabilities::READ_NOTES ) && + current_user_can( 'edit_user', $user_id ) + ); + } +} diff --git a/modules/notes/utils.php b/modules/notes/utils.php new file mode 100644 index 0000000..3d0a62d --- /dev/null +++ b/modules/notes/utils.php @@ -0,0 +1,114 @@ +filter( function ( $value, $key ) use ( $remove_if_start_with, $remove_if_end_with, $remove_if_is ) { + foreach ( $remove_if_start_with as $term ) { + if ( 0 === strpos( $key, $term ) ) { + return false; + }; + } + + foreach ( $remove_if_end_with as $term ) { + if ( 1 === preg_match( "/{$term}$/", $key ) ) { + return false; + }; + } + + foreach ( $remove_if_is as $term ) { + if ( $key === $term ) { + return false; + } + } + + return true; + } ); + + if ( ! $query->is_empty() ) { + $url = implode( '?', [ + $url, + build_query( $query->all() ), + ] ); + } + } + + return esc_url_raw( rtrim( $url, '/' ) ); + } + + /** + * @param $value + * + * @return bool + */ + public static function validate_url_or_relative_url( $value ) { + $is_valid_url = filter_var( $value, FILTER_VALIDATE_URL ); + + if ( $is_valid_url ) { + return (bool) $is_valid_url; + } + + // Check if the $value is relative url. + return (bool) filter_var( 'https://localhost/' . ltrim( $value, '/' ), FILTER_VALIDATE_URL ); + } + + /** + * Clean the WP document title and return it. + * + * @return string + */ + public static function get_clean_document_title() { + $filter = function ( $title ) { + if ( is_home() || is_front_page() ) { + return [ esc_html__( 'Home page', 'elementor-pro' ) ]; + } + + unset( $title['site'] ); + + return $title; + }; + + add_filter( 'document_title_parts', $filter ); + + $title = wp_get_document_title(); + + remove_filter( 'document_title_parts', $filter ); + + return $title; + } +} diff --git a/modules/page-transitions/module.php b/modules/page-transitions/module.php new file mode 100644 index 0000000..11e38c0 --- /dev/null +++ b/modules/page-transitions/module.php @@ -0,0 +1,877 @@ +add_actions(); + } + + /** + * Get the module name. + * + * @return string + */ + public function get_name() { + return self::NAME; + } + + /** + * Register the Page Transitions controls. + * + * @param $element Controls_Stack + * @param $section_id string + * + * @return void + */ + public function register_controls( Controls_Stack $element, $section_id ) { + // Remove Page Transitions Banner (from Core version). + if ( 'section_page_transitions_teaser' !== $section_id ) { + return; + } + + // Delete the Teaser message. + Plugin::elementor()->controls_manager->remove_control_from_stack( + $element->get_unique_name(), + [ + 'section_page_transitions_teaser', + 'page_transitions_teaser', + ] + ); + + // Replace the teaser message with actual controls. + $this->register_page_transitions_controls( $element ); + } + + /** + * Retrieve a control ID prefixed with the tab ID. + * + * @param string $id - Control id. + * + * @return string + */ + private function get_control_id( $id ) { + $tab_id = Settings_Page_Transitions::TAB_ID; + $tab_id = str_replace( '-', '_', $tab_id ); + + return $tab_id . '_' . $id; + } + + /** + * Add a Page Transitions preview button. + * + * @param Controls_Stack $controls_stack - Controls Stack context to add the button to. + * @param string $prefix - Button ID prefix. + * + * @return void + */ + private function add_preview_button( $controls_stack, $prefix ) { + $controls_stack->add_control( + $this->get_control_id( $prefix . '_play_button' ), + [ + 'type' => Controls_Manager::BUTTON, + 'label_block' => true, + 'text' => esc_html__( 'Preview Page Transition', 'elementor-pro' ), + 'button_type' => 'default e-page-transition-preview', + 'separator' => 'before', + 'event' => 'elementorPageTransitions:animate', + 'condition' => [ + $this->get_control_id( 'entrance_animation' ) . '!' => '', + ], + ] + ); + } + + /** + * Replace the Page Transition teaser with actual controls. + * + * @param Controls_Stack $controls_stack + * + * @return void + */ + public function register_page_transitions_controls( $controls_stack ) { + /** + * Page Transitions + */ + $controls_stack->start_controls_section( + 'section_page_transitions', + [ + 'label' => esc_html__( 'Page Transitions', 'elementor-pro' ), + 'tab' => Settings_Page_Transitions::TAB_ID, + ] + ); + + $controls_stack->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => $this->get_control_id( 'background' ), + 'exclude' => [ 'image', 'video' ], + 'fields_options' => [ + 'background' => [ + 'label' => esc_html__( 'Background', 'elementor-pro' ), + 'default' => 'classic', + 'description' => esc_html__( 'This is the page color behind your loading animation', 'elementor-pro' ), + ], + 'color' => [ + 'default' => '#FFBC7D', + ], + ], + 'selector' => '{{WRAPPER}} e-page-transition', + ] + ); + + $controls_stack->add_responsive_control( + $this->get_control_id( 'entrance_animation' ), + [ + 'label' => esc_html__( 'Entrance Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'label_block' => true, + // The animations are the opposite of what the user sees because the user thinks in the context + // of a page transition, while we actually animate the overlay. + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'fade-out' => esc_html__( 'Fade In', 'elementor-pro' ), + 'fade-out-down' => esc_html__( 'Fade In Down', 'elementor-pro' ), + 'fade-out-right' => esc_html__( 'Fade In Right', 'elementor-pro' ), + 'fade-out-up' => esc_html__( 'Fade In Up', 'elementor-pro' ), + 'fade-out-left' => esc_html__( 'Fade In Left', 'elementor-pro' ), + 'zoom-out' => esc_html__( 'Zoom In', 'elementor-pro' ), + 'slide-out-down' => esc_html__( 'Slide In Down', 'elementor-pro' ), + 'slide-out-right' => esc_html__( 'Slide In Right', 'elementor-pro' ), + 'slide-out-up' => esc_html__( 'Slide In Up', 'elementor-pro' ), + 'slide-out-left' => esc_html__( 'Slide In Left', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-page-transition-entrance-animation: e-page-transition-{{VALUE}}', + ], + ] + ); + + $controls_stack->add_responsive_control( + $this->get_control_id( 'exit_animation' ), + [ + 'label' => esc_html__( 'Exit Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'label_block' => true, + // The animations are the opposite of what the user sees because the user thinks in the context + // of a page transition, while we actually animate the overlay. + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'fade-in' => esc_html__( 'Fade Out', 'elementor-pro' ), + 'fade-in-down' => esc_html__( 'Fade Out Down', 'elementor-pro' ), + 'fade-in-right' => esc_html__( 'Fade Out Right', 'elementor-pro' ), + 'fade-in-up' => esc_html__( 'Fade Out Up', 'elementor-pro' ), + 'fade-in-left' => esc_html__( 'Fade Out Left', 'elementor-pro' ), + 'zoom-in' => esc_html__( 'Zoom Out', 'elementor-pro' ), + 'slide-in-down' => esc_html__( 'Slide Out Down', 'elementor-pro' ), + 'slide-in-right' => esc_html__( 'Slide Out Right', 'elementor-pro' ), + 'slide-in-up' => esc_html__( 'Slide Out Up', 'elementor-pro' ), + 'slide-in-left' => esc_html__( 'Slide Out Left', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-page-transition-exit-animation: e-page-transition-{{VALUE}}', + ], + 'condition' => [ + $this->get_control_id( 'entrance_animation' ) . '!' => '', + ], + ] + ); + + $controls_stack->add_control( + $this->get_control_id( 'animation_duration' ), + [ + 'label' => esc_html__( 'Animation Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + 'size' => 1500, + ], + 'range' => [ + 's' => [ + 'max' => 5, + ], + 'ms' => [ + 'max' => 5000, + ], + ], + 'condition' => [ + $this->get_control_id( 'entrance_animation' ) . '!' => '', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-page-transition-animation-duration: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_preview_button( $controls_stack, 'page_transition' ); + + $controls_stack->end_controls_section(); + + /** + * Preloader + */ + $controls_stack->start_controls_section( + 'section_preloader', + [ + 'label' => esc_html__( 'Preloader', 'elementor-pro' ), + 'tab' => Settings_Page_Transitions::TAB_ID, + ] + ); + + $controls_stack->add_control( + $this->get_control_id( 'preloader_type' ), + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + self::TYPE_ANIMATION => esc_html__( 'Animation', 'elementor-pro' ), + self::TYPE_ICON => esc_html__( 'Icon', 'elementor-pro' ), + self::TYPE_IMAGE => esc_html__( 'Image', 'elementor-pro' ), + ], + ] + ); + + $controls_stack->add_control( + $this->get_control_id( 'preloader_icon' ), + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'default' => [ + 'value' => 'fas fa-spinner', + 'library' => 'fa-solid', + ], + 'condition' => [ + $this->get_control_id( 'preloader_type' ) => 'icon', + ], + ] + ); + + $controls_stack->add_control( + $this->get_control_id( 'preloader_image' ), + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'condition' => [ + $this->get_control_id( 'preloader_type' ) => 'image', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $controls_stack->add_control( + $this->get_control_id( 'preloader_animation_type' ), + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => self::LOADER_CIRCLE, + 'options' => [ + self::LOADER_CIRCLE => esc_html__( 'Circle', 'elementor-pro' ), + self::LOADER_CIRCLE_DASHED => esc_html__( 'Circle Dashed', 'elementor-pro' ), + self::LOADER_BOUNCING_DOTS => esc_html__( 'Bouncing Dots', 'elementor-pro' ), + self::LOADER_PULSING_DOTS => esc_html__( 'Pulsing Dots', 'elementor-pro' ), + self::LOADER_PULSE => esc_html__( 'Pulse', 'elementor-pro' ), + self::LOADER_OVERLAP => esc_html__( 'Overlap', 'elementor-pro' ), + self::LOADER_SPINNERS => esc_html__( 'Spinners', 'elementor-pro' ), + self::LOADER_NESTED_SPINNERS => esc_html__( 'Nested Spinners', 'elementor-pro' ), + self::LOADER_OPPOSING_NESTED_SPINNERS => esc_html__( 'Opposing Nested Spinners', 'elementor-pro' ), + self::LOADER_OPPOSING_NESTED_RINGS => esc_html__( 'Opposing Nested Rings', 'elementor-pro' ), + self::LOADER_PROGRESS_BAR => esc_html__( 'Progress Bar', 'elementor-pro' ), + self::LOADER_TWO_WAY_PROGRESS_BAR => esc_html__( 'Two Way Progress Bar', 'elementor-pro' ), + self::LOADER_REPEATING_BAR => esc_html__( 'Repeating Bar', 'elementor-pro' ), + ], + 'condition' => [ + $this->get_control_id( 'preloader_type' ) => 'animation', + ], + ] + ); + + $controls_stack->add_control( + $this->get_control_id( 'preloader_animation' ), + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'eicon-spin' => esc_html__( 'Spinning', 'elementor-pro' ), + 'bounce' => esc_html__( 'Bounce', 'elementor-pro' ), + 'flash' => esc_html__( 'Flash', 'elementor-pro' ), + 'pulse' => esc_html__( 'Pulse', 'elementor-pro' ), + 'rubberBand' => esc_html__( 'Rubber Band', 'elementor-pro' ), + 'shake' => esc_html__( 'Shake', 'elementor-pro' ), + 'headShake' => esc_html__( 'Head Shake', 'elementor-pro' ), + 'swing' => esc_html__( 'Swing', 'elementor-pro' ), + 'tada' => esc_html__( 'Tada', 'elementor-pro' ), + 'wobble' => esc_html__( 'Wobble', 'elementor-pro' ), + 'jello' => esc_html__( 'Jello', 'elementor-pro' ), + ], + 'condition' => [ + $this->get_control_id( 'preloader_type' ) => [ 'icon', 'image' ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-preloader-animation: {{VALUE}}', + ], + ] + ); + + // Include animation speed control only for specific pre-loaders which support that. + $included_preloaders = [ + self::LOADER_CIRCLE, + self::LOADER_CIRCLE_DASHED, + self::LOADER_BOUNCING_DOTS, + self::LOADER_PULSING_DOTS, + self::LOADER_SPINNERS, + self::LOADER_NESTED_SPINNERS, + self::LOADER_OPPOSING_NESTED_SPINNERS, + self::LOADER_OPPOSING_NESTED_RINGS, + self::LOADER_PROGRESS_BAR, + self::LOADER_TWO_WAY_PROGRESS_BAR, + self::LOADER_REPEATING_BAR, + ]; + + $controls_stack->add_control( + $this->get_control_id( 'preloader_animation_duration' ), + [ + 'label' => esc_html__( 'Animation Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + 'size' => 1500, + ], + 'range' => [ + 's' => [ + 'max' => 5, + ], + 'ms' => [ + 'max' => 5000, + ], + ], + // Show the control only for images, icons & specific custom pre-loaders. + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => $this->get_control_id( 'preloader_type' ), + 'operator' => 'in', + 'value' => [ + self::TYPE_IMAGE, + self::TYPE_ICON, + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => $this->get_control_id( 'preloader_type' ), + 'operator' => '=', + 'value' => self::TYPE_ANIMATION, + ], + [ + 'name' => $this->get_control_id( 'preloader_animation_type' ), + 'operator' => 'in', + 'value' => $included_preloaders, + ], + ], + ], + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-preloader-animation-duration: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $controls_stack->add_control( + $this->get_control_id( 'preloader_delay' ), + [ + 'label' => esc_html__( 'Preloader Delay', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + 'size' => 0, + ], + 'range' => [ + 's' => [ + 'max' => 5, + ], + 'ms' => [ + 'max' => 5000, + ], + ], + 'condition' => [ + $this->get_control_id( 'preloader_type' ) . '!' => '', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-preloader-delay: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $controls_stack->add_control( + $this->get_control_id( 'text_heading' ), + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'condition' => [ + $this->get_control_id( 'preloader_type' ) . '!' => '', + ], + ] + ); + + $controls_stack->add_control( + $this->get_control_id( 'preloader_color' ), + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#FFF', + 'condition' => [ + $this->get_control_id( 'preloader_type' ) => [ 'icon', 'animation' ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-preloader-color: {{VALUE}}', + ], + ] + ); + + $controls_stack->add_responsive_control( + $this->get_control_id( 'preloader_size' ), + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 20, + ], + 'range' => [ + 'px' => [ + 'max' => 300, + ], + 'em' => [ + 'max' => 30, + ], + 'rem' => [ + 'max' => 30, + ], + ], + 'condition' => [ + $this->get_control_id( 'preloader_type' ) => [ 'icon', 'animation' ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-preloader-size: {{SIZE}}{{UNIT}}', + ], + ] + ); + + // Animation to exclude in rotation. + $excluded_animations = [ + 'eicon-spin', + 'bounce', + 'pulse', + 'rubberBand', + 'shake', + 'headShake', + 'swing', + 'tada', + 'wobble', + 'jello', + ]; + + $controls_stack->add_responsive_control( + $this->get_control_id( 'preloader_rotate' ), + [ + 'label' => esc_html__( 'Rotate', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'deg', 'grad', 'rad', 'turn' ], + 'default' => [ + 'unit' => 'deg', + 'size' => 0, + ], + 'condition' => [ + $this->get_control_id( 'preloader_type' ) => 'icon', + $this->get_control_id( 'preloader_animation' ) . '!' => $excluded_animations, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-preloader-rotate: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $controls_stack->add_responsive_control( + $this->get_control_id( 'preloader_width' ), + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'tablet_default' => [ + 'unit' => '%', + ], + 'mobile_default' => [ + 'unit' => '%', + ], + 'range' => [ + '%' => [ + 'min' => 1, + 'max' => 100, + ], + 'px' => [ + 'min' => 1, + 'max' => 1000, + ], + 'em' => [ + 'max' => 100, + ], + 'rem' => [ + 'max' => 100, + ], + 'vw' => [ + 'min' => 1, + 'max' => 100, + ], + ], + 'condition' => [ + $this->get_control_id( 'preloader_type' ) => 'image', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-preloader-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $controls_stack->add_responsive_control( + $this->get_control_id( 'preloader_max_width' ), + [ + 'label' => esc_html__( 'Max Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'tablet_default' => [ + 'unit' => '%', + ], + 'mobile_default' => [ + 'unit' => '%', + ], + 'range' => [ + '%' => [ + 'min' => 1, + 'max' => 100, + ], + 'px' => [ + 'min' => 1, + 'max' => 1000, + ], + 'em' => [ + 'max' => 100, + ], + 'rem' => [ + 'max' => 100, + ], + 'vw' => [ + 'min' => 1, + 'max' => 100, + ], + ], + 'condition' => [ + $this->get_control_id( 'preloader_type' ) => 'image', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-preloader-max-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $controls_stack->add_responsive_control( + $this->get_control_id( 'preloader_opacity' ), + [ + 'label' => esc_html__( 'Opacity', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 1, + 'step' => 0.1, + ], + ], + 'condition' => [ + $this->get_control_id( 'preloader_type' ) => 'image', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-preloader-opacity: {{SIZE}}', + ], + ] + ); + + $this->add_preview_button( $controls_stack, 'preloader' ); + + $controls_stack->end_controls_section(); + } + + /** + * Get a control value from the settings. + * + * @param string $control - Non prefixed control name. + * + * @return mixed + */ + private function get_setting( $control ) { + $document = Plugin::elementor()->kits_manager->get_active_kit_for_frontend(); + $control = $this->get_control_id( $control ); + + return $document->get_settings_for_display( $control ); + } + + /** + * Get the Page Transitions element CSS class. + * + * @return string + */ + private function get_page_transition_class() { + $is_preview_mode = Plugin::elementor()->preview->is_preview_mode(); + return $is_preview_mode ? 'e-page-transition--entered' : 'e-page-transition--entering'; + } + + /** + * Get the Page Transitions links Regex filter. + * + * @return string + */ + private function get_page_transition_filter() { + // Prepare the admin URL to be "regex-ready" (escape special characters). + $admin_url = preg_quote( get_admin_url(), '/' ); + + // A regex pattern for URLs under `wp-admin`. + return '^' . $admin_url; + } + + /** + * Print the Page Transition element attributes. + * + * @return void + */ + private function print_render_attribute_string() { + $kit = Plugin::elementor()->kits_manager->get_active_kit(); + + $settings = [ + 'preloader_type', + 'preloader_icon', + 'preloader_image', + 'preloader_animation_type', + ]; + + foreach ( $settings as $setting ) { + $key = str_replace( '_', '-', $setting ); + $value = $this->get_setting( $setting ); + + if ( empty( $value ) ) { + continue; + } + + // Change the key & value specifically for the image control, since the returned value + // is an array while the Page Transition element expects a URL as a string. + if ( 'preloader-image' === $key ) { + $key = 'preloader-image-url'; + $value = $value['url']; + } + + $kit->add_render_attribute( Settings_Page_Transitions::TAB_ID, $key, $value ); + } + + $class = $this->get_page_transition_class(); + + if ( $class ) { + $kit->add_render_attribute( Settings_Page_Transitions::TAB_ID, 'class', $class ); + } + + // Add URL regex filter to filter only URLs without `wp-admin`. + $kit->add_render_attribute( Settings_Page_Transitions::TAB_ID, 'exclude', $this->get_page_transition_filter() ); + + $kit->print_render_attribute_string( Settings_Page_Transitions::TAB_ID ); + } + + /** + * Determine if the Page Transition element should be rendered. + * + * @return bool + */ + private function should_render() { + // Don't render the Page Transition if the page is a non-interactive (static-rendered) page (e.g. template-preview). + if ( Plugin::elementor()->frontend->is_static_render_mode() ) { + return false; + } + + $has_entrance_animation = ! ! $this->get_setting( 'entrance_animation' ); + $has_preloader = ! ! $this->get_setting( 'preloader_type' ); + $is_page = ( is_singular() || is_archive() ) && ! is_paged(); + + return $is_page && ( $has_entrance_animation || $has_preloader ); + } + + /** + * Whether the Page Transitions scripts should be enqueued. + * When in preview mode, the scripts should be loaded since the user might not have a Page Transition + * set on initial load but he will need them when changing the settings. + * + * @return bool + */ + private function should_enqueue_scripts() { + return $this->should_render() || Plugin::elementor()->preview->is_preview_mode(); + } + + /** + * Render the Page Transition markup. + * + * @return void + */ + private function render() { + $is_inline_font_icon_active = Plugin::elementor()->experiments->is_feature_active( 'e_font_icon_svg' ); + + ?> + print_render_attribute_string(); ?>> + get_setting( 'preloader_icon' ); + + // Render inline SVG icon when the experiment is active, since the component itself + // shouldn't know about the Editor or the experiments. + if ( $is_inline_font_icon_active && ! empty( $icon ) ) { + Icons_Manager::render_icon( $icon, [ 'class' => 'e-page-transition--preloader' ] ); + } + ?> + + enqueue_instant_page_script(); + + wp_enqueue_script( + 'page-transitions', + $this->get_js_assets_url( 'page-transitions' ), + null, + ELEMENTOR_PRO_VERSION, + false + ); + } + + /** + * Get the base URL for assets. + * + * @return string + */ + public function get_assets_base_url() { + return ELEMENTOR_PRO_URL; + } + + /** + * Add actions & filters. + * + * @return void + */ + private function add_actions() { + add_action( 'elementor/element/after_section_end', [ $this, 'register_controls' ], 10, 2 ); + + add_action( 'wp_enqueue_scripts', function () { + if ( $this->should_enqueue_scripts() ) { + $this->enqueue_scripts(); + } + } ); + + // Render the Page Transition element after the body open tag. + add_action( 'wp_body_open', function () { + if ( $this->should_render() ) { + $this->render(); + } + }, 10, 2 ); + } +} diff --git a/modules/payments/classes/payment-button.php b/modules/payments/classes/payment-button.php new file mode 100644 index 0000000..7abf9bd --- /dev/null +++ b/modules/payments/classes/payment-button.php @@ -0,0 +1,573 @@ + _x( 'AUD', 'Currency', 'elementor-pro' ), + 'CAD' => _x( 'CAD', 'Currency', 'elementor-pro' ), + 'CZK' => _x( 'CZK', 'Currency', 'elementor-pro' ), + 'DKK' => _x( 'DKK', 'Currency', 'elementor-pro' ), + 'EUR' => _x( 'EUR', 'Currency', 'elementor-pro' ), + 'HKD' => _x( 'HKD', 'Currency', 'elementor-pro' ), + 'HUF' => _x( 'HUF', 'Currency', 'elementor-pro' ), + 'ILS' => _x( 'ILS', 'Currency', 'elementor-pro' ), + 'JPY' => _x( 'JPY', 'Currency', 'elementor-pro' ), + 'MXN' => _x( 'MXN', 'Currency', 'elementor-pro' ), + 'NOK' => _x( 'NOK', 'Currency', 'elementor-pro' ), + 'NZD' => _x( 'NZD', 'Currency', 'elementor-pro' ), + 'PHP' => _x( 'PHP', 'Currency', 'elementor-pro' ), + 'PLN' => _x( 'PLN', 'Currency', 'elementor-pro' ), + 'GBP' => _x( 'GBP', 'Currency', 'elementor-pro' ), + 'RUB' => _x( 'RUB', 'Currency', 'elementor-pro' ), + 'SGD' => _x( 'SGD', 'Currency', 'elementor-pro' ), + 'SEK' => _x( 'SEK', 'Currency', 'elementor-pro' ), + 'CHF' => _x( 'CHF', 'Currency', 'elementor-pro' ), + 'TWD' => _x( 'TWD', 'Currency', 'elementor-pro' ), + 'THB' => _x( 'THB', 'Currency', 'elementor-pro' ), + 'TRY' => _x( 'TRY', 'Currency', 'elementor-pro' ), + 'USD' => _x( 'USD', 'Currency', 'elementor-pro' ), + ]; + } + + // Return an array of default error messages. + protected function get_default_error_messages() { + return [ + self::ERROR_MESSAGE_GLOBAL => esc_html__( 'An error occurred.', 'elementor-pro' ), + self::ERROR_MESSAGE_PAYMENT_METHOD => esc_html__( 'No payment method connected. Contact seller.', 'elementor-pro' ), + ]; + } + + // Get message text by id (`error_message_$id`). + protected function get_custom_message( $id ) { + $message = $this->get_settings_for_display( 'error_message_' . $id ); + + // Return the user-defined message. + if ( ! empty( $message ) ) { + return $message; + } + + // Return the default message. + $error_messages = $this->get_default_error_messages(); + + return ( ! empty( $error_messages[ $id ] ) ) ? $error_messages[ $id ] : esc_html__( 'Unknown error.', 'elementor-pro' ); + } + + // Product details section. + protected function register_product_controls() { + + $this->add_control( + 'type', + [ + 'label' => esc_html__( 'Transaction Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'checkout', + 'options' => [ + self::PAYMENT_TYPE_CHECKOUT => esc_html__( 'Checkout', 'elementor-pro' ), + self::PAYMENT_TYPE_DONATION => esc_html__( 'Donation', 'elementor-pro' ), + self::PAYMENT_TYPE_SUBSCRIPTION => esc_html__( 'Subscription', 'elementor-pro' ), + ], + 'separator' => 'before', + ] + ); + + $this->after_product_type(); + + $this->add_control( + 'product_name', + [ + 'label' => esc_html__( 'Item Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'label_block' => true, + ] + ); + + $this->add_control( + 'product_sku', + [ + 'label' => esc_html__( 'SKU', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'product_price', + [ + 'label' => esc_html__( 'Price', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => '0.00', + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'type!' => self::PAYMENT_TYPE_DONATION, + ], + ] + ); + + $this->add_control( + 'donation_type', + [ + 'label' => esc_html__( 'Donation Amount', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => self::DONATION_TYPE_FIXED, + 'options' => [ + self::DONATION_TYPE_ANY => esc_html__( 'Any Amount', 'elementor-pro' ), + self::DONATION_TYPE_FIXED => esc_html__( 'Fixed', 'elementor-pro' ), + ], + 'condition' => [ + 'type' => self::PAYMENT_TYPE_DONATION, + ], + ] + ); + + $this->add_control( + 'donation_amount', + [ + 'label' => esc_html__( 'Amount', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => '1', + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'type' => self::PAYMENT_TYPE_DONATION, + 'donation_type' => self::DONATION_TYPE_FIXED, + ], + ] + ); + + $this->add_control( + 'currency', + [ + 'label' => esc_html__( 'Currency', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'USD', + 'options' => $this->get_currencies(), + ] + ); + + $this->add_control( + 'billing_cycle', + [ + 'label' => esc_html__( 'Billing Cycle', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => self::BILLING_CYCLE_MONTHS, + 'options' => [ + self::BILLING_CYCLE_DAYS => esc_html__( 'Daily', 'elementor-pro' ), + self::BILLING_CYCLE_WEEKS => esc_html__( 'Weekly', 'elementor-pro' ), + self::BILLING_CYCLE_MONTHS => esc_html__( 'Monthly', 'elementor-pro' ), + self::BILLING_CYCLE_YEARS => esc_html__( 'Yearly', 'elementor-pro' ), + ], + 'condition' => [ + 'type' => self::PAYMENT_TYPE_SUBSCRIPTION, + ], + ] + ); + + $this->add_control( + 'auto_renewal', + [ + 'type' => Controls_Manager::SWITCHER, + 'label' => esc_html__( 'Auto Renewal', 'elementor-pro' ), + 'default' => 'yes', + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'condition' => [ + 'type' => self::PAYMENT_TYPE_SUBSCRIPTION, + ], + ] + ); + + $this->add_control( + 'quantity', + [ + 'label' => esc_html__( 'Quantity', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 1, + 'condition' => [ + 'type' => self::PAYMENT_TYPE_CHECKOUT, + ], + ] + ); + + $this->add_control( + 'shipping_price', + [ + 'label' => esc_html__( 'Shipping Price', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 0, + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'type' => self::PAYMENT_TYPE_CHECKOUT, + ], + ] + ); + + $this->add_control( + 'tax_type', + [ + 'label' => esc_html__( 'Tax', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'percentage' => esc_html__( 'Percentage', 'elementor-pro' ), + ], + 'condition' => [ + 'type' => self::PAYMENT_TYPE_CHECKOUT, + ], + ] + ); + + $this->add_control( + 'tax_rate', + [ + 'label' => esc_html__( 'Tax Percentage', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => '0', + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'type' => self::PAYMENT_TYPE_CHECKOUT, + 'tax_type' => 'percentage', + ], + ] + ); + } + + // Submission settings section. + protected function register_settings_section() { + $this->start_controls_section( + 'section_settings', + [ + 'label' => esc_html__( 'Additional Options', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'redirect_after_success', + [ + 'label' => esc_html__( 'Redirect After Success', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'options' => false, + 'placeholder' => esc_html__( 'Choose a page or add a URL', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'label_block' => true, + 'render_type' => 'none', + ] + ); + + $this->add_control( + 'sandbox_mode', + [ + 'type' => Controls_Manager::SWITCHER, + 'label' => esc_html__( 'Sandbox', 'elementor-pro' ), + 'default' => 'no', + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + ] + ); + + $this->register_sandbox_controls(); + + $this->add_control( + 'open_in_new_window', + [ + 'type' => Controls_Manager::SWITCHER, + 'label' => sprintf( + /* translators: %s: Merchant name. */ + esc_html__( 'Open %s In New Tab', 'elementor-pro' ), + $this->get_merchant_name() + ), + 'default' => 'yes', + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'custom_messages', + [ + 'label' => esc_html__( 'Custom Messages', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + ] + ); + + $this->after_custom_messages_toggle(); + + $error_messages = $this->get_default_error_messages(); + + $this->add_control( + 'error_message_' . self::ERROR_MESSAGE_GLOBAL, + [ + 'label' => esc_html__( 'Error Message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => $error_messages[ self::ERROR_MESSAGE_GLOBAL ], + 'placeholder' => $error_messages[ self::ERROR_MESSAGE_GLOBAL ], + 'label_block' => true, + 'condition' => [ + 'custom_messages!' => '', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'error_message_' . self::ERROR_MESSAGE_PAYMENT_METHOD, + [ + 'label' => sprintf( + /* translators: %s: Merchant name. */ + esc_html__( '%s Not Connected', 'elementor-pro' ), + $this->get_merchant_name() + ), + 'type' => Controls_Manager::TEXT, + 'default' => $error_messages[ self::ERROR_MESSAGE_PAYMENT_METHOD ], + 'placeholder' => $error_messages[ self::ERROR_MESSAGE_PAYMENT_METHOD ], + 'label_block' => true, + 'condition' => [ + 'custom_messages!' => '', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->update_error_massages(); + + $this->end_controls_section(); + } + + // Customize the default button controls. + protected function register_button_controls() { + parent::register_controls(); + + $this->remove_control( 'button_type' ); + + $this->remove_control( 'link' ); + + $this->remove_control( 'size' ); + + $this->update_control( 'text', [ + 'default' => 'Buy Now', + ] ); + + $this->update_control( 'button_text_color', [ + 'default' => '#FFF', + ] ); + + $this->update_control( + 'icon_align', + [ + 'options' => [ + 'left' => esc_html__( 'Before Text', 'elementor-pro' ), + 'right' => esc_html__( 'After Text', 'elementor-pro' ), + ], + ] + ); + } + + // Add typography settings for custom messages. + protected function register_messages_style_section() { + $this->start_controls_section( + 'section_messages_style', + [ + 'label' => esc_html__( 'Messages', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'message_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-message', + ] + ); + + $this->add_control( + 'message_color_' . self::ERROR_MESSAGE_GLOBAL, + [ + 'label' => esc_html__( 'Error Message Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-message.elementor-error-message-' . self::ERROR_MESSAGE_GLOBAL => 'color: {{COLOR}};', + ], + ] + ); + + $this->add_control( + 'message_color_' . self::ERROR_MESSAGE_PAYMENT_METHOD, + [ + 'label' => sprintf( + /* translators: %s: Merchant name. */ + esc_html__( '%s Not Connected Color', 'elementor-pro' ), + $this->get_merchant_name() + ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-message.elementor-error-message-' . self::ERROR_MESSAGE_PAYMENT_METHOD => 'color: {{COLOR}};', + ], + ] + ); + + $this->end_controls_section(); + } + + // Register widget controls. + protected function register_controls() { + $this->register_account_section(); + $this->register_button_controls(); + $this->register_settings_section(); + $this->register_messages_style_section(); + } + + // Render the checkout button. + protected function render_button( Widget_Base $instance = null, $tag = 'a' ) { + $this->add_render_attribute( 'button', 'class', 'elementor-payment-button' ); + + ?> + < print_render_attribute_string( 'button' ); ?>> + render_text(); ?> + > + get_settings_for_display(); + + $this->add_render_attribute( 'wrapper', 'class', 'elementor-button-wrapper' ); + $this->add_render_attribute( 'button', 'class', 'elementor-button' ); + $this->add_render_attribute( 'button', 'role', 'button' ); + + if ( ! empty( $settings['button_css_id'] ) ) { + $this->add_render_attribute( 'button', 'id', $settings['button_css_id'] ); + } + + if ( ! empty( $settings['size'] ) ) { + $this->add_render_attribute( 'button', 'class', 'elementor-size-' . $settings['size'] ); + } + + if ( $settings['hover_animation'] ) { + $this->add_render_attribute( 'button', 'class', 'elementor-animation-' . $settings['hover_animation'] ); + } + + ?> +
    print_render_attribute_string( 'wrapper' ); ?>> + render_button(); ?> +
    + + <# + view.addRenderAttribute( 'text', 'class', 'elementor-button-text' ); + view.addInlineEditingAttributes( 'text', 'none' ); + var iconHTML = elementor.helpers.renderIcon( view, settings.selected_icon, { 'aria-hidden': true }, 'i' , 'object' ), + migrated = elementor.helpers.isIconMigrated( settings, 'selected_icon' ); + #> + + get_settings_for_display( 'sandbox_mode' ); + } +} diff --git a/modules/payments/classes/stripe-handler.php b/modules/payments/classes/stripe-handler.php new file mode 100644 index 0000000..1ffa869 --- /dev/null +++ b/modules/payments/classes/stripe-handler.php @@ -0,0 +1,41 @@ + 'Bearer ' . $secret_key ]; + return wp_remote_get( self::STRIPE_ENDPOINT_URL . $endpoint, [ + 'headers' => $headers, + 'body' => $body, + ] ); + } + + /** + * @param $headers + * @param $body + * @param $endpoint + * @return array|\WP_Error + */ + public function post( $headers, $body, $endpoint ) { + return wp_remote_post( self::STRIPE_ENDPOINT_URL . $endpoint, [ + 'headers' => $headers, + 'body' => $body, + ] ); + } +} diff --git a/modules/payments/module.php b/modules/payments/module.php new file mode 100644 index 0000000..2ff4520 --- /dev/null +++ b/modules/payments/module.php @@ -0,0 +1,470 @@ + 'Paypal_Button', + self::STRIPE_LICENCE_FEATURE_NAME => 'Stripe_Button', + ]; + + public $secret_key = ''; + private $stripe_handler; + + public function get_widgets() { + return API::filter_active_features( static::WIDGET_NAME_CLASS_NAME_MAP ); + } + + /** + * Error handler + * + * @since 3.7.0 + * + * @param integer $status_code + * @param string $error_massage + */ + protected function error_handler( $status_code, $error_massage ) { + $resp['response']['code'] = $status_code; + $resp['body'] = wp_json_encode( + [ 'error' => [ 'message' => $error_massage ] ], + JSON_PRETTY_PRINT + ); + + wp_send_json( $resp ); + } + + public function get_name() { + return 'payments'; + } + + /** + * Reads secret test key from wp_options table + * + * @since 3.7.0 + * + * @return string + */ + public static function get_global_stripe_test_secret_key() { + return get_option( 'elementor_' . self::STRIPE_TEST_SECRET_KEY, '' ); + } + + /** + * Reads secret live key from wp_options table + * + * @since 3.7.0 + * + * @return string + */ + public static function get_global_stripe_live_secret_key() { + return get_option( 'elementor_' . self::STRIPE_LIVE_SECRET_KEY, '' ); + } + + /** + * Integrations page secret key validations' callback function + * + * @since 3.7.0 + * + * @return void + */ + public function ajax_validate_secret_key() { + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $action = Utils::_unstable_get_super_global_value( $_POST, 'action' ); + $nonce_action = ( ! strpos( $action, 'test' ) ? self::STRIPE_LIVE_SECRET_KEY : self::STRIPE_TEST_SECRET_KEY ); + + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $nonce = Utils::_unstable_get_super_global_value( $_POST, '_nonce' ); + if ( ! $nonce || ! wp_verify_nonce( $nonce, $nonce_action ) ) { + $this->error_handler( 403, esc_html__( 'Something went wrong, please refresh the page.', 'elementor-pro' ) ); + die(); + } + + if ( ! Utils::_unstable_get_super_global_value( $_POST, 'secret_key' ) ) { + wp_send_json_error(); + } else { + $this->secret_key = Utils::_unstable_get_super_global_value( $_POST, 'secret_key' ); + } + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( 'Permission denied' ); + } + + $stripe_handler = new Stripe_handler(); + $response = $stripe_handler->get( $this->secret_key, self::STRIPE_TAX_ENDPOINT_URL, [ 'limit' => 0 ] ); + $code = $response['response']['code']; + + if ( 200 !== $code ) { + wp_send_json_error(); + } else { + wp_send_json_success(); + } + } + + /** + * Ajax callback + * + * Returns a list of tax rates + * + * @since 3.7.0 + * + * @return array + */ + public function register_ajax_actions( $ajax ) { + return $ajax->register_ajax_action( 'pro_get_stripe_tax_rates', [ $this, 'get_stripe_tax_rates' ] ); + } + + /** + * returns a list of tax rates + * + * if tax rates are set in stripe admin dashboard + * from here the tax rates array is implemented in + * tax rates select control + * + * @param array $data + * + * @return array - returns to js ajax function. + * + * @throws \Exception + * @since 3.7.0 + * + */ + public function get_stripe_tax_rates( array $data ) { + Utils::_unstable_check_document_permissions( $data['editor_post_id'] ); + + $tax_rates_lists = []; + $tax_rates_lists['live_api_key'] = $this->get_tax_rates( $this->get_global_stripe_live_secret_key() ); + $tax_rates_lists['test_api_key'] = $this->get_tax_rates( $this->get_global_stripe_test_secret_key() ); + return $tax_rates_lists; + } + + /** + * Get ajax tax rates from API + * + * Read all ajax tax rates from stripes API and + * + * @since 3.7.0 + * + * @param string $secret_key + * + * @return array - returns to js ajax function. + * + */ + protected function tax_rates_result_funnel( $secret_key ) { + $response = $this->stripe_handler->get( $secret_key, self::STRIPE_TAX_ENDPOINT_URL, [ 'active' => 'true' ] ); + + // If there is no internet connection or no active tax rates in stripe + if ( ! is_wp_error( $response ) ) { + $decoded_response = json_decode( $response['body'], true ); + + // If there is no API key or a wrong one in integrations page + if ( isset( $decoded_response['error'] ) || 0 === count( $decoded_response['data'] ) ) { + $data = []; + } else { + $data = $decoded_response['data']; + } + } else { + $data = []; + } + + return $data; + } + + /** + * Gets and Organizes all tax rates in a + * list suitable for the select control + * + * @since 3.7.0 + * + * @param string $secret_key + * + * @return array - returns to js ajax function. + * + */ + protected function get_tax_rates( $secret_key ) { + $data = $this->tax_rates_result_funnel( $secret_key ); + return $this->tax_rates_options( $data ); + } + + /** + * Create options array for tax_rates controls + * + * This function can return two scenarios: + * 1. Show tax rates options. + * 2. There are no active tax rates or the user is working on a local environment. + * + * @since 3.7.0 + * + * @param array $data the returned value of get_data_from_api() function + * + * @return array $tax_rates_options placed as the control options + */ + private function tax_rates_options( $data = [] ) { + $tax_rates_options = []; + if ( ! empty( $data ) ) { + foreach ( $data as $k => $v ) { + $is_inclusive = ( true === $v['inclusive'] ? 'inclusive' : 'exclusive' ); + $joint_tax_data = serialize( [ $v['id'], $is_inclusive ] ); + $display_name = $v['description'] ? $v['display_name'] . ' - ' . $v['description'] : $v['display_name']; + $tax_rates_options[ $joint_tax_data ] = $display_name; + } + // Add 'None' value as the first element in $test_tax_rates_options array. + return array_merge( [ '' => esc_html__( 'None', 'elementor-pro' ) ], $tax_rates_options ); + + } else { + $tax_rates_options = [ '' => esc_html__( 'None', 'elementor-pro' ) ]; + + } + return $tax_rates_options; + } + + /** + * Create options array for tax_rates controls + * + * Zero decimal currencies by stripe https://stripe.com/docs/currencies#zero-decimal + * this option is zero decimal what means that only complete numbers bill pass to stripe. + * for example 555.55 will return product_price of 555. + * + * @since 3.7.0 + * + * @param $currency string + * @param $product_price + * + * @return false|float $tax_rates_options placed as the control options + */ + public function currency_adaptation( $currency, $product_price ) { + $zero_decimal = [ 'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF' ]; + if ( in_array( $currency, $zero_decimal ) ) { + // There is no need to multiply $product_price by 100 + return floor( $product_price ); + } else { + return floor( $product_price * 100 ); + } + } + + /** + * Secret key conditional function + * + * @since 3.7.0 + * + * @param string $test_mode + * + * @return void + */ + public function set_secret_key_by_environment_state( $test_mode = 'no' ) { + if ( ! $this->secret_key ) { + if ( 'yes' === $test_mode ) { + $this->secret_key = $this->get_global_stripe_test_secret_key(); + } else { + $this->secret_key = $this->get_global_stripe_live_secret_key(); + } + } + } + + /** + * Ajax callback function - API stripe call . + * + * get stripe user data on widget load + * sends the product data and returns the product page checkout url. + * + * @since 3.7.0 + */ + public function submit_stripe_form() { + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $data = Utils::_unstable_get_super_global_value( $_POST, 'data' ); + if ( ! isset( $data['nonce'] ) || ! wp_verify_nonce( $data['nonce'], 'stripe_form_submit' ) ) { + $this->error_handler( 403, esc_html__( 'Something went wrong, please refresh the page.', 'elementor-pro' ) ); + die(); + } + $args = []; + $widget_id = $data['widgetId'] ?? null; + $args['page_url'] = $data['pageUrl'] ?? null; + + Plugin::elementor()->db->switch_to_post( $data['postId'] ); + $document = Plugin::elementor()->documents->get( $data['postId'] ); + + // Retrieve data from widget document + if ( $document ) { + $widget = \ElementorPro\Modules\Forms\Module::find_element_recursive( $document->get_elements_data(), $widget_id ); + $widget_instance = Plugin::elementor()->elements_manager->create_element_instance( $widget ); + $widget_settings = $widget_instance->get_settings_for_display(); + + $args['product_name'] = $widget_settings['product_name'] ? $widget_settings['product_name'] : 'Product'; + $product_price = $widget_settings['stripe_product_price'] ? $widget_settings['stripe_product_price'] : null; + $args['currency'] = $widget['settings']['stripe_currency'] ? $widget['settings']['stripe_currency'] : 'USD'; + $args['quantity'] = $widget['settings']['stripe_quantity'] ? $widget['settings']['stripe_quantity'] : 1; + $args['success_url'] = ( empty( $widget_settings['redirect_after_success']['url'] ) ? $args['page_url'] : $widget_settings['redirect_after_success']['url'] ); + $args['shipping_amount'] = $widget_settings['shipping_amount'] ? $widget_settings['shipping_amount'] * 100 : ''; + $this->stripe_test_mode = $widget['settings']['sandbox_mode'] ? $widget['settings']['sandbox_mode'] : 'no'; + $args['test_mode'] = $this->stripe_test_mode; + $args['tax_rates'] = 'yes' === $args['test_mode'] ? $widget['settings']['stripe_test_env_tax_rates_list'] : $widget['settings']['stripe_live_env_tax_rates_list']; + } + + $args['unit_amount'] = $this->currency_adaptation( $args['currency'], $product_price ); + + $this->set_secret_key_by_environment_state( $args['test_mode'] ); + + if ( ! empty( $this->secret_key ) ) { + $headers = [ 'Authorization' => 'Bearer ' . $this->secret_key ]; + $body = $this->build_body_for_post_request( $args ); + $this->execute_post_request_to_stripe_api( $headers, $body ); + } else { + $this->error_handler( 401, esc_html__( 'You have not entered a valid secret key for this environment, Please add a valid secret key', 'elementor-pro' ) ); + } + } + + /** + * Builds the body for the API POST request. + * + * @since 3.7.0 + * + * @param $args + * + * @return array + */ + public function build_body_for_post_request( $args ) { + $body = [ + 'cancel_url' => $args['page_url'], + 'payment_method_types' => [ 'card' ], + 'success_url' => $args['success_url'], + 'mode' => 'payment', + 'line_items[0][quantity]' => $args['quantity'], + 'line_items[0][price_data][currency]' => $args['currency'], + 'line_items[0][price_data][product_data][name]' => $args['product_name'], + 'line_items[0][price_data][unit_amount]' => $args['unit_amount'], + ]; + + if ( $args['shipping_amount'] ) { + $body['shipping_options'][0]['shipping_rate_data']['type'] = 'fixed_amount'; + $body['shipping_options'][0]['shipping_rate_data']['fixed_amount']['amount'] = $args['shipping_amount']; + $body['shipping_options'][0]['shipping_rate_data']['fixed_amount']['currency'] = $args['currency']; + $body['shipping_options'][0]['shipping_rate_data']['display_name'] = esc_html__( 'shipping fee', 'elementor-pro' ); + } + + if ( isset( $args['tax_rates'] ) ) { + $tax_rate = unserialize( $args['tax_rates'] ); + $tax_id = [ $tax_rate[0] ]; + $tax_behavior = $tax_rate[1]; + + if ( ! empty( $tax_behavior ) && ! empty( $tax_id ) ) { + $body['line_items'][0]['price_data']['tax_behavior'] = $tax_behavior; + $body['line_items'][0]['tax_rates'] = $tax_id; + } + } + + return $body; + } + + /** + * API call handler + * + * @since 3.7.0 + * + * @param $headers + * @param $body + * + * @return void + */ + public function execute_post_request_to_stripe_api( $headers, $body ) { + $response = $this->stripe_handler->post( $headers, $body, self::STRIPE_CHECKOUT_URL_EXT ); + wp_send_json( $response ); + } + + /** + * Add secret_keys to Elementor integrations section + * + * @since 3.7.0 + * + * @param Settings $settings + */ + public function register_admin_fields( Settings $settings ) { + $settings->add_section( Settings::TAB_INTEGRATIONS, 'stripe_api_keys', [ + 'callback' => function () { + echo '

    ' . esc_html__( 'Stripe', 'elementor-pro' ) . '

    '; + echo '

    ' . esc_html__( 'Insert the API keys provided in the stripe admin dashboard to start collecting payments on your website using Stripe.', 'elementor-pro' ) . '
    '; + echo esc_html__( 'These keys will serve as your default API key for all stripe implementations on your site.', 'elementor-pro' ) . '

    '; + }, + 'fields' => [ + self::STRIPE_TEST_SECRET_KEY => [ + 'label' => esc_html__( 'Test Secret key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + 'desc' => sprintf( + /* translators: 1: Link to stripe api key explanation, 2: Link closing tag. */ + esc_html__( 'Enter your test secret key %1$slink%2$s.', 'elementor-pro' ), + '', + '' + ), + ], + ], + 'validate_stripe_api_test_secret_key_button' => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => sprintf( '', self::STRIPE_TEST_SECRET_KEY . '_validate', wp_create_nonce( self::STRIPE_TEST_SECRET_KEY ), esc_html__( 'Validate Test API Key', 'elementor-pro' ) ), + ], + ], + self::STRIPE_LIVE_SECRET_KEY => [ + 'label' => esc_html__( 'Live Secret key', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + 'desc' => sprintf( + /* translators: 1: Link to stripe api key explanation, 2: Link closing tag. */ + esc_html__( 'Enter your Live secret key %1$slink%2$s.', 'elementor-pro' ), + '', + '' + ), + ], + ], + 'validate_stripe_api_live_secret_key_button' => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => sprintf( '', self::STRIPE_TEST_SECRET_KEY . '_validate', wp_create_nonce( self::STRIPE_TEST_SECRET_KEY ), esc_html__( 'Validate Live API Key', 'elementor-pro' ) ), + ], + ], + 'stripe_legal_disclaimer' => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => sprintf( + /* translators: %s:
    . */ + esc_html__( 'Please note: The Stripe name and logos are trademarks or service marks of Stripe, Inc. or its affiliates in the U.S. and other countries. %s Other names may be trademarks of their respective owners.', 'elementor-pro' ), + '
    ' + ), + ], + ], + ], + ] ); + } + + public function __construct() { + parent::__construct(); + + $this->stripe_handler = new Stripe_Handler(); + + add_action( 'wp_ajax_submit_stripe_form', [ $this, 'submit_stripe_form' ] ); + add_action( 'wp_ajax_nopriv_submit_stripe_form', [ $this, 'submit_stripe_form' ] ); + add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); + + if ( current_user_can( 'administrator' ) && API::is_licence_has_feature( static::STRIPE_LICENCE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 999 ); + } + add_action( 'wp_ajax_' . self::STRIPE_TEST_SECRET_KEY . '_validate', [ $this, 'ajax_validate_secret_key' ] ); + add_action( 'wp_ajax_' . self::STRIPE_LIVE_SECRET_KEY . '_validate', [ $this, 'ajax_validate_secret_key' ] ); + } +} diff --git a/modules/payments/widgets/paypal-button.php b/modules/payments/widgets/paypal-button.php new file mode 100644 index 0000000..f5ba813 --- /dev/null +++ b/modules/payments/widgets/paypal-button.php @@ -0,0 +1,368 @@ + 'D', + self::BILLING_CYCLE_WEEKS => 'W', + self::BILLING_CYCLE_MONTHS => 'M', + self::BILLING_CYCLE_YEARS => 'Y', + ]; + + public function get_name() { + return 'paypal-button'; + } + + public function get_title() { + return esc_html__( 'PayPal Button', 'elementor-pro' ); + } + + public function get_icon() { + return 'eicon-paypal-button'; + } + + public function get_keywords() { + return [ 'paypal', 'payment', 'sell', 'donate' ]; + } + + protected function get_merchant_name() { + return 'PayPal'; + } + + // Retrieve a numerical field from settings, and default to $min if it's too small. + protected function get_numeric_setting( $key, $min = 0 ) { + $num = doubleval( $this->get_settings_for_display( $key ) ); + + return ( $min > $num ) ? $min : $num; + } + + // Print a numerical field from settings, using `get_numeric_setting`. + protected function print_numeric_setting( $key, $min = 0 ) { + // PHPCS - the get_numeric_setting function is safe. + echo $this->get_numeric_setting( $key, $min ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + // Get the currently selected API communication method ( legacy / SDK ). + protected function get_api_method() { + $settings = $this->get_settings_for_display(); + + return ( self::API_TYPE_ADVANCED === $settings['merchant_account'] ) ? 'sdk' : 'legacy'; + } + + // Get validation errors. + protected function get_errors( $squash_errors = true ) { + $settings = $this->get_settings_for_display(); + $errors = []; + + // Don't render errors in the editor. + if ( Plugin::instance()->editor->is_edit_mode() ) { + return $errors; + } + + // No payment method provided. + if ( 'legacy' === $this->get_api_method() ) { + $empty_email = empty( $settings['email'] ); + $empty_sandbox_email = $this->is_sandbox() && empty( $settings['sandbox_email'] ); + + if ( $empty_email || $empty_sandbox_email ) { + $errors[ self::ERROR_MESSAGE_PAYMENT_METHOD ] = $this->get_custom_message( self::ERROR_MESSAGE_PAYMENT_METHOD ); + } + } + + // Other errors. + $empty_product_price = ( self::PAYMENT_TYPE_DONATION !== $settings['type'] && empty( $settings['product_price'] ) ); + $empty_donation_amount = ( self::DONATION_TYPE_FIXED === $settings['donation_type'] && empty( $settings['donation_amount'] ) ); + $empty_tax = ( ! empty( $settings['tax_type'] ) && empty( $settings['tax_rate'] ) ); + + if ( $empty_product_price || $empty_donation_amount || $empty_tax ) { + $errors[ self::ERROR_MESSAGE_GLOBAL ] = $this->get_custom_message( self::ERROR_MESSAGE_GLOBAL ); + } + + // Squash errors to show only a global error. + if ( $squash_errors && 1 < count( $errors ) ) { + return [ + self::ERROR_MESSAGE_GLOBAL => $this->get_custom_message( self::ERROR_MESSAGE_GLOBAL ), + ]; + } + + return $errors; + } + + // Render PayPal's legacy checkout form. + protected function render_legacy_form() { + $settings = $this->get_settings_for_display(); + + // Handle sandbox mode. + if ( ! $this->is_sandbox() ) { + $form_action = self::PROD_URL; + $email = $settings['email']; + } else { + $form_action = self::SANDBOX_URL; + $email = $settings['sandbox_email']; + $this->add_render_attribute( 'button', 'class', 'elementor-payment-sandbox-mode' ); + } + + if ( 'yes' === $settings['open_in_new_window'] ) { + $target = '_blank'; + } else { + $target = '_top'; + } + + // Set PayPal payment settings by payment type. + switch ( $settings['type'] ) { + case self::PAYMENT_TYPE_CHECKOUT: + $cmd = self::CMD_CHECKOUT; + $price_field = [ + 'name' => 'amount', + 'value' => $settings['product_price'], + ]; + break; + + case self::PAYMENT_TYPE_DONATION: + $cmd = self::CMD_DONATION; + $donation_amount = ''; + + // phpcs:ignore + if ( self::DONATION_TYPE_FIXED === $settings['donation_type'] ) { + $donation_amount = $settings['donation_amount']; + } + + $price_field = [ + 'name' => 'amount', + 'value' => $donation_amount, + ]; + break; + + case self::PAYMENT_TYPE_SUBSCRIPTION: + $cmd = self::CMD_SUBSCRIPTION; + $price_field = [ + 'name' => 'a3', + 'value' => $settings['product_price'], + ]; + $auto_renewal = ( 'yes' === $settings['auto_renewal'] ) ? 1 : 0; + $billing_cycle = self::BILLING_CYCLE_TYPES[ $settings['billing_cycle'] ]; + break; + } + + // PayPal HTML reference: + // https://developer.paypal.com/docs/paypal-payments-standard/integration-guide/html-reference-landing/ + + ?> +
    + + + + + + + + + + + + + + + + + + + + + + + add_render_attribute( 'button', 'type', 'submit' ); + $this->add_render_attribute( 'button', 'class', 'elementor-paypal-legacy' ); + parent::render_button( null, 'button' ); + + foreach ( $this->get_errors() as $type => $message ) { + ?> +
    + +
    + +
    + get_api_method() ) { + case 'legacy': + $this->render_legacy_form(); + break; + } + } + + // Account details section. + protected function register_account_section() { + $this->start_controls_section( + 'section_account', + [ + 'label' => esc_html__( 'Pricing & Payments', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'merchant_account', + [ + 'label' => esc_html__( 'Merchant Account', 'elementor-pro' ), + 'type' => Controls_Manager::HIDDEN, + 'default' => self::API_TYPE_SIMPLE, + 'options' => [ + self::API_TYPE_SIMPLE => esc_html__( 'Default (Simple)', 'elementor-pro' ), + self::API_TYPE_ADVANCED => esc_html__( 'Custom (Advanced)', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'email', + [ + 'label' => esc_html__( 'PayPal Account', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'description' => esc_html__( 'Transactions made through your PayPal button will be registered under this account.', 'elementor-pro' ), + 'label_block' => true, + 'condition' => [ + 'merchant_account' => self::API_TYPE_SIMPLE, + ], + 'placeholder' => 'yours@email.com', + ] + ); + + $this->add_control( + 'sdk_token', + [ + 'label' => esc_html__( 'SDK Token', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'label_block' => true, + 'condition' => [ + 'merchant_account' => self::API_TYPE_ADVANCED, + ], + ] + ); + + $this->register_product_controls(); + + $this->end_controls_section(); + } + + /** + * Updates Button tab controls in 'Style' tab + * + * @since 3.7.0 + */ + public function register_paypal_button_controls() { + parent::register_controls(); + + $this->update_control( 'selected_icon', [ + 'default' => [ + 'value' => 'fab fa-paypal', + 'library' => 'fa-brands', + ], + ] ); + + $this->update_control( 'background_color', [ + 'default' => '#032E82', + ] ); + } + + /** + * Edit button control initial UI + * + * @since 3.7.0 + * + */ + protected function register_controls() { + $this->register_paypal_button_controls(); + } + + // Custom sandbox controls. + protected function register_sandbox_controls() { + $this->add_control( + 'sandbox_email', + [ + 'label' => esc_html__( 'Sandbox Email Account', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'description' => esc_html__( 'This is the address given to you by PayPal when you set up a sandbox with your developer account. You can use the sandbox to test your purchase flow.', 'elementor-pro' ), + 'label_block' => true, + 'condition' => [ + 'sandbox_mode' => 'yes', + ], + ] + ); + } + + // This widget extends the button core widget and therefore needs to overwrite the widget-base core CSS config. + public function get_css_config() { + $widget_name = 'payments'; + + $direction = is_rtl() ? '-rtl' : ''; + + $css_file_path = 'css/widget-' . $widget_name . $direction . '.min.css'; + + /* + * Currently this widget does not support custom-breakpoints in its CSS file. + * In order to support it, this widget needs to get the CSS config from the base-widget-trait.php. + * But to make sure that it implements the Pro assets-path due to the fact that it extends a Core widget. + */ + return [ + 'key' => $widget_name, + 'version' => ELEMENTOR_PRO_VERSION, + 'file_path' => ELEMENTOR_PRO_ASSETS_PATH . $css_file_path, + 'data' => [ + 'file_url' => ELEMENTOR_PRO_ASSETS_URL . $css_file_path, + ], + ]; + } +} diff --git a/modules/payments/widgets/stripe-button.php b/modules/payments/widgets/stripe-button.php new file mode 100644 index 0000000..f92468f --- /dev/null +++ b/modules/payments/widgets/stripe-button.php @@ -0,0 +1,534 @@ + _x( 'AED', 'Currency', 'elementor-pro' ), + 'AFN' => _x( 'AFN', 'Currency', 'elementor-pro' ), + 'ALL' => _x( 'ALL', 'Currency', 'elementor-pro' ), + 'AMD' => _x( 'AMD', 'Currency', 'elementor-pro' ), + 'ANG' => _x( 'ANG', 'Currency', 'elementor-pro' ), + 'AOA' => _x( 'AOA', 'Currency', 'elementor-pro' ), + 'ARS' => _x( 'ARS', 'Currency', 'elementor-pro' ), + 'AUD' => _x( 'AUD', 'Currency', 'elementor-pro' ), + 'AWG' => _x( 'AWG', 'Currency', 'elementor-pro' ), + 'AZN' => _x( 'AZN', 'Currency', 'elementor-pro' ), + 'BAM' => _x( 'BAM', 'Currency', 'elementor-pro' ), + 'BBD' => _x( 'BBD', 'Currency', 'elementor-pro' ), + 'BDT' => _x( 'BDT', 'Currency', 'elementor-pro' ), + 'BGN' => _x( 'BGN', 'Currency', 'elementor-pro' ), + 'BIF' => _x( 'BIF', 'Currency', 'elementor-pro' ), + 'BMD' => _x( 'BMD', 'Currency', 'elementor-pro' ), + 'BND' => _x( 'BND', 'Currency', 'elementor-pro' ), + 'BOB' => _x( 'BOB', 'Currency', 'elementor-pro' ), + 'BRL' => _x( 'BRL', 'Currency', 'elementor-pro' ), + 'BSD' => _x( 'BSD', 'Currency', 'elementor-pro' ), + 'BWP' => _x( 'BWP', 'Currency', 'elementor-pro' ), + 'BYN' => _x( 'BYN', 'Currency', 'elementor-pro' ), + 'BZD' => _x( 'BZD', 'Currency', 'elementor-pro' ), + 'CAD' => _x( 'CAD', 'Currency', 'elementor-pro' ), + 'CDF' => _x( 'CDF', 'Currency', 'elementor-pro' ), + 'CHF' => _x( 'CHF', 'Currency', 'elementor-pro' ), + 'CLP' => _x( 'CLP', 'Currency', 'elementor-pro' ), + 'CNY' => _x( 'CNY', 'Currency', 'elementor-pro' ), + 'COP' => _x( 'COP', 'Currency', 'elementor-pro' ), + 'CRC' => _x( 'CRC', 'Currency', 'elementor-pro' ), + 'CVE' => _x( 'CVE', 'Currency', 'elementor-pro' ), + 'CZK' => _x( 'CZK', 'Currency', 'elementor-pro' ), + 'DJF' => _x( 'DJF', 'Currency', 'elementor-pro' ), + 'DKK' => _x( 'DKK', 'Currency', 'elementor-pro' ), + 'DOP' => _x( 'DOP', 'Currency', 'elementor-pro' ), + 'DZD' => _x( 'DZD', 'Currency', 'elementor-pro' ), + 'EGP' => _x( 'EGP', 'Currency', 'elementor-pro' ), + 'ETB' => _x( 'ETB', 'Currency', 'elementor-pro' ), + 'EUR' => _x( 'EUR', 'Currency', 'elementor-pro' ), + 'FJD' => _x( 'FJD', 'Currency', 'elementor-pro' ), + 'FKP' => _x( 'FKP', 'Currency', 'elementor-pro' ), + 'GBP' => _x( 'GBP', 'Currency', 'elementor-pro' ), + 'GEL' => _x( 'GEL', 'Currency', 'elementor-pro' ), + 'GIP' => _x( 'GIP', 'Currency', 'elementor-pro' ), + 'GMD' => _x( 'GMD', 'Currency', 'elementor-pro' ), + 'GNF' => _x( 'GNF', 'Currency', 'elementor-pro' ), + 'GTQ' => _x( 'GTQ', 'Currency', 'elementor-pro' ), + 'GYD' => _x( 'GYD', 'Currency', 'elementor-pro' ), + 'HKD' => _x( 'HKD', 'Currency', 'elementor-pro' ), + 'HNL' => _x( 'HNL', 'Currency', 'elementor-pro' ), + 'HRK' => _x( 'HRK', 'Currency', 'elementor-pro' ), + 'HTG' => _x( 'HTG', 'Currency', 'elementor-pro' ), + 'IDR' => _x( 'IDR', 'Currency', 'elementor-pro' ), + 'ILS' => _x( 'ILS', 'Currency', 'elementor-pro' ), + 'INR' => _x( 'INR', 'Currency', 'elementor-pro' ), + 'ISK' => _x( 'ISK', 'Currency', 'elementor-pro' ), + 'JMD' => _x( 'JMD', 'Currency', 'elementor-pro' ), + 'JPY' => _x( 'JPY', 'Currency', 'elementor-pro' ), + 'KES' => _x( 'KES', 'Currency', 'elementor-pro' ), + 'KGS' => _x( 'KGS', 'Currency', 'elementor-pro' ), + 'KHR' => _x( 'KHR', 'Currency', 'elementor-pro' ), + 'KMF' => _x( 'KMF', 'Currency', 'elementor-pro' ), + 'KRW' => _x( 'KRW', 'Currency', 'elementor-pro' ), + 'KYD' => _x( 'KYD', 'Currency', 'elementor-pro' ), + 'KZT' => _x( 'KZT', 'Currency', 'elementor-pro' ), + 'LAK' => _x( 'LAK', 'Currency', 'elementor-pro' ), + 'LBP' => _x( 'LBP', 'Currency', 'elementor-pro' ), + 'LKR' => _x( 'LKR', 'Currency', 'elementor-pro' ), + 'LRD' => _x( 'LRD', 'Currency', 'elementor-pro' ), + 'LSL' => _x( 'LSL', 'Currency', 'elementor-pro' ), + 'MAD' => _x( 'MAD', 'Currency', 'elementor-pro' ), + 'MDL' => _x( 'MDL', 'Currency', 'elementor-pro' ), + 'MGA' => _x( 'MGA', 'Currency', 'elementor-pro' ), + 'MKD' => _x( 'MKD', 'Currency', 'elementor-pro' ), + 'MMK' => _x( 'MMK', 'Currency', 'elementor-pro' ), + 'MNT' => _x( 'MNT', 'Currency', 'elementor-pro' ), + 'MOP' => _x( 'MOP', 'Currency', 'elementor-pro' ), + 'MRO' => _x( 'MRO', 'Currency', 'elementor-pro' ), + 'MUR' => _x( 'MUR', 'Currency', 'elementor-pro' ), + 'MVR' => _x( 'MVR', 'Currency', 'elementor-pro' ), + 'MWK' => _x( 'MWK', 'Currency', 'elementor-pro' ), + 'MXN' => _x( 'MXN', 'Currency', 'elementor-pro' ), + 'MYR' => _x( 'MYR', 'Currency', 'elementor-pro' ), + 'MZN' => _x( 'MZN', 'Currency', 'elementor-pro' ), + 'NAD' => _x( 'NAD', 'Currency', 'elementor-pro' ), + 'NGN' => _x( 'NGN', 'Currency', 'elementor-pro' ), + 'NIO' => _x( 'NIO', 'Currency', 'elementor-pro' ), + 'NOK' => _x( 'NOK', 'Currency', 'elementor-pro' ), + 'NPR' => _x( 'NPR', 'Currency', 'elementor-pro' ), + 'NZD' => _x( 'NZD', 'Currency', 'elementor-pro' ), + 'PAB' => _x( 'PAB', 'Currency', 'elementor-pro' ), + 'PEN' => _x( 'PEN', 'Currency', 'elementor-pro' ), + 'PGK' => _x( 'PGK', 'Currency', 'elementor-pro' ), + 'PHP' => _x( 'PHP', 'Currency', 'elementor-pro' ), + 'PKR' => _x( 'PKR', 'Currency', 'elementor-pro' ), + 'PLN' => _x( 'PLN', 'Currency', 'elementor-pro' ), + 'PYG' => _x( 'PYG', 'Currency', 'elementor-pro' ), + 'QAR' => _x( 'QAR', 'Currency', 'elementor-pro' ), + 'RON' => _x( 'RON', 'Currency', 'elementor-pro' ), + 'RSD' => _x( 'RSD', 'Currency', 'elementor-pro' ), + 'RUB' => _x( 'RUB', 'Currency', 'elementor-pro' ), + 'RWF' => _x( 'RWF', 'Currency', 'elementor-pro' ), + 'SAR' => _x( 'SAR', 'Currency', 'elementor-pro' ), + 'SBD' => _x( 'SBD', 'Currency', 'elementor-pro' ), + 'SCR' => _x( 'SCR', 'Currency', 'elementor-pro' ), + 'SEK' => _x( 'SEK', 'Currency', 'elementor-pro' ), + 'SGD' => _x( 'SGD', 'Currency', 'elementor-pro' ), + 'SHP' => _x( 'SHP', 'Currency', 'elementor-pro' ), + 'SLL' => _x( 'SLL', 'Currency', 'elementor-pro' ), + 'SOS' => _x( 'SOS', 'Currency', 'elementor-pro' ), + 'SRD' => _x( 'SRD', 'Currency', 'elementor-pro' ), + 'STD' => _x( 'STD', 'Currency', 'elementor-pro' ), + 'SZL' => _x( 'SZL', 'Currency', 'elementor-pro' ), + 'THB' => _x( 'THB', 'Currency', 'elementor-pro' ), + 'TJS' => _x( 'TJS', 'Currency', 'elementor-pro' ), + 'TOP' => _x( 'TOP', 'Currency', 'elementor-pro' ), + 'TRY' => _x( 'TRY', 'Currency', 'elementor-pro' ), + 'TTD' => _x( 'TTD', 'Currency', 'elementor-pro' ), + 'TWD' => _x( 'TWD', 'Currency', 'elementor-pro' ), + 'TZS' => _x( 'TZS', 'Currency', 'elementor-pro' ), + 'UAH' => _x( 'UAH', 'Currency', 'elementor-pro' ), + 'UYU' => _x( 'UYU', 'Currency', 'elementor-pro' ), + 'UZS' => _x( 'UZS', 'Currency', 'elementor-pro' ), + 'VND' => _x( 'VND', 'Currency', 'elementor-pro' ), + 'VUV' => _x( 'VUV', 'Currency', 'elementor-pro' ), + 'WST' => _x( 'WST', 'Currency', 'elementor-pro' ), + 'XAF' => _x( 'XAF', 'Currency', 'elementor-pro' ), + 'XCD' => _x( 'XCD', 'Currency', 'elementor-pro' ), + 'XOF' => _x( 'XOF', 'Currency', 'elementor-pro' ), + 'XPF' => _x( 'XPF', 'Currency', 'elementor-pro' ), + 'YER' => _x( 'YER', 'Currency', 'elementor-pro' ), + 'ZAR' => _x( 'ZAR', 'Currency', 'elementor-pro' ), + 'ZMW' => _x( 'ZMW', 'Currency', 'elementor-pro' ), + 'USD' => _x( 'USD', 'Currency', 'elementor-pro' ), + ]; + } + + /** + * Global error message. + * + * @since 3.7.0 + * + * @return string + */ + protected function stripe_global_error_massage() { + return esc_html__( 'Something went wrong', 'elementor-pro' ); + } + + /** + * Gateway error message. + * + * @since 3.7.0 + * + * @return string + */ + protected function stripe_gateway_error_massage() { + return esc_html__( 'Gateway not connected. Contact seller', 'elementor-pro' ); + } + + /** + * Get validation errors. + * + * @since 3.7.0 + * + * @return array + */ + protected function get_errors() { + $settings = $this->get_settings_for_display(); + $errors = []; + + if ( empty( $settings['product_name'] ) || empty( $settings['stripe_product_price'] ) ) { + $errors[ self::ERROR_MESSAGE_GLOBAL ] = $this->get_custom_message( self::ERROR_MESSAGE_GLOBAL ); + } + + return $errors; + } + + /** + * Render the payment button. + * + * @param string $tag - this is an inheritance from the payment_button class + * + * @since 3.7.0 + * + * @return array + */ + protected function render_button( Widget_Base $instance = null, $tag = 'a' ) { + $settings = $this->get_settings_for_display(); + ?> + +
    + + + + + + + + + + add_render_attribute( 'input', 'type', 'submit' ); + $this->add_render_attribute( 'input', 'class', 'elementor-stripe' ); + parent::render_button( null, 'button' ); + + foreach ( $this->get_errors() as $type => $message ) { + ?> +
    + +
    + +
    + start_controls_section( + 'section_stripe_account', + [ + 'label' => esc_html__( 'Pricing & Payments', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'test_environment_msg', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => sprintf( + /* translators: 1: Elementor's integrations settings link opening tab, 2: Link closing tag. */ + esc_html__( 'For this widget to work, you need to set your Stripe API keys in the %1$sIntegrations Settings%2$s.', 'elementor-pro' ), + sprintf( '', admin_url( 'admin.php?page=elementor#tab-integrations' ) ), + '' + ), + 'separator' => 'after', + ] + ); + + $this->register_product_controls(); + + $this->remove_control( 'type' ); + + $this->update_control( + 'type', + [ + 'default' => self::STRIPE_PAYMENT_TYPE_CHECKOUT, + 'options' => [ + self::STRIPE_PAYMENT_TYPE_CHECKOUT => esc_html__( 'Checkout', 'elementor-pro' ), + ], + ] + ); + + $this->update_control( + 'product_name', + [ + 'label' => esc_html__( 'Product Name', 'elementor-pro' ), + 'required' => true, + ] + ); + + $this->add_control( + 'stripe_currency', + [ + 'label' => esc_html__( 'Currency', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'options' => $this->get_stripe_currencies(), + 'frontend_available' => true, + 'multiple' => false, + 'default' => 'USD', + 'description' => sprintf( + /* translators: 1: Stripe api key explanation link opening tag, 2: Link closing tag. */ + esc_html__( 'Notice! Please make sure to meet Stripe\'s guidelines regarding minimum charge amounts. %1$s Learn more. %2$s', 'elementor-pro' ), + sprintf( '', Module::STRIPE_TRANSACTIONS_LINK ), + '' + ), + 'render_type' => 'none', + 'required' => true, + 'select2options' => [ + 'placeholder' => esc_html__( 'USD', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'stripe_product_price', + [ + 'label' => esc_html__( 'Product Price', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => '0.00', + 'dynamic' => [ + 'active' => true, + ], + 'min' => 0, + ] + ); + + $this->remove_control( 'product_price' ); + + $this->remove_control( 'currency' ); + + $this->remove_control( 'billing_cycle' ); + + $this->remove_control( 'auto_renewal' ); + + $this->remove_control( 'product_sku' ); + + $this->remove_control( 'quantity' ); + + $this->add_control( + 'stripe_quantity', + [ + 'label' => esc_html__( 'Quantity', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 1, + ] + ); + + $this->remove_control( 'tax_type' ); + + $this->add_control( + 'shipping_amount', + [ + 'label' => esc_html__( 'Shipping Price', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 0, + 'dynamic' => [ + 'active' => true, + ], + 'min' => 0, + ] + ); + + $this->add_control( + 'stripe_test_env_tax_rates_list', + [ + 'label' => esc_html__( 'Tax Rate', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ '' => esc_html__( 'None', 'elementor-pro' ) ], + 'condition' => [ + 'sandbox_mode[value]' => 'yes', + ], + 'description' => esc_html__( 'To manage these options, go to your Stripe account > Products > Tax Rates.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'stripe_live_env_tax_rates_list', + [ + 'label' => esc_html__( 'Tax Rate', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ '' => esc_html__( 'None', 'elementor-pro' ) ], + 'condition' => [ + 'sandbox_mode[value]!' => 'yes', + ], + 'description' => esc_html__( 'To manage these options, go to your Stripe account > Products > Tax Rates.', 'elementor-pro' ), + ] + ); + + $this->end_controls_section(); + } + /** + * Updates Button tab controls in 'Style' tab + * + * @since 3.7.0 + */ + public function register_stripe_button_controls() { + parent::register_controls(); + + $this->update_control( 'selected_icon', [ + 'default' => [ + 'value' => 'fab fa-stripe-s', + 'library' => 'fa-brands', + ], + 'recommended' => [ + 'fa-brands' => [ + 'stripe-s', + 'stripe', + 'cc-stripe', + ], + ], + ] ); + + $this->update_control( 'background_color', [ + 'default' => '#635bff', + ] ); + } + + /** + * Edit button control initial UI + * + * @since 3.7.0 + * + */ + protected function register_controls() { + $this->register_stripe_button_controls(); + } + + /** + * Update error messages controls text and placeholders. + * + * @since 3.7.0 + * + */ + protected function update_error_massages() { + $this->update_control( + 'error_message_' . self::ERROR_MESSAGE_GLOBAL, + [ + 'placeholder' => $this->stripe_global_error_massage(), + 'default' => $this->stripe_global_error_massage(), + ] + ); + + $this->update_control( + 'error_message_' . self::ERROR_MESSAGE_PAYMENT_METHOD, + [ + 'placeholder' => $this->stripe_gateway_error_massage(), + 'default' => $this->stripe_gateway_error_massage(), + ] + ); + } + + /** + * Custom sandbox controls. + * + * @since 3.7.0 + * + */ + protected function after_custom_messages_toggle() { + $this->add_control( + 'custom_error_on_notice', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'These messages override Stripe\'s error messages.', 'elementor-pro' ) . '
    ' . esc_html__( 'Use them on your live site - not while testing.', 'elementor-pro' ), + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'custom_messages!' => '', + ], + ] + ); + } + + protected function register_sandbox_controls() { + $this->update_control( 'sandbox_mode', + [ + 'label' => esc_html__( 'Stripe test environment', 'elementor-pro' ), + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'sandbox_mode_on_notice', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => sprintf( + /* translators: 1: Elementor's integrations settings link opening tab, 2: Link closing tag. */ + esc_html__( 'Complete the entire checkout experience on your site with a mock payment method, using the Stripe Test key in the %1$sIntegrations Settings%2$s.', 'elementor-pro' ), + sprintf( '', admin_url( 'admin.php?page=elementor#tab-integrations' ) ), + '' + ), + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'sandbox_mode' => 'yes', + ], + ] + ); + + $this->remove_control( 'sandbox_email' ); + } +} diff --git a/modules/popup/admin-menu-items/popups-menu-item.php b/modules/popup/admin-menu-items/popups-menu-item.php new file mode 100644 index 0000000..ba58a01 --- /dev/null +++ b/modules/popup/admin-menu-items/popups-menu-item.php @@ -0,0 +1,32 @@ +license_admin->get_connect_url( [ + 'utm_source' => 'popup-templates', + 'utm_medium' => 'wp-dash', + 'utm_campaign' => 'connect-and-activate-license', + ] ); + + $renew_url = 'https://go.elementor.com/renew-popups/'; + + return API::is_license_expired() + ? $renew_url + : $connect_url; + } + + public function get_parent_slug() { + return Source_Local::ADMIN_MENU_SLUG; + } + + public function get_label() { + return esc_html__( 'Popups', 'elementor-pro' ); + } + + public function get_page_title() { + return esc_html__( 'Popups', 'elementor-pro' ); + } + + public function get_promotion_title() { + return esc_html__( 'Get Popup Builder', 'elementor-pro' ); + } + + public function get_promotion_description() { + return esc_html__( + "Create custom designed Popups using all of Elementor's widgets. Use advanced display conditions and triggers to display the right popup, to the right visitor, at the right time and maximize conversions.", + 'elementor-pro' + ); + } + + /** + * @deprecated use get_promotion_description instead + * @return void + */ + public function render_promotion_description() { + echo $this->get_promotion_description(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } +} diff --git a/modules/popup/assets/images/timing-tab.svg b/modules/popup/assets/images/timing-tab.svg new file mode 100644 index 0000000..dc25b6d --- /dev/null +++ b/modules/popup/assets/images/timing-tab.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/modules/popup/assets/images/timing/browsers.svg b/modules/popup/assets/images/timing/browsers.svg new file mode 100644 index 0000000..1b5b88d --- /dev/null +++ b/modules/popup/assets/images/timing/browsers.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/modules/popup/assets/images/timing/devices.svg b/modules/popup/assets/images/timing/devices.svg new file mode 100644 index 0000000..21d002e --- /dev/null +++ b/modules/popup/assets/images/timing/devices.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/modules/popup/assets/images/timing/logged_in.svg b/modules/popup/assets/images/timing/logged_in.svg new file mode 100644 index 0000000..47bf01e --- /dev/null +++ b/modules/popup/assets/images/timing/logged_in.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/modules/popup/assets/images/timing/page_views.svg b/modules/popup/assets/images/timing/page_views.svg new file mode 100644 index 0000000..565ee7e --- /dev/null +++ b/modules/popup/assets/images/timing/page_views.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/modules/popup/assets/images/timing/schedule.svg b/modules/popup/assets/images/timing/schedule.svg new file mode 100644 index 0000000..75d5fbe --- /dev/null +++ b/modules/popup/assets/images/timing/schedule.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/popup/assets/images/timing/sessions.svg b/modules/popup/assets/images/timing/sessions.svg new file mode 100644 index 0000000..63a5bc0 --- /dev/null +++ b/modules/popup/assets/images/timing/sessions.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/modules/popup/assets/images/timing/sources.svg b/modules/popup/assets/images/timing/sources.svg new file mode 100644 index 0000000..7ff097f --- /dev/null +++ b/modules/popup/assets/images/timing/sources.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/modules/popup/assets/images/timing/times.svg b/modules/popup/assets/images/timing/times.svg new file mode 100644 index 0000000..704c75c --- /dev/null +++ b/modules/popup/assets/images/timing/times.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/modules/popup/assets/images/timing/url.svg b/modules/popup/assets/images/timing/url.svg new file mode 100644 index 0000000..ed52ff2 --- /dev/null +++ b/modules/popup/assets/images/timing/url.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/modules/popup/assets/images/triggers-tab.svg b/modules/popup/assets/images/triggers-tab.svg new file mode 100644 index 0000000..0a23f2a --- /dev/null +++ b/modules/popup/assets/images/triggers-tab.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/modules/popup/assets/images/triggers/click.svg b/modules/popup/assets/images/triggers/click.svg new file mode 100644 index 0000000..67ea5aa --- /dev/null +++ b/modules/popup/assets/images/triggers/click.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/modules/popup/assets/images/triggers/exit_intent.svg b/modules/popup/assets/images/triggers/exit_intent.svg new file mode 100644 index 0000000..5f161ac --- /dev/null +++ b/modules/popup/assets/images/triggers/exit_intent.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/modules/popup/assets/images/triggers/inactivity.svg b/modules/popup/assets/images/triggers/inactivity.svg new file mode 100644 index 0000000..7cf15f8 --- /dev/null +++ b/modules/popup/assets/images/triggers/inactivity.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/modules/popup/assets/images/triggers/page_load.svg b/modules/popup/assets/images/triggers/page_load.svg new file mode 100644 index 0000000..edf92ba --- /dev/null +++ b/modules/popup/assets/images/triggers/page_load.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/modules/popup/assets/images/triggers/scrolling.svg b/modules/popup/assets/images/triggers/scrolling.svg new file mode 100644 index 0000000..b399768 --- /dev/null +++ b/modules/popup/assets/images/triggers/scrolling.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/modules/popup/assets/images/triggers/scrolling_to.svg b/modules/popup/assets/images/triggers/scrolling_to.svg new file mode 100644 index 0000000..5c405ef --- /dev/null +++ b/modules/popup/assets/images/triggers/scrolling_to.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/modules/popup/display-settings/base.php b/modules/popup/display-settings/base.php new file mode 100644 index 0000000..62a3586 --- /dev/null +++ b/modules/popup/display-settings/base.php @@ -0,0 +1,62 @@ +current_group = $group_name; + + $args = [ + 'type' => Controls_Manager::HEADING, + 'label' => $group_title, + ]; + + $this->add_control( $group_name . '_heading', $args ); + } + + protected function end_settings_group() { + $this->add_control( + $this->current_group, + [ + 'type' => Controls_Manager::SWITCHER, + 'classes' => 'elementor-popup__display-settings__group-toggle', + 'frontend_available' => true, + ] + ); + + $this->current_group = null; + } + + protected function add_settings_group_control( $id, array $args ) { + $id = $this->get_prefixed_control_id( $id ); + + $args['frontend_available'] = true; + + if ( ! empty( $args['condition'] ) ) { + $args['condition'] = array_combine( + array_map( function( $key ) { + return $this->current_group . '_' . $key; + }, array_keys( $args['condition'] ) ), + $args['condition'] + ); + } + + $args['condition'][ $this->current_group ] = 'yes'; + + return $this->add_control( $id, $args ); + } + + protected function get_prefixed_control_id( $id ) { + return $this->current_group . '_' . $id; + } +} diff --git a/modules/popup/display-settings/timing.php b/modules/popup/display-settings/timing.php new file mode 100644 index 0000000..c4229bc --- /dev/null +++ b/modules/popup/display-settings/timing.php @@ -0,0 +1,310 @@ +start_controls_section( 'timing' ); + + $this->start_settings_group( 'page_views', esc_html__( 'Show after X page views', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'views', + [ + 'type' => Controls_Manager::NUMBER, + 'label' => esc_html__( 'Page Views', 'elementor-pro' ), + 'default' => 3, + 'min' => 1, + ] + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'sessions', esc_html__( 'Show after X sessions', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'sessions', + [ + 'type' => Controls_Manager::NUMBER, + 'label' => esc_html__( 'Sessions', 'elementor-pro' ), + 'default' => 2, + 'min' => 1, + ] + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'times', esc_html__( 'Show up to X times', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'times', + [ + 'type' => Controls_Manager::NUMBER, + 'label' => esc_html__( 'Times', 'elementor-pro' ), + 'default' => 3, + 'min' => 1, + ] + ); + + $this->add_settings_group_control( + 'period', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Per', 'elementor-pro' ), + 'default' => '', // Backward Compatibility - Persisting is old default value. + 'options' => [ + '' => esc_html__( 'Persisting', 'elementor-pro' ), + 'session' => esc_html__( 'Session', 'elementor-pro' ), + 'day' => esc_html__( 'Day', 'elementor-pro' ), + 'week' => esc_html__( 'Week', 'elementor-pro' ), + 'month' => esc_html__( 'Month', 'elementor-pro' ), + ], + ] + ); + + $this->add_settings_group_control( + 'count', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Count', 'elementor-pro' ), + 'options' => [ + '' => esc_html__( 'On Open', 'elementor-pro' ), + 'close' => esc_html__( 'On Close', 'elementor-pro' ), + ], + ] + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'url', esc_html__( 'When arriving from specific URL', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'action', + [ + 'type' => Controls_Manager::SELECT, + 'default' => 'show', + 'options' => [ + 'show' => esc_html__( 'Show', 'elementor-pro' ), + 'hide' => esc_html__( 'Hide', 'elementor-pro' ), + 'regex' => esc_html__( 'Regex', 'elementor-pro' ), + ], + ] + ); + + $this->add_settings_group_control( + 'url', + [ + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'URL', 'elementor-pro' ), + ] + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'sources', esc_html__( 'Show when arriving from', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'sources', + [ + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'default' => [ 'search', 'external', 'internal' ], + 'options' => [ + 'search' => esc_html__( 'Search Engines', 'elementor-pro' ), + 'external' => esc_html__( 'External Links', 'elementor-pro' ), + 'internal' => esc_html__( 'Internal Links', 'elementor-pro' ), + ], + ] + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'logged_in', esc_html__( 'Hide for logged in users', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'users', + [ + 'type' => Controls_Manager::SELECT, + 'default' => 'all', + 'options' => [ + 'all' => esc_html__( 'All Users', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + ] + ); + + global $wp_roles; + + $roles = array_map( function( $role ) { + return $role['name']; + }, $wp_roles->roles ); + + $this->add_settings_group_control( + 'roles', + [ + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'default' => [], + 'options' => $roles, + 'select2options' => [ + 'placeholder' => esc_html__( 'Select Roles', 'elementor-pro' ), + ], + 'condition' => [ + 'users' => 'custom', + ], + ] + ); + + $this->end_settings_group(); + + $available_devices = [ + 'desktop' => esc_html__( 'Desktop', 'elementor-pro' ), + ]; + + $default_devices = [ 'desktop' ]; + + $active_breakpoints = Plugin::elementor()->breakpoints->get_active_breakpoints(); + + foreach ( $active_breakpoints as $breakpoint_key => $breakpoint ) { + $available_devices[ $breakpoint_key ] = $breakpoint->get_label(); + $default_devices[] = $breakpoint_key; + } + + $this->start_settings_group( 'devices', esc_html__( 'Show on devices', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'devices', + [ + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'default' => $default_devices, + 'options' => $available_devices, + ] + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'browsers', esc_html__( 'Show on browsers', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'browsers', + [ + 'type' => Controls_Manager::SELECT, + 'default' => 'all', + 'options' => [ + 'all' => esc_html__( 'All Browsers', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + ] + ); + + $this->add_settings_group_control( + 'browsers_options', + [ + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'default' => [], + 'options' => [ + 'ie' => esc_html__( 'Internet Explorer', 'elementor-pro' ), + 'chrome' => esc_html__( 'Chrome', 'elementor-pro' ), + 'edge' => esc_html__( 'Edge', 'elementor-pro' ), + 'firefox' => esc_html__( 'Firefox', 'elementor-pro' ), + 'safari' => esc_html__( 'Safari', 'elementor-pro' ), + ], + 'condition' => [ + 'browsers' => 'custom', + ], + ] + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'schedule', esc_html__( 'Schedule date and time', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'timezone', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Timezone', 'elementor-pro' ), + 'default' => 'site', + 'options' => [ + 'site' => esc_html__( 'Site', 'elementor-pro' ), + 'visitor' => esc_html__( 'Visitor', 'elementor-pro' ), + ], + ] + ); + + $this->add_settings_group_control( + 'start_date', + [ + 'label' => esc_html__( 'Start', 'elementor-pro' ), + 'type' => Controls_Manager::DATE_TIME, + 'picker_options' => [ + 'enableTime' => true, + 'minDate' => 'today', + ], + 'validation' => [ + 'date_time' => [ + 'control_name' => $this->get_prefixed_control_id( 'end_date' ), + 'operator' => '<=', + ], + ], + ] + ); + + $this->add_settings_group_control( + 'end_date', + [ + 'label' => esc_html__( 'End', 'elementor-pro' ), + 'type' => Controls_Manager::DATE_TIME, + 'picker_options' => [ + 'enableTime' => true, + 'minDate' => 'today', + ], + 'validation' => [ + 'date_time' => [ + 'control_name' => $this->get_prefixed_control_id( 'start_date' ), + 'operator' => '>=', + ], + ], + ] + ); + + $datetime = new \DateTime( 'now', new \DateTimeZone( wp_timezone_string() ) ); + $datetime = $datetime->format( 'Y-m-d H:i:s' ); + + $this->add_settings_group_control( + 'server_datetime', + [ + 'type' => Controls_Manager::HIDDEN, + 'default' => $datetime, + ] + ); + + $this->end_settings_group(); + + $this->end_controls_section(); + } +} diff --git a/modules/popup/display-settings/triggers.php b/modules/popup/display-settings/triggers.php new file mode 100644 index 0000000..910da7b --- /dev/null +++ b/modules/popup/display-settings/triggers.php @@ -0,0 +1,127 @@ +start_controls_section( 'triggers' ); + + $this->start_settings_group( 'page_load', esc_html__( 'On Page Load', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'delay', + [ + 'type' => Controls_Manager::NUMBER, + 'label' => esc_html__( 'Within', 'elementor-pro' ) . ' (sec)', + 'default' => 0, + 'min' => 0, + 'step' => 0.1, + ] + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'scrolling', esc_html__( 'On Scroll', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'direction', + [ + 'type' => Controls_Manager::SELECT, + 'label' => esc_html__( 'Direction', 'elementor-pro' ), + 'default' => 'down', + 'options' => [ + 'down' => esc_html__( 'Down', 'elementor-pro' ), + 'up' => esc_html__( 'Up', 'elementor-pro' ), + ], + ] + ); + + $this->add_settings_group_control( + 'offset', + [ + 'type' => Controls_Manager::NUMBER, + 'label' => esc_html__( 'Within', 'elementor-pro' ) . ' (%)', + 'default' => 50, + 'min' => 1, + 'max' => 100, + 'condition' => [ + 'direction' => 'down', + ], + ] + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'scrolling_to', esc_html__( 'On Scroll To Element', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'selector', + [ + 'type' => Controls_Manager::TEXT, + 'label' => esc_html__( 'Selector', 'elementor-pro' ), + 'placeholder' => '.my-class', + 'ai' => [ + 'active' => false, + ], + ], + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'click', esc_html__( 'On Click', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'times', + [ + 'label' => esc_html__( 'Clicks', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 1, + 'min' => 1, + ] + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'inactivity', esc_html__( 'After Inactivity', 'elementor-pro' ) ); + + $this->add_settings_group_control( + 'time', + [ + 'type' => Controls_Manager::NUMBER, + 'label' => esc_html__( 'Within', 'elementor-pro' ) . ' (sec)', + 'default' => 30, + 'min' => 1, + 'step' => 0.1, + ] + ); + + $this->end_settings_group(); + + $this->start_settings_group( 'exit_intent', esc_html__( 'On Page Exit Intent', 'elementor-pro' ) ); + + $this->end_settings_group(); + + $this->end_controls_section(); + } +} diff --git a/modules/popup/document.php b/modules/popup/document.php new file mode 100644 index 0000000..2f692fa --- /dev/null +++ b/modules/popup/document.php @@ -0,0 +1,848 @@ +display_settings ) { + $settings = $this->get_display_settings_data(); + + if ( ! $settings ) { + $settings = [ + 'triggers' => [], + 'timing' => [], + ]; + } + + $id = $this->get_main_id(); + + $this->display_settings = [ + 'triggers' => new Triggers( [ + 'id' => $id, + 'settings' => $settings['triggers'], + ] ), + 'timing' => new Timing( [ + 'id' => $id, + 'settings' => $settings['timing'], + ] ), + ]; + } + + return $this->display_settings; + } + + public function get_initial_config() { + $config = parent::get_initial_config(); + + $display_settings = $this->get_display_settings(); + + $config['displaySettings'] = [ + 'triggers' => [ + 'controls' => $display_settings['triggers']->get_controls(), + 'settings' => $display_settings['triggers']->get_settings(), + ], + 'timing' => [ + 'controls' => $display_settings['timing']->get_controls(), + 'settings' => $display_settings['timing']->get_settings(), + ], + ]; + + $config['container'] = '.elementor-popup-modal .dialog-widget-content'; + + return $config; + } + + public function get_name() { + return 'popup'; + } + + public function get_css_wrapper_selector() { + return '#elementor-popup-modal-' . $this->get_main_id(); + } + + public function get_display_settings_data() { + return $this->get_main_meta( self::DISPLAY_SETTINGS_META_KEY ); + } + + public function save_display_settings_data( $display_settings_data ) { + $this->update_main_meta( self::DISPLAY_SETTINGS_META_KEY, $display_settings_data ); + } + + public function get_frontend_settings() { + $settings = parent::get_frontend_settings(); + + $display_settings = $this->get_display_settings(); + + // Disable triggers if the popup is not printed by the theme builder conditions. + // avoid auto show the popup if it's enqueued by a dynamic tag and etc.) + $popups_by_condition = ThemeBuilderModule::instance()->get_conditions_manager()->get_documents_for_location( 'popup' ); + + if ( $popups_by_condition && isset( $popups_by_condition[ $this->get_main_id() ] ) ) { + $settings['triggers'] = $display_settings['triggers']->get_frontend_settings(); + } + + $settings['timing'] = $display_settings['timing']->get_frontend_settings(); + + return $settings; + } + + public function get_export_data() { + $data = parent::get_export_data(); + + $display_settings = $this->get_display_settings(); + + $data['display_settings'] = [ + 'triggers' => $display_settings['triggers']->get_frontend_settings(), + 'timing' => $display_settings['timing']->get_frontend_settings(), + ]; + + return $data; + } + + public function import( array $data ) { + parent::import( $data ); + + $this->save_display_settings_data( $data['display_settings'] ); + } + + protected function register_controls() { + $this->start_controls_section( + 'popup_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_SETTINGS, + ] + ); + + $this->add_responsive_control( + 'width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], // Only CSS `` data-type is allowed, not ``. + 'range' => [ + 'px' => [ + 'min' => 100, + 'max' => 1000, + ], + 'em' => [ + 'min' => 10, + 'max' => 100, + ], + 'rem' => [ + 'min' => 10, + 'max' => 100, + ], + 'vh' => [ + 'min' => 10, + 'max' => 100, + ], + ], + 'default' => [ + 'size' => 640, + ], + 'selectors' => [ + '{{WRAPPER}} .dialog-message' => 'width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'height_type', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'auto', + 'options' => [ + 'auto' => esc_html__( 'Fit To Content', 'elementor-pro' ), + 'fit_to_screen' => esc_html__( 'Fit To Screen', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'selectors_dictionary' => [ + 'fit_to_screen' => '100vh', + ], + 'selectors' => [ + '{{WRAPPER}} .dialog-message' => 'height: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'height', + [ + 'label' => esc_html__( 'Custom Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 100, + 'max' => 1000, + ], + 'vh' => [ + 'min' => 10, + 'max' => 100, + ], + ], + 'condition' => [ + 'height_type' => 'custom', + ], + 'default' => [ + 'size' => 380, + ], + 'selectors' => [ + '{{WRAPPER}} .dialog-message' => 'height: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'content_position', + [ + 'label' => esc_html__( 'Content Position', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'top', + 'options' => [ + 'top' => esc_html__( 'Top', 'elementor-pro' ), + 'center' => esc_html__( 'Center', 'elementor-pro' ), + 'bottom' => esc_html__( 'Bottom', 'elementor-pro' ), + ], + 'condition' => [ + 'height_type!' => 'auto', + ], + 'selectors_dictionary' => [ + 'top' => 'flex-start', + 'bottom' => 'flex-end', + ], + 'selectors' => [ + '{{WRAPPER}} .dialog-message' => 'align-items: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'position_heading', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'horizontal_position', + [ + 'label' => esc_html__( 'Horizontal', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'toggle' => false, + 'default' => 'center', + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-h-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => 'justify-content: {{VALUE}}', + ], + 'selectors_dictionary' => [ + 'left' => 'flex-start', + 'right' => 'flex-end', + ], + ] + ); + + $this->add_responsive_control( + 'vertical_position', + [ + 'label' => esc_html__( 'Vertical', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'toggle' => false, + 'default' => 'center', + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => 'align-items: {{VALUE}}', + ], + 'selectors_dictionary' => [ + 'top' => 'flex-start', + 'bottom' => 'flex-end', + ], + ] + ); + + $this->add_control( + 'overlay', + [ + 'label' => esc_html__( 'Overlay', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'default' => 'yes', + 'selectors' => [ + '{{WRAPPER}}' => 'pointer-events: all', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'close_button', + [ + 'label' => esc_html__( 'Close Button', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'default' => 'yes', + 'selectors' => [ + // Using flex to make sure that each icon type (i or SVG) will be vertically aligned. + '{{WRAPPER}} .dialog-close-button' => 'display: flex', + ], + ] + ); + + $this->add_responsive_control( + 'entrance_animation', + [ + 'label' => esc_html__( 'Entrance Animation', 'elementor-pro' ), + 'type' => Controls_Manager::ANIMATION, + 'frontend_available' => true, + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'exit_animation', + [ + 'label' => esc_html__( 'Exit Animation', 'elementor-pro' ), + 'type' => Controls_Manager::EXIT_ANIMATION, + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'entrance_animation_duration', + [ + 'label' => esc_html__( 'Animation Duration', 'elementor-pro' ) . ' (s)', + 'type' => Controls_Manager::SLIDER, + 'frontend_available' => true, + 'default' => [ + 'size' => 1.2, + ], + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 5, + 'step' => 0.1, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .dialog-widget-content' => 'animation-duration: {{SIZE}}s', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'entrance_animation', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'exit_animation', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ] + ); + + $this->end_controls_section(); + + parent::register_controls(); + + $this->start_controls_section( + 'section_page_style', + [ + 'label' => esc_html__( 'Popup', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'background', + 'selector' => '{{WRAPPER}} .dialog-widget-content', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'border', + 'selector' => '{{WRAPPER}} .dialog-widget-content', + ] + ); + + $this->add_responsive_control( + 'border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .dialog-widget-content' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_shadow', + 'selector' => '{{WRAPPER}} .dialog-widget-content', + 'fields_options' => [ + 'box_shadow_type' => [ + 'default' => 'yes', + ], + 'box_shadow' => [ + 'default' => [ + 'horizontal' => 2, + 'vertical' => 8, + 'blur' => 23, + 'spread' => 3, + 'color' => 'rgba(0,0,0,0.2)', + ], + ], + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_overlay', + [ + 'label' => esc_html__( 'Overlay', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'overlay' => 'yes', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'overlay_background', + 'types' => [ 'classic', 'gradient' ], + 'selector' => '{{WRAPPER}}', + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + 'color' => [ + 'default' => 'rgba(0,0,0,.8)', + ], + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_close_button', + [ + 'label' => esc_html__( 'Close Button', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'close_button!' => '', + ], + ] + ); + + $this->add_control( + 'close_button_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Inside', 'elementor-pro' ), + 'outside' => esc_html__( 'Outside', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( + 'close_button_vertical', + [ + 'label' => esc_html__( 'Vertical Position', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => -500, + 'max' => 500, + ], + 'em' => [ + 'min' => -50, + 'max' => 50, + ], + 'rem' => [ + 'min' => -50, + 'max' => 50, + ], + ], + 'default' => [ + 'unit' => '%', + ], + 'tablet_default' => [ + 'unit' => '%', + ], + 'mobile_default' => [ + 'unit' => '%', + ], + 'selectors' => [ + '{{WRAPPER}} .dialog-close-button' => 'top: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'close_button_horizontal', + [ + 'label' => esc_html__( 'Horizontal Position', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => -500, + 'max' => 500, + ], + 'em' => [ + 'min' => -50, + 'max' => 50, + ], + 'rem' => [ + 'min' => -50, + 'max' => 50, + ], + ], + 'default' => [ + 'unit' => '%', + ], + 'tablet_default' => [ + 'unit' => '%', + ], + 'mobile_default' => [ + 'unit' => '%', + ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .dialog-close-button' => 'right: {{SIZE}}{{UNIT}}', + 'body.rtl {{WRAPPER}} .dialog-close-button' => 'left: {{SIZE}}{{UNIT}}', + ], + 'separator' => 'after', + ] + ); + + $this->start_controls_tabs( 'close_button_style_tabs' ); + + $this->start_controls_tab( + 'tab_x_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'close_button_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .dialog-close-button i' => 'color: {{VALUE}}', + '{{WRAPPER}} .dialog-close-button svg' => 'fill: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'close_button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .dialog-close-button' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_x_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'close_button_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .dialog-close-button:hover i' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'close_button_hover_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .dialog-close-button:hover' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_responsive_control( + 'icon_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .dialog-close-button' => 'font-size: {{SIZE}}{{UNIT}}', + ], + 'separator' => 'before', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_advanced', + [ + 'label' => esc_html__( 'Advanced', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_ADVANCED, + ] + ); + + $this->add_control( + 'close_button_delay', + [ + 'label' => esc_html__( 'Show Close Button After', 'elementor-pro' ) . ' (sec)', + 'type' => Controls_Manager::NUMBER, + 'min' => 0.1, + 'max' => 60, + 'step' => 0.1, + 'condition' => [ + 'close_button' => 'yes', + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'close_automatically', + [ + 'label' => esc_html__( 'Automatically Close After', 'elementor-pro' ) . ' (sec)', + 'type' => Controls_Manager::NUMBER, + 'min' => 0.1, + 'max' => 60, + 'step' => 0.1, + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'prevent_close_on_background_click', + [ + 'label' => esc_html__( 'Prevent Closing on Overlay', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'prevent_close_on_esc_key', + [ + 'label' => esc_html__( 'Prevent Closing on ESC key', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'prevent_scroll', + [ + 'label' => esc_html__( 'Disable Page Scrolling', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'avoid_multiple_popups', + [ + 'label' => esc_html__( 'Avoid Multiple Popups', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'description' => esc_html__( 'If the user has seen another popup on the page hide this popup', 'elementor-pro' ), + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'a11y_navigation', + [ + 'label' => esc_html__( 'Accessible navigation', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'description' => esc_html__( 'Allow keyboard tab navigation for accessibility', 'elementor-pro' ), + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'open_selector', + [ + 'label' => esc_html__( 'Open By Selector', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( '#id, .class', 'elementor-pro' ), + 'description' => esc_html__( 'In order to open a popup on selector click, please set your Popup Conditions', 'elementor-pro' ), + 'frontend_available' => true, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_responsive_control( + 'margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'separator' => 'before', + 'selectors' => [ + '{{WRAPPER}} .dialog-widget-content' => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .dialog-message' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'classes', + [ + 'label' => esc_html__( 'CSS Classes', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor-pro' ), + 'ai' => [ + 'active' => false, + ], + 'frontend_available' => true, + ] + ); + + $this->end_controls_section(); + + Plugin::elementor()->controls_manager->add_custom_css_controls( $this ); + } + + protected function get_remote_library_config() { + $config = parent::get_remote_library_config(); + $config['type'] = 'popup'; + $config['default_route'] = 'templates/popups'; + $config['autoImportSettings'] = true; + + return $config; + } +} diff --git a/modules/popup/form-action.php b/modules/popup/form-action.php new file mode 100644 index 0000000..52a4a11 --- /dev/null +++ b/modules/popup/form-action.php @@ -0,0 +1,143 @@ +start_controls_section( + 'section_popup', + [ + 'label' => esc_html__( 'Popup', 'elementor-pro' ), + 'condition' => [ + 'submit_actions' => $this->get_name(), + ], + ] + ); + + $widget->add_control( + 'popup_action', + [ + 'label' => esc_html__( 'Action', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Choose', 'elementor-pro' ), + 'open' => esc_html__( 'Open Popup', 'elementor-pro' ), + 'close' => esc_html__( 'Close Popup', 'elementor-pro' ), + ], + ] + ); + + $widget->add_control( + 'popup_action_popup_id', + [ + 'label' => esc_html__( 'Popup', 'elementor-pro' ), + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_LIBRARY_TEMPLATE, + 'query' => [ + 'posts_per_page' => 20, + 'meta_query' => [ + [ + 'key' => Document::TYPE_META_KEY, + 'value' => 'popup', + ], + ], + ], + ], + 'condition' => [ + 'popup_action' => 'open', + ], + ] + ); + + $widget->add_control( + 'popup_action_do_not_show_again', + [ + 'label' => esc_html__( 'Don\'t Show Again', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'popup_action' => 'close', + ], + ] + ); + + $widget->end_controls_section(); + } + + public function on_export( $element ) { + unset( + $element['settings']['popup_action'], + $element['settings']['popup_action_popup_id'], + $element['settings']['popup_action_do_not_show_again'] + ); + + return $element; + } + + public function run( $record, $ajax_handler ) { + $popup_action = $record->get_form_settings( 'popup_action' ); + + if ( empty( $popup_action ) ) { + return; + } + + $action_settings = [ + 'action' => $popup_action, + ]; + + if ( 'open' === $popup_action ) { + $popup_id = $record->get_form_settings( 'popup_action_popup_id' ); + + if ( empty( $popup_id ) ) { + return; + } + + $action_settings['id'] = $popup_id; + } else { + $action_settings['do_not_show_again'] = $record->get_form_settings( 'popup_action_do_not_show_again' ); + } + + $ajax_handler->add_response_data( 'popup', $action_settings ); + } + + public function maybe_print_popup( $settings, $widget ) { + if ( ! is_array( $settings['submit_actions'] ) || ! in_array( 'popup', $settings['submit_actions'] ) ) { + return; + } + + $has_valid_settings = ( ! empty( $settings['popup_action'] ) && 'open' === $settings['popup_action'] && ! empty( $settings['popup_action_popup_id'] ) ); + if ( ! $has_valid_settings ) { + return; + } + + Module::add_popup_to_location( $settings['popup_action_popup_id'] ); + } + + public function __construct() { + /** @var FormsModule $forms_module */ + $forms_module = FormsModule::instance(); + + // Register popup form action + $forms_module->actions_registrar->register( $this ); + + add_action( 'elementor-pro/forms/pre_render', [ $this, 'maybe_print_popup' ], 10, 2 ); + } +} diff --git a/modules/popup/module.php b/modules/popup/module.php new file mode 100644 index 0000000..07371d1 --- /dev/null +++ b/modules/popup/module.php @@ -0,0 +1,273 @@ +can_use_popups() ) { + add_action( 'elementor/documents/register', [ $this, 'register_documents' ] ); + add_action( 'elementor/theme/register_locations', [ $this, 'register_location' ] ); + add_action( 'elementor/dynamic_tags/register', [ $this, 'register_tag' ] ); + add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); + + add_action( 'wp_footer', [ $this, 'print_popups' ] ); + add_action( 'elementor_pro/init', [ $this, 'add_form_action' ] ); + } else { + add_action( 'load-post.php', [ $this, 'disable_editing' ] ); + add_action( 'admin_init', [ $this, 'maybe_redirect_to_promotion_page' ] ); + } + + if ( Plugin::elementor()->experiments->is_feature_active( 'admin_menu_rearrangement' ) ) { + add_action( 'elementor/admin/menu_registered/elementor', function( MainMenu $menu ) { + $this->register_admin_menu( $menu ); + } ); + } else { + add_action( 'elementor/admin/menu/register', function( Admin_Menu_Manager $admin_menu_manager ) { + if ( $this->can_use_popups() ) { + $admin_menu_manager->register( $this->get_admin_url( true ), new Popups_Menu_Item() ); + } else { + $admin_menu_manager->register( static::PROMOTION_MENU_SLUG, new Popups_Promotion_Menu_Item() ); + } + } ); + + // TODO: BC - Remove in the future. + add_action( 'admin_menu', function() { + $this->register_admin_menu_legacy(); + }, 21 /* After `Admin_Menu_Manager` */ ); + } + + add_filter( 'elementor/finder/categories', [ $this, 'add_finder_items' ] ); + } + + public function disable_editing() { + $post_id = Utils::_unstable_get_super_global_value( $_GET, 'post' ); + + if ( ! $post_id ) { + return; + } + + $document = Plugin::elementor()->documents->get( $post_id ); + + if ( ! $document ) { + return; + } + + $template_type = $document->get_main_meta( DocumentBase::TYPE_META_KEY ); + + if ( static::DOCUMENT_TYPE === $template_type ) { + $error = new \WP_Error( 'e_popups_editing_disabled', esc_html__( 'Invalid post type.', 'elementor-pro' ) ); + wp_die( $error ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + } + + public function maybe_redirect_to_promotion_page() { + if ( $this->is_on_popups_admin_page() ) { + wp_redirect( $this->get_promotion_url() ); + exit(); + } + } + + private function is_on_popups_admin_page() { + global $pagenow; + + return isset( $pagenow ) && + 'edit.php' === $pagenow && + Source_Local::CPT === Utils::_unstable_get_super_global_value( $_GET, 'post_type' ) && + static::DOCUMENT_TYPE === Utils::_unstable_get_super_global_value( $_GET, Source_Local::TAXONOMY_TYPE_SLUG ); + } + + private function get_promotion_url() { + return add_query_arg( + [ + 'page' => static::PROMOTION_MENU_SLUG, + ], + Source_Local::ADMIN_MENU_SLUG + ); + } + + public function get_name() { + return 'popup'; + } + + public function add_form_action() { + $this->add_component( 'form-action', new Form_Action() ); + } + + public static function add_popup_to_location( $popup_id ) { + /** @var \ElementorPro\Modules\ThemeBuilder\Module $theme_builder */ + $theme_builder = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); + + $theme_builder->get_locations_manager()->add_doc_to_location( Document::get_property( 'location' ), $popup_id ); + } + + public function register_documents( Documents_Manager $documents_manager ) { + $documents_manager->register_document_type( self::DOCUMENT_TYPE, Document::get_class_full_name() ); + } + + public function register_location( Locations_Manager $location_manager ) { + $location_manager->register_location( + 'popup', + [ + 'label' => esc_html__( 'Popup', 'elementor-pro' ), + 'multiple' => true, + 'public' => false, + 'edit_in_content' => false, + ] + ); + } + + public function print_popups() { + elementor_theme_do_location( 'popup' ); + } + + public function register_tag( DynamicTagsManager $dynamic_tags ) { + $tag = __NAMESPACE__ . '\Tag'; + + $dynamic_tags->register( new $tag() ); + } + + public function register_ajax_actions( Ajax $ajax ) { + $ajax->register_ajax_action( 'pro_popup_save_display_settings', [ $this, 'save_display_settings' ] ); + } + + /** + * @deprecated 3.1.0 + */ + public function localize_settings() { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); + + return []; + } + + /** + * @throws \Exception + */ + public function save_display_settings( $data ) { + $document = Utils::_unstable_get_document_for_edit( $data['editor_post_id'] ); + + /** @var Document $document */ + $document->save_display_settings_data( $data['settings'] ); + } + + /** + * Add New item to admin menu. + * + * @since 3.6.0 + * @access private + */ + private function register_admin_menu( MainMenu $menu ) { + $menu->add_submenu( [ + 'menu_title' => esc_html__( 'Popups', 'elementor-pro' ), + 'menu_slug' => $this->get_admin_url( true ), + 'index' => 10, + ] ); + } + + /** + * Add New item to admin menu. + * + * Fired by `admin_menu` action. + * + * @since 3.6.0 + * @access private + */ + private function register_admin_menu_legacy() { + if ( did_action( 'elementor/admin/menu/register' ) ) { + return; + } + + add_submenu_page( + Source_Local::ADMIN_MENU_SLUG, + '', + esc_html__( 'Popups', 'elementor-pro' ), + 'publish_posts', + $this->get_admin_url( true ) + ); + } + + public function add_finder_items( array $categories ) { + $categories['general']['items']['popups'] = [ + 'title' => esc_html__( 'Popups', 'elementor-pro' ), + 'icon' => 'library-save', + 'url' => $this->get_admin_url(), + 'keywords' => [ 'template', 'popup', 'library' ], + ]; + + if ( ! $this->can_use_popups() ) { + $lock = new Feature_Lock( [ 'type' => 'popup' ] ); + + $categories['general']['items']['popups']['lock'] = $lock->get_config(); + } + + return $categories; + } + + private function get_admin_url( $relative = false ) { + $base_url = Source_Local::ADMIN_MENU_SLUG; + if ( ! $relative ) { + $base_url = admin_url( $base_url ); + } + + return add_query_arg( + [ + 'tabs_group' => 'popup', + 'elementor_library_type' => 'popup', + ], + $base_url + ); + } + + private function can_use_popups() { + return ( API::is_license_active() && API::is_licence_has_feature( static::DOCUMENT_TYPE, API::BC_VALIDATION_CALLBACK ) ) || $this->has_popups(); + } + + private function has_popups() { + if ( null !== $this->has_popups ) { + return $this->has_popups; + } + + $existing_popups = new \WP_Query( [ + 'post_type' => Source_Local::CPT, + 'posts_per_page' => 1, + 'post_status' => 'any', + 'meta_query' => [ + [ + 'key' => DocumentBase::TYPE_META_KEY, + 'value' => Document::get_type(), + ], + ], + 'meta_key' => DocumentBase::TYPE_META_KEY, + ] ); + + $this->has_popups = $existing_popups->post_count > 0; + + return $this->has_popups; + } +} diff --git a/modules/popup/tag.php b/modules/popup/tag.php new file mode 100644 index 0000000..b767c71 --- /dev/null +++ b/modules/popup/tag.php @@ -0,0 +1,134 @@ +add_control( + 'action', + [ + 'label' => esc_html__( 'Action', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'open', + 'options' => [ + 'open' => esc_html__( 'Open Popup', 'elementor-pro' ), + 'close' => esc_html__( 'Close Popup', 'elementor-pro' ), + 'toggle' => esc_html__( 'Toggle Popup', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'popup', + [ + 'label' => esc_html__( 'Popup', 'elementor-pro' ), + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_LIBRARY_TEMPLATE, + 'query' => [ + 'posts_per_page' => 20, + 'post_status' => [ 'publish', 'private' ], + 'meta_query' => [ + [ + 'key' => Document::TYPE_META_KEY, + 'value' => 'popup', + ], + ], + ], + ], + 'label_block' => true, + 'condition' => [ + 'action' => [ 'open', 'toggle' ], + ], + ] + ); + + $this->add_control( + 'do_not_show_again', + [ + 'label' => esc_html__( 'Don\'t Show Again', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'action' => 'close', + ], + ] + ); + } + + public function render() { + $settings = $this->get_active_settings(); + + if ( 'close' === $settings['action'] ) { + $this->print_close_popup_link( $settings ); + + return; + } + + $this->print_open_popup_link( $settings ); + } + + // Keep Empty to avoid default advanced section + protected function register_advanced_section() {} + + private function print_open_popup_link( array $settings ) { + if ( ! $settings['popup'] ) { + return; + } + + $link_action_url = Plugin::elementor()->frontend->create_action_hash( 'popup:open', [ + 'id' => $settings['popup'], + 'toggle' => 'toggle' === $settings['action'], + ] ); + + Module::add_popup_to_location( $settings['popup'] ); + + // PHPCS - `create_action_hash` is safe. + echo $link_action_url; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + private function print_close_popup_link( array $settings ) { + // PHPCS - `create_action_hash` is safe. + echo Plugin::elementor()->frontend->create_action_hash( 'popup:close', [ 'do_not_show_again' => $settings['do_not_show_again'] ] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } +} diff --git a/modules/posts/data/controller.php b/modules/posts/data/controller.php new file mode 100644 index 0000000..89bfb1f --- /dev/null +++ b/modules/posts/data/controller.php @@ -0,0 +1,61 @@ +documents->get( $request->get_param( 'post_id' ) ); + + if ( ! $document ) { + return new \WP_Error( + 'document_not_exist', + __( 'Document doesn\'t exist', 'elementor-pro' ), + [ 'status' => 404 ] + ); + } + + $element_data = $document->get_elements_data(); + $posts_widget = Utils::find_element_recursive( $element_data, $request->get_param( 'element_id' ) ); + + if ( empty( $posts_widget ) ) { + return new \WP_Error( + 'Element_not_exist', + __( 'Posts widget doesn\'t exist', 'elementor-pro' ), + [ 'status' => 404 ] + ); + } + + set_query_var( 'paged', $request->get_param( 'page' ) ); + + /** @var \ElementorPro\Modules\Posts\Widgets\Posts $element_instance */ + $element_instance = Plugin::elementor()->elements_manager->create_element_instance( $posts_widget ); + + ob_start(); + $element_instance->render_content(); + $html = ob_get_clean(); + + return [ + 'content' => $html, + ]; + } + + public function get_permission_callback( $request ) { + return true; + } +} diff --git a/modules/posts/module.php b/modules/posts/module.php new file mode 100644 index 0000000..9a4f737 --- /dev/null +++ b/modules/posts/module.php @@ -0,0 +1,63 @@ +query_vars['page'] ) || ! is_singular() || empty( $wp_query->post ) ) { + return $handled; + } + + $document = Plugin::elementor()->documents->get( $wp_query->post->ID ); + + return $this->is_valid_pagination( $document->get_elements_data(), $wp_query->query_vars['page'] ); + } + + public function __construct() { + parent::__construct(); + + Plugin::elementor()->data_manager->register_controller( Controller::class ); + + add_filter( 'pre_handle_404', [ $this, 'allow_posts_widget_pagination' ], 10, 2 ); + } + +} diff --git a/modules/posts/skins/skin-base.php b/modules/posts/skins/skin-base.php new file mode 100644 index 0000000..45bd3b3 --- /dev/null +++ b/modules/posts/skins/skin-base.php @@ -0,0 +1,1409 @@ +parent = $widget; + + $this->register_design_controls(); + } + + public function register_controls( Widget_Base $widget ) { + $this->parent = $widget; + + $this->register_columns_controls(); + $this->register_post_count_control(); + $this->register_thumbnail_controls(); + $this->register_title_controls(); + $this->register_excerpt_controls(); + $this->register_meta_data_controls(); + $this->register_read_more_controls(); + $this->register_link_controls(); + } + + public function register_design_controls() { + $this->register_design_layout_controls(); + $this->register_design_image_controls(); + $this->register_design_content_controls(); + } + + protected function register_thumbnail_controls() { + $this->add_control( + 'thumbnail', + [ + 'label' => esc_html__( 'Image Position', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'top', + 'options' => [ + 'top' => esc_html__( 'Top', 'elementor-pro' ), + 'left' => esc_html__( 'Left', 'elementor-pro' ), + 'right' => esc_html__( 'Right', 'elementor-pro' ), + 'none' => esc_html__( 'None', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-posts--thumbnail-', + ] + ); + + $this->add_control( + 'masonry', + [ + 'label' => esc_html__( 'Masonry', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'condition' => [ + $this->get_control_id( 'columns!' ) => '1', + $this->get_control_id( 'thumbnail' ) => 'top', + ], + 'render_type' => 'ui', + 'frontend_available' => true, + ] + ); + + $this->add_group_control( + Group_Control_Image_Size::get_type(), + [ + 'name' => 'thumbnail_size', + 'default' => 'medium', + 'exclude' => [ 'custom' ], + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => 'none', + ], + 'prefix_class' => 'elementor-posts--thumbnail-size-', + ] + ); + + $this->add_responsive_control( + 'item_ratio', + [ + 'label' => esc_html__( 'Image Ratio', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 0.66, + ], + 'tablet_default' => [ + 'size' => '', + ], + 'mobile_default' => [ + 'size' => 0.5, + ], + 'range' => [ + 'px' => [ + 'min' => 0.1, + 'max' => 2, + 'step' => 0.01, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-posts-container .elementor-post__thumbnail' => 'padding-bottom: calc( {{SIZE}} * 100% );', + '{{WRAPPER}}:after' => 'content: "{{SIZE}}";', + ], + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => 'none', + $this->get_control_id( 'masonry' ) => '', + ], + ] + ); + + $this->add_responsive_control( + 'image_width', + [ + 'label' => esc_html__( 'Image Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + '%' => [ + 'min' => 10, + 'max' => 100, + ], + 'px' => [ + 'min' => 10, + 'max' => 600, + ], + 'em' => [ + 'min' => 1, + 'max' => 60, + ], + 'rem' => [ + 'min' => 1, + 'max' => 60, + ], + ], + 'default' => [ + 'size' => 100, + 'unit' => '%', + ], + 'tablet_default' => [ + 'size' => '', + 'unit' => '%', + ], + 'mobile_default' => [ + 'size' => 100, + 'unit' => '%', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__thumbnail__link' => 'width: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => 'none', + ], + ] + ); + } + + protected function register_columns_controls() { + $this->add_responsive_control( + 'columns', + [ + 'label' => esc_html__( 'Columns', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '3', + 'tablet_default' => '2', + 'mobile_default' => '1', + 'options' => [ + '1' => '1', + '2' => '2', + '3' => '3', + '4' => '4', + '5' => '5', + '6' => '6', + ], + 'prefix_class' => 'elementor-grid%s-', + 'frontend_available' => true, + ] + ); + } + + protected function register_post_count_control() { + $this->add_control( + 'posts_per_page', + [ + 'label' => esc_html__( 'Posts Per Page', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 6, + ] + ); + } + + protected function register_title_controls() { + $this->add_control( + 'show_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'title_tag', + [ + 'label' => esc_html__( 'Title HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + 'p' => 'p', + ], + 'default' => 'h3', + 'condition' => [ + $this->get_control_id( 'show_title' ) => 'yes', + ], + ] + ); + + } + + protected function register_excerpt_controls() { + $this->add_control( + 'show_excerpt', + [ + 'label' => esc_html__( 'Excerpt', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'excerpt_length', + [ + 'label' => esc_html__( 'Excerpt Length', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + /** This filter is documented in wp-includes/formatting.php */ + 'default' => apply_filters( 'excerpt_length', 25 ), + 'condition' => [ + $this->get_control_id( 'show_excerpt' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'apply_to_custom_excerpt', + [ + 'label' => esc_html__( 'Apply to custom Excerpt', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'default' => 'no', + 'condition' => [ + $this->get_control_id( 'show_excerpt' ) => 'yes', + ], + ] + ); + } + + protected function register_read_more_controls() { + $this->add_control( + 'show_read_more', + [ + 'label' => esc_html__( 'Read More', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'read_more_text', + [ + 'label' => esc_html__( 'Read More Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Read More »', 'elementor-pro' ), + 'condition' => [ + $this->get_control_id( 'show_read_more' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'read_more_alignment', + [ + 'label' => esc_html__( 'Automatically align buttons', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'default' => '', + 'render_type' => 'template', + 'selectors' => [ + // --item-display is used for the styling of both elementor-post__card and elementor-post__text + '{{WRAPPER}}' => '--item-display: flex; --read-more-alignment: 1;', + ], + 'condition' => [ + $this->get_control_id( 'masonry!' ) => 'yes', + $this->get_control_id( 'show_read_more' ) => 'yes', + ], + ] + ); + } + + protected function register_link_controls() { + $this->add_control( + 'open_new_tab', + [ + 'label' => esc_html__( 'Open in new window', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'default' => 'no', + 'render_type' => 'none', + ] + ); + } + + protected function get_optional_link_attributes_html() { + $settings = $this->parent->get_settings(); + $new_tab_setting_key = $this->get_control_id( 'open_new_tab' ); + $optional_attributes_html = 'yes' === $settings[ $new_tab_setting_key ] ? 'target="_blank"' : ''; + + return $optional_attributes_html; + } + + protected function register_meta_data_controls() { + $this->add_control( + 'meta_data', + [ + 'label' => esc_html__( 'Meta Data', 'elementor-pro' ), + 'label_block' => true, + 'type' => Controls_Manager::SELECT2, + 'default' => [ 'date', 'comments' ], + 'multiple' => true, + 'options' => [ + 'author' => esc_html__( 'Author', 'elementor-pro' ), + 'date' => esc_html__( 'Date', 'elementor-pro' ), + 'time' => esc_html__( 'Time', 'elementor-pro' ), + 'comments' => esc_html__( 'Comments', 'elementor-pro' ), + 'modified' => esc_html__( 'Date Modified', 'elementor-pro' ), + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'meta_separator', + [ + 'label' => esc_html__( 'Separator Between', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '///', + 'ai' => [ + 'active' => false, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__meta-data span + span:before' => 'content: "{{VALUE}}"', + ], + 'condition' => [ + $this->get_control_id( 'meta_data!' ) => [], + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + } + + /** + * Style Tab + */ + protected function register_design_layout_controls() { + $this->start_controls_section( + 'section_design_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'column_gap', + [ + 'label' => esc_html__( 'Columns Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 30, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--grid-column-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'row_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 35, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'frontend_available' => true, + 'selectors' => [ + '{{WRAPPER}}' => '--grid-row-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'prefix_class' => 'elementor-posts--align-', + ] + ); + + $this->end_controls_section(); + } + + protected function register_design_image_controls() { + $this->start_controls_section( + 'section_design_image', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'img_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__thumbnail' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'image_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-posts--thumbnail-left .elementor-post__thumbnail__link' => 'margin-right: {{SIZE}}{{UNIT}}', + '{{WRAPPER}}.elementor-posts--thumbnail-right .elementor-post__thumbnail__link' => 'margin-left: {{SIZE}}{{UNIT}}', + '{{WRAPPER}}.elementor-posts--thumbnail-top .elementor-post__thumbnail__link' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + 'default' => [ + 'size' => 20, + ], + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => 'none', + ], + ] + ); + + $this->start_controls_tabs( 'thumbnail_effects_tabs' ); + + $this->start_controls_tab( 'normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Css_Filter::get_type(), + [ + 'name' => 'thumbnail_filters', + 'selector' => '{{WRAPPER}} .elementor-post__thumbnail img', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Css_Filter::get_type(), + [ + 'name' => 'thumbnail_hover_filters', + 'selector' => '{{WRAPPER}} .elementor-post:hover .elementor-post__thumbnail img', + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + } + + protected function register_design_content_controls() { + $this->start_controls_section( + 'section_design_content', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'heading_title_style', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'condition' => [ + $this->get_control_id( 'show_title' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__title, {{WRAPPER}} .elementor-post__title a' => 'color: {{VALUE}};', + ], + 'condition' => [ + $this->get_control_id( 'show_title' ) => 'yes', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'title_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-post__title, {{WRAPPER}} .elementor-post__title a', + 'condition' => [ + $this->get_control_id( 'show_title' ) => 'yes', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Stroke::get_type(), + [ + 'name' => 'text_stroke', + 'selector' => '{{WRAPPER}} .elementor-post__title', + ] + ); + + $this->add_responsive_control( + 'title_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__title' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + $this->get_control_id( 'show_title' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'heading_meta_style', + [ + 'label' => esc_html__( 'Meta', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + $this->get_control_id( 'meta_data!' ) => [], + ], + ] + ); + + $this->add_control( + 'meta_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-post__meta-data' => 'color: {{VALUE}};', + ], + 'condition' => [ + $this->get_control_id( 'meta_data!' ) => [], + ], + ] + ); + + $this->add_control( + 'meta_separator_color', + [ + 'label' => esc_html__( 'Separator Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-post__meta-data span:before' => 'color: {{VALUE}};', + ], + 'condition' => [ + $this->get_control_id( 'meta_data!' ) => [], + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'meta_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + 'selector' => '{{WRAPPER}} .elementor-post__meta-data', + 'condition' => [ + $this->get_control_id( 'meta_data!' ) => [], + ], + ] + ); + + $this->add_responsive_control( + 'meta_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__meta-data' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + $this->get_control_id( 'meta_data!' ) => [], + ], + ] + ); + + $this->add_control( + 'heading_excerpt_style', + [ + 'label' => esc_html__( 'Excerpt', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + $this->get_control_id( 'show_excerpt' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'excerpt_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-post__excerpt p' => 'color: {{VALUE}};', + ], + 'condition' => [ + $this->get_control_id( 'show_excerpt' ) => 'yes', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'excerpt_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-post__excerpt p', + 'condition' => [ + $this->get_control_id( 'show_excerpt' ) => 'yes', + ], + ] + ); + + $this->add_responsive_control( + 'excerpt_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__excerpt' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + $this->get_control_id( 'show_excerpt' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'heading_readmore_style', + [ + 'label' => esc_html__( 'Read More', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + $this->get_control_id( 'show_read_more' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'read_more_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__read-more' => 'color: {{VALUE}};', + ], + 'condition' => [ + $this->get_control_id( 'show_read_more' ) => 'yes', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'read_more_typography', + // The 'a' selector is added for specificity, for when this control's selector is used in globals CSS. + 'selector' => '{{WRAPPER}} a.elementor-post__read-more', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'condition' => [ + $this->get_control_id( 'show_read_more' ) => 'yes', + ], + ] + ); + + $this->add_responsive_control( + 'read_more_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__text' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + $this->get_control_id( 'show_read_more' ) => 'yes', + ], + ] + ); + + $this->end_controls_section(); + } + + public function render() { + $this->parent->query_posts(); + + /** @var \WP_Query $query */ + $query = $this->parent->get_query(); + + if ( ! $query->found_posts ) { + $this->handle_no_posts_found(); + return; + } + + $this->render_loop_header(); + + // It's the global `wp_query` it self. and the loop was started from the theme. + if ( $query->in_the_loop ) { + $this->current_permalink = get_permalink(); + $this->render_post(); + } else { + while ( $query->have_posts() ) { + $query->the_post(); + + $this->current_permalink = get_permalink(); + $this->render_post(); + } + } + + wp_reset_postdata(); + + $this->render_loop_footer(); + } + + public function filter_excerpt_length() { + return $this->get_instance_value( 'excerpt_length' ); + } + + public function filter_excerpt_more( $more ) { + return ''; + } + + public function get_container_class() { + return 'elementor-posts--skin-' . $this->get_id(); + } + + protected function render_thumbnail() { + $thumbnail = $this->get_instance_value( 'thumbnail' ); + + if ( 'none' === $thumbnail && ! Plugin::elementor()->editor->is_edit_mode() ) { + return; + } + + $settings = $this->parent->get_settings(); + $setting_key = $this->get_control_id( 'thumbnail_size' ); + $settings[ $setting_key ] = [ + 'id' => get_post_thumbnail_id(), + ]; + $thumbnail_html = Group_Control_Image_Size::get_attachment_image_html( $settings, $setting_key ); + + if ( empty( $thumbnail_html ) ) { + return; + } + + $optional_attributes_html = $this->get_optional_link_attributes_html(); + + ?> + > +
    +
    + get_instance_value( 'show_title' ) ) { + return; + } + + $optional_attributes_html = $this->get_optional_link_attributes_html(); + + $tag = $this->get_instance_value( 'title_tag' ); + ?> + < class="elementor-post__title"> + > + + + > + get_instance_value( 'show_excerpt' ) ) { + return; + } + + add_filter( 'excerpt_more', [ $this, 'filter_excerpt_more' ], 20 ); + add_filter( 'excerpt_length', [ $this, 'filter_excerpt_length' ], 20 ); + + ?> +
    + get_instance_value( 'apply_to_custom_excerpt' ); + + // Force the manually-generated Excerpt length as well if the user chose to enable 'apply_to_custom_excerpt'. + if ( 'yes' === $apply_to_custom_excerpt && ! empty( $post->post_excerpt ) ) { + $max_length = (int) $this->get_instance_value( 'excerpt_length' ); + $excerpt = apply_filters( 'the_excerpt', get_the_excerpt() ); + $excerpt = ProUtils::trim_words( $excerpt, $max_length ); + echo wp_kses_post( $excerpt ); + } else { + the_excerpt(); + } + ?> +
    + parent->get_settings_for_display(); + $read_more_key = $this->get_control_id( 'read_more_text' ); + $read_more = $settings[ $read_more_key ]; + + if ( ! $this->get_instance_value( 'show_read_more' ) ) { + return; + } + + $aria_label_text = sprintf( + /* translators: %s: Post title. */ + esc_attr__( 'Read more about %s', 'elementor-pro' ), + get_the_title() + ); + + $optional_attributes_html = $this->get_optional_link_attributes_html(); + + if ( $this->display_read_more_bottom() ) : ?> +
    + + + > + + + + display_read_more_bottom() ) : ?> +
    + +
    > + +
    + +
    + +
    + get_container_class(), + ]; + } + + protected function handle_no_posts_found() {} + + protected function render_loop_header() { + $classes = $this->get_loop_header_widget_classes(); + + /** @var \WP_Query $e_wp_query */ + $e_wp_query = $this->parent->get_query(); + + // Use grid only if found posts. + if ( isset( $e_wp_query->found_posts ) || Taxonomy_Loop_Provider::is_loop_taxonomy() ) { + $classes[] = 'elementor-grid'; + } + + $render_attributes = apply_filters( 'elementor/skin/loop_header_attributes', [ + 'class' => $classes, + ] ); + + $this->parent->add_render_attribute( 'container', $render_attributes ); + + ?> +
    parent->print_render_attribute_string( 'container' ); ?>> + parent->get_settings_for_display(); + ?> +
    + +
    + parent->get_settings_for_display(); + + // If the skin has no pagination, there's nothing to render in the loop footer. + if ( ! isset( $parent_settings['pagination_type'] ) ) { + return; + } + + $using_ajax_pagination = in_array( $parent_settings['pagination_type'], [ + Posts_Base::LOAD_MORE_ON_CLICK, + Posts_Base::LOAD_MORE_INFINITE_SCROLL, + ], true); + + if ( $using_ajax_pagination && ! empty( $parent_settings['load_more_spinner']['value'] ) ) : ?> + + 'true' ] ); ?> + + + + parent->get_query()->max_num_pages; + + // Page limit control should not effect in load more mode. + if ( '' !== $parent_settings['pagination_page_limit'] && ! $using_ajax_pagination ) { + $page_limit = min( $parent_settings['pagination_page_limit'], $page_limit ); + } + + if ( 2 > $page_limit ) { + return; + } + + $this->parent->add_render_attribute( 'pagination', 'class', 'elementor-pagination' ); + + $has_numbers = in_array( $parent_settings['pagination_type'], [ 'numbers', 'numbers_and_prev_next' ] ); + $has_prev_next = in_array( $parent_settings['pagination_type'], [ 'prev_next', 'numbers_and_prev_next' ] ); + + $load_more_type = $parent_settings['pagination_type']; + + $current_page = $this->parent->get_current_page(); + $next_page = intval( $current_page ) + 1; + + $this->parent->add_render_attribute( 'load_more_anchor', [ + 'data-page' => $current_page, + 'data-max-page' => $this->parent->get_query()->max_num_pages, + 'data-next-page' => $this->parent->get_wp_link_page( $next_page ), + ] ); + + ?> +
    parent->print_render_attribute_string( 'load_more_anchor' ); ?>>
    + parent->set_settings( 'link', [ 'url' => '#' ] ); + + $this->render_button( $this->parent ); + } + + $this->render_message(); + return; + } + + $links = []; + + if ( $has_numbers ) { + $paginate_args = [ + 'type' => 'array', + 'current' => $this->parent->get_current_page(), + 'total' => $page_limit, + 'prev_next' => false, + 'show_all' => 'yes' !== $parent_settings['pagination_numbers_shorten'], + 'before_page_number' => '' . esc_html__( 'Page', 'elementor-pro' ) . '', + ]; + + if ( is_singular() && ! is_front_page() && ! $this->parent->is_rest_request() ) { + $paginate_args = $this->get_paginate_args_for_singular_post( $paginate_args ); + } + + if ( is_archive() && $this->parent->current_url_contains_taxonomy_filter() ) { + $paginate_args = $this->get_paginate_args_for_archive_with_filters( $paginate_args ); + } + + if ( $this->parent->is_rest_request() ) { + $paginate_args = $this->get_paginate_args_for_rest_request( $paginate_args ); + } + + if ( $this->parent->is_allow_to_use_custom_page_option() ) { + $paginate_args['format'] = $this->get_pagination_format( $paginate_args ); + } + + $links = paginate_links( $paginate_args ); + } + + if ( $has_prev_next ) { + $prev_next = $this->parent->get_posts_nav_link( $page_limit ); + array_unshift( $links, $prev_next['prev'] ); + $links[] = $prev_next['next']; + } + + // PHPCS - Seems that `$links` is safe. + ?> + + parent->get_id() . '=%#%'; + } + + protected function get_paginate_args_for_singular_post( $paginate_args ) { + global $wp_rewrite; + + if ( $wp_rewrite->using_permalinks() ) { + $paginate_args['base'] = trailingslashit( get_permalink() ) . '%_%'; + $paginate_args['format'] = user_trailingslashit( '%#%', 'single_paged' ); + } else { + $paginate_args['format'] = '?page=%#%'; + } + + return $paginate_args; + } + + protected function get_paginate_args_for_archive_with_filters( $paginate_args ) { + global $wp_rewrite; + + if ( ! $wp_rewrite->using_permalinks() ) { + $paginate_args['format'] = '?page=%#%'; + } + + return $paginate_args; + } + + protected function get_paginate_args_for_rest_request( $paginate_args ) { + global $wp_rewrite; + + $link_unescaped = wp_get_referer(); + $url_components = wp_parse_url( $link_unescaped ); + $add_args = []; + + if ( isset( $url_components['query'] ) ) { + wp_parse_str( $url_components['query'], $add_args ); + } + + $url_to_post_id = url_to_postid( $link_unescaped ); + $pagination_base_url = 0 !== $url_to_post_id + ? get_permalink( $url_to_post_id ) + : get_query_var( 'pagination_base_url' ); + + if ( $wp_rewrite->using_permalinks() ) { + $paginate_args['base'] = trailingslashit( $pagination_base_url ) . '%_%'; + $paginate_args['format'] = user_trailingslashit( '%#%', 'single_paged' ); + $paginate_args['add_args'] = $add_args; + + if ( 0 === $url_to_post_id ) { + unset( $paginate_args['format'] ); + } + } else { + $base = $this->parent->is_allow_to_use_custom_page_option() ? $pagination_base_url . '&%_%' : trailingslashit( $pagination_base_url ) . '%_%'; + $paginate_args['base'] = $base; + $paginate_args['format'] = '&page=%#%'; + $paginate_args['add_args'] = $add_args; + } + + return $paginate_args; + } + + protected function render_meta_data() { + /** @var array $settings e.g. [ 'author', 'date', ... ] */ + $settings = $this->get_instance_value( 'meta_data' ); + if ( empty( $settings ) ) { + return; + } + ?> + + + + + + + + + + parent->get_settings(); + + if ( 'full_content' === $settings['_skin'] ) { + return false; + } + + return 'yes' === $settings[ $this->get_control_id( 'read_more_alignment' ) ] && + 'yes' === $settings[ $this->get_control_id( 'show_read_more' ) ] && + 'yes' !== $settings[ $this->get_control_id( 'masonry' ) ]; + } + + protected function render_comments() { + ?> + + + + render_post_header(); + $this->render_thumbnail(); + $this->render_text_header(); + $this->render_title(); + $this->render_meta_data(); + $this->render_excerpt(); + $this->render_read_more(); + $this->render_text_footer(); + $this->render_post_footer(); + } +} diff --git a/modules/posts/skins/skin-cards.php b/modules/posts/skins/skin-cards.php new file mode 100644 index 0000000..ad4d088 --- /dev/null +++ b/modules/posts/skins/skin-cards.php @@ -0,0 +1,649 @@ +get_id(); + $this->parent->start_controls_tab( $this->get_control_id( $id ), $args ); + } + + public function end_controls_tab() { + $this->parent->end_controls_tab(); + } + + public function start_controls_tabs( $id ) { + $args['condition']['_skin'] = $this->get_id(); + $this->parent->start_controls_tabs( $this->get_control_id( $id ) ); + } + + public function end_controls_tabs() { + $this->parent->end_controls_tabs(); + } + + public function register_controls( Widget_Base $widget ) { + $this->parent = $widget; + + $this->register_columns_controls(); + $this->register_post_count_control(); + $this->register_thumbnail_controls(); + $this->register_title_controls(); + $this->register_excerpt_controls(); + $this->register_meta_data_controls(); + $this->register_read_more_controls(); + $this->register_link_controls(); + $this->register_badge_controls(); + $this->register_avatar_controls(); + } + + public function register_design_controls() { + $this->register_design_layout_controls(); + $this->register_design_card_controls(); + $this->register_design_image_controls(); + $this->register_design_content_controls(); + } + + protected function register_thumbnail_controls() { + parent::register_thumbnail_controls(); + $this->remove_responsive_control( 'image_width' ); + $this->update_control( + 'thumbnail', + [ + 'label' => esc_html__( 'Show Image', 'elementor-pro' ), + 'options' => [ + 'top' => esc_html__( 'Yes', 'elementor-pro' ), + 'none' => esc_html__( 'No', 'elementor-pro' ), + ], + 'render_type' => 'template', + ] + ); + } + + protected function register_meta_data_controls() { + parent::register_meta_data_controls(); + $this->update_control( + 'meta_separator', + [ + 'default' => '•', + ] + ); + } + + public function register_additional_design_image_controls() { + $this->update_control( + 'image_spacing', + [ + 'selectors' => [ + '{{WRAPPER}} .elementor-post__text' => 'margin-top: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => 'none', + ], + ] + ); + + $this->remove_control( 'img_border_radius' ); + + $this->add_control( + 'heading_badge_style', + [ + 'label' => esc_html__( 'Badge', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + $this->get_control_id( 'show_badge' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'badge_position', + [ + 'label' => 'Badge Position', + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'default' => 'right', + 'selectors' => [ + '{{WRAPPER}} .elementor-post__badge' => '{{VALUE}}: 0', + ], + 'condition' => [ + $this->get_control_id( 'show_badge' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'badge_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-post__card .elementor-post__badge' => 'background-color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'condition' => [ + $this->get_control_id( 'show_badge' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'badge_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-post__card .elementor-post__badge' => 'color: {{VALUE}};', + ], + 'condition' => [ + $this->get_control_id( 'show_badge' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'badge_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__card .elementor-post__badge' => 'border-radius: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + $this->get_control_id( 'show_badge' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'badge_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 5, + 'max' => 50, + ], + 'em' => [ + 'min' => 0.5, + 'max' => 5, + ], + 'rem' => [ + 'min' => 0.5, + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__card .elementor-post__badge' => 'font-size: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + $this->get_control_id( 'show_badge' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'badge_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'default' => [ + 'size' => 20, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__card .elementor-post__badge' => 'margin: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + $this->get_control_id( 'show_badge' ) => 'yes', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'badge_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}} .elementor-post__card .elementor-post__badge', + 'exclude' => [ 'font_size', 'line-height' ], + 'condition' => [ + $this->get_control_id( 'show_badge' ) => 'yes', + ], + ] + ); + + $this->add_control( + 'heading_avatar_style', + [ + 'label' => esc_html__( 'Avatar', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => 'none', + $this->get_control_id( 'show_avatar' ) => 'show-avatar', + ], + ] + ); + + $this->add_control( + 'avatar_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__avatar' => 'top: calc(-{{SIZE}}{{UNIT}} / 2);', + '{{WRAPPER}} .elementor-post__avatar img' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .elementor-post__thumbnail__link' => 'margin-bottom: calc({{SIZE}}{{UNIT}} / 2)', + ], + 'condition' => [ + $this->get_control_id( 'show_avatar' ) => 'show-avatar', + ], + ] + ); + } + + public function register_badge_controls() { + $this->add_control( + 'show_badge', + [ + 'label' => esc_html__( 'Badge', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'badge_taxonomy', + [ + 'label' => esc_html__( 'Badge Taxonomy', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'label_block' => true, + 'default' => 'category', + 'options' => $this->get_taxonomies(), + 'condition' => [ + $this->get_control_id( 'show_badge' ) => 'yes', + ], + ] + ); + } + + public function register_avatar_controls() { + $this->add_control( + 'show_avatar', + [ + 'label' => esc_html__( 'Avatar', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'return_value' => 'show-avatar', + 'default' => 'show-avatar', + 'separator' => 'before', + 'prefix_class' => 'elementor-posts--', + 'render_type' => 'template', + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => 'none', + ], + ] + ); + } + + public function register_design_card_controls() { + $this->start_controls_section( + 'section_design_card', + [ + 'label' => esc_html__( 'Card', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'card_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-post__card' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'card_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-post__card' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'card_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__card' => 'border-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'card_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__card' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'card_padding', + [ + 'label' => esc_html__( 'Horizontal Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__text' => 'padding: 0 {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .elementor-post__meta-data' => 'padding: 10px {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .elementor-post__avatar' => 'padding-right: {{SIZE}}{{UNIT}}; padding-left: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'card_vertical_padding', + [ + 'label' => esc_html__( 'Vertical Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__card' => 'padding-top: {{SIZE}}{{UNIT}}; padding-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'box_shadow_box_shadow_type', // The name of this control is like that, for future extensibility to group_control box shadow. + [ + 'label' => esc_html__( 'Box Shadow', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'prefix_class' => 'elementor-card-shadow-', + 'default' => 'yes', + ] + ); + + $this->add_control( + 'hover_effect', + [ + 'label' => esc_html__( 'Hover Effect', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'label_block' => false, + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'gradient' => esc_html__( 'Gradient', 'elementor-pro' ), + //'zoom-in' => esc_html__( 'Zoom In', 'elementor-pro' ), + //'zoom-out' => esc_html__( 'Zoom Out', 'elementor-pro' ), + ], + 'default' => 'gradient', + 'separator' => 'before', + 'prefix_class' => 'elementor-posts__hover-', + ] + ); + + $this->add_control( + 'meta_border_color', + [ + 'label' => esc_html__( 'Meta Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'separator' => 'before', + 'selectors' => [ + '{{WRAPPER}} .elementor-post__card .elementor-post__meta-data' => 'border-top-color: {{VALUE}}', + ], + 'condition' => [ + $this->get_control_id( 'meta_data!' ) => [], + ], + ] + ); + + $this->end_controls_section(); + } + + protected function register_design_content_controls() { + parent::register_design_content_controls(); + + $this->remove_control( 'meta_spacing' ); + + $this->update_control( + 'read_more_spacing', + [ + 'selectors' => [ + '{{WRAPPER}} .elementor-post__read-more' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + ], + [ + 'recursive' => true, + ] + ); + } + + protected function get_taxonomies() { + $taxonomies = get_taxonomies( [ 'show_in_nav_menus' => true ], 'objects' ); + + $options = [ '' => '' ]; + + foreach ( $taxonomies as $taxonomy ) { + $options[ $taxonomy->name ] = $taxonomy->label; + } + + return $options; + } + + protected function render_post_header() { + ?> +
    > +
    + +
    +
    + +
    + +
    + get_instance_value( 'badge_taxonomy' ); + if ( empty( $taxonomy ) || ! taxonomy_exists( $taxonomy ) ) { + return; + } + + $terms = get_the_terms( get_the_ID(), $taxonomy ); + if ( empty( $terms[0] ) ) { + return; + } + ?> +
    name ); ?>
    + get_instance_value( 'thumbnail' ) ) { + return; + } + + $settings = $this->parent->get_settings(); + $setting_key = $this->get_control_id( 'thumbnail_size' ); + $settings[ $setting_key ] = [ + 'id' => get_post_thumbnail_id(), + ]; + $thumbnail_html = Group_Control_Image_Size::get_attachment_image_html( $settings, $setting_key ); + + if ( empty( $thumbnail_html ) ) { + return; + } + + $optional_attributes_html = $this->get_optional_link_attributes_html(); + + ?> + >
    + get_instance_value( 'show_badge' ) ) { + $this->render_badge(); + } + + if ( $this->get_instance_value( 'show_avatar' ) ) { + $this->render_avatar(); + } + } + + protected function render_post() { + $this->render_post_header(); + $this->render_thumbnail(); + $this->render_text_header(); + $this->render_title(); + $this->render_excerpt(); + $this->render_read_more(); + $this->render_text_footer(); + $this->render_meta_data(); + $this->render_post_footer(); + } +} diff --git a/modules/posts/skins/skin-classic.php b/modules/posts/skins/skin-classic.php new file mode 100644 index 0000000..9ec309a --- /dev/null +++ b/modules/posts/skins/skin-classic.php @@ -0,0 +1,194 @@ +start_controls_section( + 'section_design_box', + [ + 'label' => esc_html__( 'Box', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'box_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post' => 'border-style: solid; border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'box_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'box_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'content_padding', + [ + 'label' => esc_html__( 'Content Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__text' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + 'separator' => 'after', + ] + ); + + $this->start_controls_tabs( 'bg_effects_tabs' ); + + $this->start_controls_tab( 'classic_style_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_shadow', + 'selector' => '{{WRAPPER}} .elementor-post', + ] + ); + + $this->add_control( + 'box_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-post' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'box_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-post' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'classic_style_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_shadow_hover', + 'selector' => '{{WRAPPER}} .elementor-post:hover', + ] + ); + + $this->add_control( + 'box_bg_color_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-post:hover' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'box_border_color_hover', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-post:hover' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + } +} diff --git a/modules/posts/skins/skin-content-base.php b/modules/posts/skins/skin-content-base.php new file mode 100644 index 0000000..1201ba7 --- /dev/null +++ b/modules/posts/skins/skin-content-base.php @@ -0,0 +1,333 @@ +parent->get_name(); + add_action( 'elementor/element/' . $widget_name . '/section_layout/before_section_end', [ $this, 'register_skin_controls' ] ); + + if ( 'archive-posts' === $widget_name ) { + add_action( 'elementor/element/archive-posts/section_layout/after_section_end', [ $this, 'register_style_sections' ] ); + } else { + add_action( 'elementor/element/posts/section_query/after_section_end', [ $this, 'register_style_sections' ] ); + } + } + + public function get_title() { + return esc_html__( 'Full Content', 'elementor-pro' ); + } + + public function register_skin_controls( Widget_Base $widget ) { + $this->parent = $widget; + $this->register_post_count_control(); + $this->register_row_gap_control(); + $this->register_thumbnail_controls(); + $this->register_title_controls(); + $this->register_meta_data_controls(); + $this->register_link_controls(); + } + + public function register_thumbnail_controls() { + $this->add_control( + 'thumbnail', + [ + 'label' => esc_html__( 'Show Thumbnail', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'return_value' => 'thumbnail', + 'prefix_class' => 'elementor-posts--show-', + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Image_Size::get_type(), + [ + 'name' => 'thumbnail_size', + 'default' => 'medium', + 'exclude' => [ 'custom' ], + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => '', + ], + 'prefix_class' => 'elementor-posts--thumbnail-size-', + ] + ); + + $this->add_responsive_control( + 'item_ratio', + [ + 'label' => esc_html__( 'Image Ratio', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 0.66, + ], + 'tablet_default' => [ + 'size' => '', + ], + 'mobile_default' => [ + 'size' => 0.5, + ], + 'range' => [ + 'px' => [ + 'min' => 0.1, + 'max' => 2, + 'step' => 0.01, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-posts-container .elementor-post__thumbnail' => 'padding-bottom: calc( {{SIZE}} * 100% );', + '{{WRAPPER}}:after' => 'content: "{{SIZE}}"; position: absolute; color: transparent;', + ], + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => '', + ], + ] + ); + + $this->add_responsive_control( + 'image_width', + [ + 'label' => esc_html__( 'Image Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 600, + ], + 'em' => [ + 'max' => 6, + ], + 'rem' => [ + 'max' => 6, + ], + ], + 'default' => [ + 'size' => 100, + 'unit' => '%', + ], + 'tablet_default' => [ + 'size' => '', + 'unit' => '%', + ], + 'mobile_default' => [ + 'size' => 100, + 'unit' => '%', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__thumbnail__link' => 'width: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + $this->get_control_id( 'thumbnail!' ) => '', + ], + ] + ); + } + + public function register_design_controls() { + $this->register_additional_design_controls(); + $this->register_design_image_controls(); + $this->register_design_content_controls(); + $this->update_image_spacing_control(); + } + + public function register_row_gap_control() { + $this->add_control( + 'row_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 35, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'frontend_available' => true, + 'selectors' => [ + '{{WRAPPER}} .elementor-posts-container article' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + } + + // Update selectors for full content + public function update_image_spacing_control() { + $image_spacing_control = [ + 'selectors' => [ + '{{WRAPPER}} .elementor-posts--skin-full_content a.elementor-post__thumbnail__link' => 'margin-bottom: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .elementor-posts--skin-archive_full_content a.elementor-post__thumbnail__link' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ]; + $this->update_control( 'image_spacing', $image_spacing_control ); + } + + protected function render_thumbnail() { + $thumbnail = $this->get_instance_value( 'thumbnail' ); + + // In edit mode we render thumbnail to avoid server side rendering on each change. + if ( empty( $thumbnail ) && ! Plugin::elementor()->editor->is_edit_mode() ) { + return; + } + + $settings = $this->parent->get_settings(); + $setting_key = $this->get_control_id( 'thumbnail_size' ); + $settings[ $setting_key ] = [ + 'id' => get_post_thumbnail_id(), + ]; + $thumbnail_html = Group_Control_Image_Size::get_attachment_image_html( $settings, $setting_key ); + + if ( empty( $thumbnail_html ) ) { + return; + } + + $optional_attributes_html = $this->get_optional_link_attributes_html(); + + // PHPCS - `get_permalink` is safe. + ?> + > +
    +
    + ID ) ) { + // PHPCS - `get_the_password_form`. is safe. + echo get_the_password_form( $post->ID ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + return; + } + + // Avoid recursion + if ( isset( $did_posts[ $post->ID ] ) ) { + return; + } + + $level++; + $did_posts[ $post->ID ] = true; + // End avoid recursion + + $editor = Plugin::elementor()->editor; + $is_edit_mode = $editor->is_edit_mode(); + + if ( Plugin::elementor()->preview->is_preview_mode( $post->ID ) ) { + $content = Plugin::elementor()->preview->builder_wrapper( '' ); // XSS ok + } else { + /** + * @var ThemeBuilder ThemeBuilder + */ + $document = ThemeBuilder::instance()->get_document( $post->ID ); + // On view theme document show it's preview content. + if ( $document ) { + $preview_type = $document->get_settings( 'preview_type' ); + $preview_id = $document->get_settings( 'preview_id' ); + + if ( 0 === strpos( $preview_type, 'single' ) && ! empty( $preview_id ) ) { + $post = get_post( $preview_id ); + + if ( ! $post ) { + $level--; + + return; + } + } + } + + // Set edit mode as false, so don't render settings and etc. use the $is_edit_mode to indicate if we need the CSS inline + $editor->set_edit_mode( false ); + + // Print manually (and don't use `the_content()`) because it's within another `the_content` filter, and the Elementor filter has been removed to avoid recursion. + $content = Plugin::elementor()->frontend->get_builder_content( $post->ID, $with_css ); + + Plugin::elementor()->frontend->remove_content_filter(); + + if ( empty( $content ) ) { + // Split to pages. + setup_postdata( $post ); + + /** This filter is documented in wp-includes/post-template.php */ + // PHPCS - `get_the_content` is safe. + echo apply_filters( 'the_content', get_the_content() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + + wp_link_pages( [ + 'before' => '', + 'link_before' => '', + 'link_after' => '', + 'pagelink' => '' . esc_html__( 'Page', 'elementor-pro' ) . ' %', + 'separator' => ', ', + ] ); + + Plugin::elementor()->frontend->add_content_filter(); + + $level--; + + // Restore edit mode state + Plugin::elementor()->editor->set_edit_mode( $is_edit_mode ); + + return; + } else { + Plugin::elementor()->frontend->remove_content_filters(); + $content = apply_filters( 'the_content', $content ); + Plugin::elementor()->frontend->restore_content_filters(); + } + } // End if(). + + // Restore edit mode state + Plugin::elementor()->editor->set_edit_mode( $is_edit_mode ); + + if ( $with_wrapper ) { + echo '
    ' . balanceTags( $content, true ) . '
    '; // XSS ok. + } else { + echo $content; // XSS ok. + } + + $level--; + + if ( 0 === $level ) { + $did_posts = []; + } + } + + protected function render_post() { + $this->render_post_header(); + $this->render_thumbnail(); + $this->render_text_header(); + $this->render_title(); + $this->render_meta_data(); + $this->render_post_content( true ); + $this->render_text_footer(); + $this->render_post_footer(); + } +} diff --git a/modules/posts/skins/skin-full-content.php b/modules/posts/skins/skin-full-content.php new file mode 100644 index 0000000..e8ad346 --- /dev/null +++ b/modules/posts/skins/skin-full-content.php @@ -0,0 +1,14 @@ + esc_html__( 'Extra Small', 'elementor-pro' ), + 'sm' => esc_html__( 'Small', 'elementor-pro' ), + 'md' => esc_html__( 'Medium', 'elementor-pro' ), + 'lg' => esc_html__( 'Large', 'elementor-pro' ), + 'xl' => esc_html__( 'Extra Large', 'elementor-pro' ), + ]; + } + + protected function register_button_content_controls( $args = [] ) { + $default_args = [ + 'section_condition' => [], + 'button_text' => esc_html__( 'Click here', 'elementor-pro' ), + 'control_label_name' => esc_html__( 'Text', 'elementor-pro' ), + 'prefix_class' => 'elementor%s-align-', + 'alignment_default' => '', + 'exclude_inline_options' => [], + ]; + + $args = wp_parse_args( $args, $default_args ); + + $this->add_control( + 'button_type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'Default', 'elementor-pro' ), + 'info' => esc_html__( 'Info', 'elementor-pro' ), + 'success' => esc_html__( 'Success', 'elementor-pro' ), + 'warning' => esc_html__( 'Warning', 'elementor-pro' ), + 'danger' => esc_html__( 'Danger', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-button-', + 'condition' => $args['section_condition'], + ] + ); + + $this->add_control( + 'text', + [ + 'label' => $args['control_label_name'], + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'default' => $args['button_text'], + 'placeholder' => $args['button_text'], + 'condition' => $args['section_condition'], + ] + ); + + $this->add_control( + 'link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'default' => [ + 'url' => '#', + ], + 'condition' => $args['section_condition'], + ] + ); + + $this->add_responsive_control( + 'align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'prefix_class' => $args['prefix_class'], + 'default' => $args['alignment_default'], + 'condition' => $args['section_condition'], + ] + ); + + $this->add_control( + 'size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'sm', + 'options' => self::get_button_sizes(), + 'style_transfer' => true, + 'condition' => $args['section_condition'], + ] + ); + + $this->add_control( + 'selected_icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'skin' => 'inline', + 'label_block' => false, + 'condition' => $args['section_condition'], + 'exclude_inline_options' => $args['exclude_inline_options'], + ] + ); + + $this->add_control( + 'icon_align', + [ + 'label' => esc_html__( 'Icon Position', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'left', + 'options' => [ + 'left' => esc_html__( 'Before', 'elementor-pro' ), + 'right' => esc_html__( 'After', 'elementor-pro' ), + ], + 'condition' => array_merge( $args['section_condition'], [ 'selected_icon[value]!' => '' ] ), + ] + ); + + $this->add_control( + 'icon_indent', + [ + 'label' => esc_html__( 'Icon Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-button .elementor-align-icon-right' => 'margin-left: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .elementor-button .elementor-align-icon-left' => 'margin-right: {{SIZE}}{{UNIT}};', + ], + 'condition' => $args['section_condition'], + ] + ); + + $this->add_control( + 'button_css_id', + [ + 'label' => esc_html__( 'Button ID', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => '', + 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor-pro' ), + 'description' => esc_html__( 'Please make sure the ID is unique and not used elsewhere on the page this form is displayed. This field allows `A-z 0-9` & underscore chars without spaces.', 'elementor-pro' ), + 'separator' => 'before', + 'condition' => $args['section_condition'], + ] + ); + } + + protected function register_button_style_controls( $args = [] ) { + $default_args = [ + 'section_condition' => [], + ]; + + $args = wp_parse_args( $args, $default_args ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}} .elementor-button', + 'condition' => $args['section_condition'], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'text_shadow', + 'selector' => '{{WRAPPER}} .elementor-button', + 'condition' => $args['section_condition'], + ] + ); + + $this->start_controls_tabs( 'tabs_button_style', [ + 'condition' => $args['section_condition'], + ] ); + + $this->start_controls_tab( + 'tab_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + 'condition' => $args['section_condition'], + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-button' => 'fill: {{VALUE}}; color: {{VALUE}};', + ], + 'condition' => $args['section_condition'], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'background', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-button', + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + 'color' => [ + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + ], + ], + 'condition' => $args['section_condition'], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + 'condition' => $args['section_condition'], + ] + ); + + $this->add_control( + 'hover_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-button:hover, {{WRAPPER}} .elementor-button:focus' => 'color: {{VALUE}};', + '{{WRAPPER}} .elementor-button:hover svg, {{WRAPPER}} .elementor-button:focus svg' => 'fill: {{VALUE}};', + ], + 'condition' => $args['section_condition'], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'button_background_hover', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-button:hover, {{WRAPPER}} .elementor-button:focus', + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + ], + 'condition' => $args['section_condition'], + ] + ); + + $this->add_control( + 'button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'condition' => [ + 'border_border!' => '', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-button:hover, {{WRAPPER}} .elementor-button:focus' => 'border-color: {{VALUE}};', + ], + 'condition' => $args['section_condition'], + ] + ); + + $this->add_control( + 'hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + 'condition' => $args['section_condition'], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'border', + 'selector' => '{{WRAPPER}} .elementor-button', + 'separator' => 'before', + 'condition' => $args['section_condition'], + ] + ); + + $this->add_control( + 'border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-button' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => $args['section_condition'], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'button_box_shadow', + 'selector' => '{{WRAPPER}} .elementor-button', + 'condition' => $args['section_condition'], + ] + ); + + $this->add_responsive_control( + 'text_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'before', + 'condition' => $args['section_condition'], + ] + ); + } + + /** + * Render button widget output on the frontend. + * + * Written in PHP and used to generate the final HTML. + * + * @param \Elementor\Widget_Base|null $instance + * + * @since 3.4.0 + * @access protected + */ + protected function render_button( Widget_Base $instance = null ) { + if ( empty( $instance ) ) { + $instance = $this; + } + + $settings = $instance->get_settings(); + $instance->add_render_attribute( 'wrapper', 'class', 'elementor-button-wrapper' ); + + if ( ! empty( $settings['link']['url'] ) ) { + $instance->add_link_attributes( 'button', $settings['link'] ); + $instance->add_render_attribute( 'button', 'class', 'elementor-button-link' ); + } + + $instance->add_render_attribute( 'button', 'class', 'elementor-button' ); + $instance->add_render_attribute( 'button', 'role', 'button' ); + + if ( ! empty( $settings['button_css_id'] ) ) { + $instance->add_render_attribute( 'button', 'id', $settings['button_css_id'] ); + } + + if ( ! empty( $settings['size'] ) ) { + $instance->add_render_attribute( 'button', 'class', 'elementor-size-' . $settings['size'] ); + } + + if ( $settings['hover_animation'] ) { + $instance->add_render_attribute( 'button', 'class', 'elementor-animation-' . $settings['hover_animation'] ); + } + ?> + + get_settings(); + + $migrated = isset( $settings['__fa4_migrated']['selected_icon'] ); + $is_new = empty( $settings['icon'] ) && Icons_Manager::is_migration_allowed(); + + if ( ! $is_new && empty( $settings['icon_align'] ) ) { + // @todo: remove when deprecated + // added as bc in 2.6 + //old default + $settings['icon_align'] = $instance->get_settings( 'icon_align' ); + } + + $instance->add_render_attribute( [ + 'content-wrapper' => [ + 'class' => 'elementor-button-content-wrapper', + ], + 'icon-align' => [ + 'class' => [ + 'elementor-button-icon', + 'elementor-align-icon-' . $settings['icon_align'], + ], + ], + 'text' => [ + 'class' => 'elementor-button-text', + ], + ] ); + + // TODO: replace the protected with public + //$instance->add_inline_editing_attributes( 'text', 'none' ); + ?> + print_render_attribute_string( 'content-wrapper' ); ?>> + + print_render_attribute_string( 'icon-align' ); ?>> + 'true' ] ); + else : ?> + + + + + print_render_attribute_string( 'text' ); ?>>print_unescaped_setting public. + Utils::print_unescaped_internal_string( $settings['text'] ); + ?> + + get_widgets_that_support_pagination(); + + Plugin::elementor()->db->iterate_data( + $elements, + $this->check_pagination_handler( $posts_widgets, $current_page, $is_valid ) + ); + + return $is_valid; + } + + /** + * Get all widgets that may add pagination. + * + * @return array + */ + public function get_widgets_that_support_pagination() { + $widgets = Plugin::elementor()->widgets_manager->get_widget_types(); + + $posts_widgets = []; + + foreach ( $widgets as $widget ) { + if ( $widget instanceof Posts_Base ) { + $posts_widgets[] = $widget->get_name(); + } + } + + return $posts_widgets; + } + + /** + * @return void + */ + public function check_pagination_handler( array $posts_widgets, $current_page, &$is_valid ) { + return function ( $element ) use ( &$is_valid, $posts_widgets, $current_page ) { + if ( ! $this->is_valid_post_widget( $element, $posts_widgets ) ) { + return; + } + + $is_valid = $this->should_allow_pagination( $element, $current_page ); + }; + } + + /** + * @return bool + */ + private function is_valid_post_widget( $element, $posts_widgets ) { + return isset( $element['widgetType'] ) && in_array( $element['widgetType'], $posts_widgets, true ); + } + + /** + * @return bool + */ + private function widget_has_pagination( $element ) { + return ! empty( $element['settings']['pagination_type'] ); + } + + /** + * @return bool + */ + private function should_allow_pagination( $element, $current_page ) { + if ( ! $this->widget_has_pagination( $element ) ) { + return false; + } + + $using_ajax_pagination = in_array($element['settings']['pagination_type'], [ + Posts_Base::LOAD_MORE_ON_CLICK, + Posts_Base::LOAD_MORE_INFINITE_SCROLL, + ], true); + + if ( empty( $element['settings']['pagination_page_limit'] ) || $using_ajax_pagination ) { + return true; + } + + return (int) $current_page <= (int) $element['settings']['pagination_page_limit']; + } + + public function get_base_url() { + if ( is_page() || is_single() ) { + // Check if it's a normal page. + return get_permalink(); + } elseif ( is_year() ) { + return get_year_link( get_query_var( 'year' ) ); + } elseif ( is_month() ) { + return get_month_link( get_query_var( 'year' ), get_query_var( 'monthnum' ) ); + } elseif ( is_day() ) { + return get_day_link( get_query_var( 'year' ), get_query_var( 'monthnum' ), get_query_var( 'day' ) ); + } elseif ( is_category() || is_tag() || is_tax() ) { + $queried_object = get_queried_object(); + return get_term_link( $queried_object->term_id, $queried_object->taxonomy ); + } elseif ( is_author() ) { + return get_author_posts_url( get_the_author_meta( 'ID' ) ); + } elseif ( is_search() ) { + return get_search_link(); + } elseif ( is_archive() ) { + // Check if it's an archive page. + return get_post_type_archive_link( get_post_type() ); + } elseif ( is_singular() && 'post' !== get_post_type() && 'page' !== get_post_type() ) { + // Check if it's a single post/page of a custom post type. + $post_type = get_post_type_object( get_post_type() ); + + if ( $post_type->has_archive ) { + return get_post_type_archive_link( get_post_type() ); + } else { + return get_permalink(); + } + } elseif ( $this->is_posts_page() ) { + return get_permalink( get_option( 'page_for_posts' ) ); + } + + // Fallback to home URL. + return home_url( '/' ); + } + + /** + * Determines whether the query is for an existing blog posts index page + * + * @param bool $custom_page_option + * @return bool + */ + private function is_posts_page( $custom_page_option = true ) { + if ( $custom_page_option ) { + return ! is_front_page() && is_home(); + } + + $posts_page_id = (int) get_option( 'page_for_posts' ); + $base_url = get_query_var( 'pagination_base_url' ); + + if ( ! empty( $base_url ) ) { + $post_id = url_to_postid( $base_url ); + + return $posts_page_id === $post_id; + } + + return false; + } +} diff --git a/modules/posts/widgets/portfolio.php b/modules/posts/widgets/portfolio.php new file mode 100644 index 0000000..5f219a6 --- /dev/null +++ b/modules/posts/widgets/portfolio.php @@ -0,0 +1,716 @@ +_query; + } + + protected function register_controls() { + $this->register_query_section_controls(); + } + + private function register_query_section_controls() { + $this->start_controls_section( + 'section_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_responsive_control( + 'columns', + [ + 'label' => esc_html__( 'Columns', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '3', + 'tablet_default' => '2', + 'mobile_default' => '1', + 'options' => [ + '1' => '1', + '2' => '2', + '3' => '3', + '4' => '4', + '5' => '5', + '6' => '6', + ], + 'prefix_class' => 'elementor-grid%s-', + 'frontend_available' => true, + 'selectors' => [ + '.elementor-msie {{WRAPPER}} .elementor-portfolio-item' => 'width: calc( 100% / {{SIZE}} )', + ], + ] + ); + + $this->add_control( + 'posts_per_page', + [ + 'label' => esc_html__( 'Posts Per Page', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 6, + ] + ); + + $this->add_group_control( + Group_Control_Image_Size::get_type(), + [ + 'name' => 'thumbnail_size', + 'exclude' => [ 'custom' ], + 'default' => 'medium', + 'prefix_class' => 'elementor-portfolio--thumbnail-size-', + ] + ); + + $this->add_control( + 'masonry', + [ + 'label' => esc_html__( 'Masonry', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'condition' => [ + 'columns!' => '1', + ], + 'render_type' => 'ui', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'item_ratio', + [ + 'label' => esc_html__( 'Item Ratio', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 0.66, + ], + 'range' => [ + 'px' => [ + 'min' => 0.1, + 'max' => 2, + 'step' => 0.01, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post__thumbnail__link' => 'padding-bottom: calc( {{SIZE}} * 100% )', + '{{WRAPPER}}:after' => 'content: "{{SIZE}}"; position: absolute; color: transparent;', + ], + 'condition' => [ + 'masonry' => '', + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'show_title', + [ + 'label' => esc_html__( 'Show Title', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'title_tag', + [ + 'label' => esc_html__( 'Title HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + 'p' => 'p', + ], + 'default' => 'h3', + 'condition' => [ + 'show_title' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_query', + [ + 'label' => esc_html__( 'Query', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_group_control( + Group_Control_Related::get_type(), + [ + 'name' => 'posts', + 'presets' => [ 'full' ], + 'exclude' => [ + 'posts_per_page', //use the one from Layout section + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'filter_bar', + [ + 'label' => esc_html__( 'Filter Bar', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_control( + 'show_filter_bar', + [ + 'label' => esc_html__( 'Show', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'taxonomy', + [ + 'label' => esc_html__( 'Taxonomy', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'label_block' => true, + 'default' => [], + 'options' => $this->get_taxonomies(), + 'condition' => [ + 'show_filter_bar' => 'yes', + 'posts_post_type!' => 'by_id', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_design_layout', + [ + 'label' => esc_html__( 'Items', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + /* + * The `item_gap` control is replaced by `column_gap` and `row_gap` controls since v 2.1.6 + * It is left (hidden) in the widget, to provide compatibility with older installs + */ + + $this->add_control( + 'item_gap', + [ + 'label' => esc_html__( 'Item Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--grid-row-gap: {{SIZE}}{{UNIT}}; --grid-column-gap: {{SIZE}}{{UNIT}};', + ], + 'frontend_available' => true, + 'classes' => 'elementor-hidden', + ] + ); + + $this->add_control( + 'column_gap', + [ + 'label' => esc_html__( 'Columns Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => ' --grid-column-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'row_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'frontend_available' => true, + 'selectors' => [ + '{{WRAPPER}}' => '--grid-row-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-portfolio-item__img, {{WRAPPER}} .elementor-portfolio-item__overlay' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_design_overlay', + [ + 'label' => esc_html__( 'Item Overlay', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'color_background', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}} a .elementor-portfolio-item__overlay' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'color_title', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'separator' => 'before', + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} a .elementor-portfolio-item__title' => 'color: {{VALUE}};', + ], + 'condition' => [ + 'show_title' => 'yes', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'typography_title', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-portfolio-item__title', + 'condition' => [ + 'show_title' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_design_filter', + [ + 'label' => esc_html__( 'Filter Bar', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'show_filter_bar' => 'yes', + ], + ] + ); + + $this->add_control( + 'color_filter', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-portfolio__filter' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'color_filter_active', + [ + 'label' => esc_html__( 'Active Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-portfolio__filter.elementor-active' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'typography_filter', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-portfolio__filter', + ] + ); + + $this->add_control( + 'filter_item_spacing', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 10, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-portfolio__filter:not(:last-child)' => 'margin-right: calc({{SIZE}}{{UNIT}}/2)', + '{{WRAPPER}} .elementor-portfolio__filter:not(:first-child)' => 'margin-left: calc({{SIZE}}{{UNIT}}/2)', + ], + ] + ); + + $this->add_control( + 'filter_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 10, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-portfolio__filters' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function get_taxonomies() { + $taxonomies = get_taxonomies( [ 'show_in_nav_menus' => true ], 'objects' ); + + $options = [ '' => '' ]; + + foreach ( $taxonomies as $taxonomy ) { + $options[ $taxonomy->name ] = $taxonomy->label; + } + + return $options; + } + + protected function get_posts_tags() { + $taxonomy = $this->get_settings( 'taxonomy' ); + + foreach ( $this->_query->posts as $post ) { + if ( ! $taxonomy ) { + $post->tags = []; + + continue; + } + + $tags = wp_get_post_terms( $post->ID, $taxonomy ); + + $tags_slugs = []; + + foreach ( $tags as $tag ) { + $tags_slugs[ $tag->term_id ] = $tag; + } + + $post->tags = $tags_slugs; + } + } + + public function query_posts() { + + $query_args = [ + 'posts_per_page' => $this->get_settings( 'posts_per_page' ), + ]; + + /** @var Module_Query $elementor_query */ + $elementor_query = Module_Query::instance(); + $this->_query = $elementor_query->get_query( $this, 'posts', $query_args, [] ); + } + + public function render() { + $this->query_posts(); + + $wp_query = $this->get_query(); + + if ( ! $wp_query->found_posts ) { + return; + } + + $this->get_posts_tags(); + + $this->render_loop_header(); + + while ( $wp_query->have_posts() ) { + $wp_query->the_post(); + + $this->render_post(); + } + + $this->render_loop_footer(); + + wp_reset_postdata(); + } + + protected function render_thumbnail() { + $settings = $this->get_settings(); + + $settings['thumbnail_size'] = [ + 'id' => get_post_thumbnail_id(), + ]; + ?> +
    + +
    + get_settings( 'taxonomy' ); + + if ( ! $taxonomy ) { + return; + } + + $terms = []; + + foreach ( $this->_query->posts as $post ) { + $terms += $post->tags; + } + + if ( empty( $terms ) ) { + return; + } + + usort( $terms, function( $a, $b ) { + return strcmp( $a->name, $b->name ); + } ); + + ?> +
      +
    • + +
    • name ); ?>
    • + +
    + get_settings( 'show_title' ) ) { + return; + } + + $tag = $this->get_settings( 'title_tag' ); + ?> + < class="elementor-portfolio-item__title"> + + > + tags ) { + return; + } + + $separator = ''; + + $tags_array = []; + + foreach ( $post->tags as $tag ) { + $tags_array[] = '' . esc_html( $tag->name ) . ''; + } + + ?> +
    + + +
    + term_id; + }, $post->tags ); + + $classes = [ + 'elementor-portfolio-item', + 'elementor-post', + implode( ' ', $tags_classes ), + ]; + + // PHPCS - `get_permalink` is safe. + ?> + + +
    + +
    + get_settings( 'show_filter_bar' ) ) { + $this->render_filter_menu(); + } + ?> +
    + +
    + render_post_header(); + $this->render_thumbnail(); + $this->render_overlay_header(); + $this->render_title(); + // $this->render_categories_names(); + $this->render_overlay_footer(); + $this->render_post_footer(); + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'posts'; + } +} diff --git a/modules/posts/widgets/posts-base.php b/modules/posts/widgets/posts-base.php new file mode 100644 index 0000000..37a91a9 --- /dev/null +++ b/modules/posts/widgets/posts-base.php @@ -0,0 +1,905 @@ +query; + } + + public function render() {} + + public function register_load_more_button_style_controls() { + $this->add_control( + 'heading_load_more_style_button', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'condition' => [ + 'pagination_type' => 'load_more_on_click', + ], + ] + ); + + $this->register_button_style_controls( [ + 'section_condition' => [ + 'pagination_type' => 'load_more_on_click', + ], + ] ); + } + + public function register_load_more_message_style_controls() { + $this->add_control( + 'heading_load_more_on_click_no_posts_message', + [ + 'label' => esc_html__( 'No More Posts Message', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'pagination_type' => 'load_more_on_click', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'heading_load_more_on_click_infinity_scroll_no_posts_message', + [ + 'label' => esc_html__( 'No More Posts Message', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'condition' => [ + 'pagination_type' => 'load_more_infinite_scroll', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'load_more_no_posts_message', + 'selector' => '{{WRAPPER}} .e-load-more-message', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + ] + ); + + $this->add_control( + 'load_more_no_posts_message_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}}' => '--load-more-message-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'load_more_spinner_color', + [ + 'label' => esc_html__( 'Spinner Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}}' => '--load-more-spinner-color: {{VALUE}};', + ], + 'separator' => 'before', + 'condition' => [ + 'load_more_spinner[value]!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'load_more_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--load-more—spacing: {{SIZE}}{{UNIT}};', + ], + 'separator' => 'before', + ] + ); + } + + public function register_pagination_section_controls() { + $this->start_controls_section( + 'section_pagination', + [ + 'label' => esc_html__( 'Pagination', 'elementor-pro' ), + 'condition' => [ + '_skin!' => [ + LoopBuilderModule::LOOP_POST_TAXONOMY_SKIN_ID, + WoocommerceModule::LOOP_PRODUCT_TAXONOMY_SKIN_ID, + ], + ], + ] + ); + + $this->add_control( + 'pagination_type', + [ + 'label' => esc_html__( 'Pagination', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => $this->get_pagination_type_options(), + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'pagination_page_limit', + [ + 'label' => esc_html__( 'Page Limit', 'elementor-pro' ), + 'default' => '5', + 'condition' => [ + 'pagination_type!' => [ + 'load_more_on_click', + 'load_more_infinite_scroll', + '', + ], + ], + ] + ); + + $this->add_control( + 'pagination_numbers_shorten', + [ + 'label' => esc_html__( 'Shorten', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'condition' => [ + 'pagination_type' => [ + 'numbers', + 'numbers_and_prev_next', + ], + ], + ] + ); + + $this->add_control( + 'pagination_prev_label', + [ + 'label' => esc_html__( 'Previous Label', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'default' => esc_html__( '« Previous', 'elementor-pro' ), + 'condition' => [ + 'pagination_type' => [ + 'prev_next', + 'numbers_and_prev_next', + ], + ], + ] + ); + + $this->add_control( + 'pagination_next_label', + [ + 'label' => esc_html__( 'Next Label', 'elementor-pro' ), + 'default' => esc_html__( 'Next »', 'elementor-pro' ), + 'condition' => [ + 'pagination_type' => [ + 'prev_next', + 'numbers_and_prev_next', + ], + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'pagination_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'default' => 'center', + 'selectors' => [ + '{{WRAPPER}} .elementor-pagination' => 'text-align: {{VALUE}};', + ], + 'condition' => [ + 'pagination_type!' => [ + 'load_more_on_click', + 'load_more_infinite_scroll', + '', + ], + ], + ] + ); + + $this->add_control( + 'pagination_individual_divider', + [ + 'type' => Controls_Manager::DIVIDER, + 'condition' => [ + 'pagination_type' => [ + 'numbers', + 'numbers_and_prev_next', + 'prev_next', + ], + ], + ] + ); + + $this->add_control( + 'pagination_individual_handle', + [ + 'label' => esc_html__( 'Individual Pagination', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'default' => '', + 'condition' => [ + 'pagination_type' => [ + 'numbers', + 'numbers_and_prev_next', + 'prev_next', + ], + ], + ] + ); + + $this->add_control( + 'pagination_individual_handle_message', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'For multiple Posts Widgets on the same page, toggle this on to control the pagination for each individually. Note: It affects the page\'s URL structure.', 'elementor-pro' ), + 'content_classes' => 'elementor-control-field-description', + 'condition' => [ + 'pagination_type' => [ + 'numbers', + 'numbers_and_prev_next', + 'prev_next', + ], + ], + ] + ); + + $this->add_control( + 'load_more_spinner', + [ + 'label' => esc_html__( 'Spinner', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'default' => [ + 'value' => 'fas fa-spinner', + 'library' => 'fa-solid', + ], + 'exclude_inline_options' => [ 'svg' ], + 'recommended' => [ + 'fa-solid' => [ + 'spinner', + 'cog', + 'sync', + 'sync-alt', + 'asterisk', + 'circle-notch', + ], + ], + 'skin' => 'inline', + 'label_block' => false, + 'condition' => [ + 'pagination_type' => [ + 'load_more_on_click', + 'load_more_infinite_scroll', + ], + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'heading_load_more_button', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'pagination_type' => 'load_more_on_click', + ], + ] + ); + + $this->register_button_content_controls( [ + 'button_text' => esc_html__( 'Load More', 'elementor-pro' ), + 'control_label_name' => esc_html__( 'Button Text', 'elementor-pro' ), + 'prefix_class' => 'load-more-align-', + 'alignment_default' => 'center', + 'section_condition' => [ + 'pagination_type' => 'load_more_on_click', + ], + 'exclude_inline_options' => [ 'svg' ], + ] ); + + $this->remove_control( 'button_type' ); + $this->remove_control( 'link' ); + $this->remove_control( 'size' ); + + $this->add_control( + 'heading_load_more_no_posts_message', + [ + 'label' => esc_html__( 'No More Posts Message', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'pagination_type' => [ + 'load_more_on_click', + 'load_more_infinite_scroll', + ], + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_responsive_control( + 'load_more_no_posts_message_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--load-more-message-alignment: {{VALUE}};', + ], + 'condition' => [ + 'pagination_type' => [ + 'load_more_on_click', + 'load_more_infinite_scroll', + ], + ], + ] + ); + + $this->add_control( + 'load_more_no_posts_message_switcher', + [ + 'label' => esc_html__( 'Custom Messages', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'condition' => [ + 'pagination_type' => [ + 'load_more_on_click', + 'load_more_infinite_scroll', + ], + ], + ] + ); + + $this->add_control( + 'load_more_no_posts_custom_message', + [ + 'label' => esc_html__( 'No more posts message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'No more posts to show', 'elementor-pro' ), + 'condition' => [ + 'pagination_type' => [ + 'load_more_on_click', + 'load_more_infinite_scroll', + ], + 'load_more_no_posts_message_switcher' => 'yes', + ], + 'label_block' => true, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->end_controls_section(); + + // Pagination style controls for prev/next and numbers pagination. + $this->start_controls_section( + 'section_pagination_style', + [ + 'label' => esc_html__( 'Pagination', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'pagination_type!' => [ + 'load_more_on_click', + 'load_more_infinite_scroll', + '', + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'pagination_typography', + 'selector' => '{{WRAPPER}} .elementor-pagination', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + ] + ); + + $this->add_control( + 'pagination_color_heading', + [ + 'label' => esc_html__( 'Colors', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->start_controls_tabs( 'pagination_colors' ); + + $this->start_controls_tab( + 'pagination_color_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'pagination_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-pagination .page-numbers:not(.dots)' => 'color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'pagination_color_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'pagination_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-pagination a.page-numbers:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'pagination_color_active', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'pagination_active_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-pagination .page-numbers.current' => 'color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_responsive_control( + 'pagination_spacing', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'separator' => 'before', + 'default' => [ + 'size' => 10, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .elementor-pagination .page-numbers:not(:first-child)' => 'margin-left: calc( {{SIZE}}{{UNIT}}/2 );', + 'body:not(.rtl) {{WRAPPER}} .elementor-pagination .page-numbers:not(:last-child)' => 'margin-right: calc( {{SIZE}}{{UNIT}}/2 );', + 'body.rtl {{WRAPPER}} .elementor-pagination .page-numbers:not(:first-child)' => 'margin-right: calc( {{SIZE}}{{UNIT}}/2 );', + 'body.rtl {{WRAPPER}} .elementor-pagination .page-numbers:not(:last-child)' => 'margin-left: calc( {{SIZE}}{{UNIT}}/2 );', + ], + ] + ); + + $this->add_responsive_control( + 'pagination_spacing_top', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-pagination' => 'margin-top: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + + // Pagination style controls for on-load pagination with type on-click/infinity-scroll. + $this->start_controls_section( + 'section_style', + [ + 'label' => esc_html__( 'Pagination', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'pagination_type' => [ + 'load_more_on_click', + 'load_more_infinite_scroll', + ], + ], + ] + ); + + $this->register_load_more_button_style_controls(); + + $this->register_load_more_message_style_controls(); + + $this->end_controls_section(); + } + + abstract public function query_posts(); + + public function get_current_page() { + if ( '' === $this->get_settings_for_display( 'pagination_type' ) ) { + return 1; + } + + return max( + 1, + get_query_var( 'paged' ), + get_query_var( 'page' ), + Utils::_unstable_get_super_global_value( $_GET, 'e-page-' . $this->get_id() ) + ); + } + + public function is_rest_request() { + $request_uri = Utils::_unstable_get_super_global_value( $_SERVER, 'REQUEST_URI' ); + + return false !== wp_get_referer() && + isset( $_SERVER['REQUEST_URI'] ) && + ( false !== strpos( $request_uri, 'wp-json' ) || false !== strpos( $request_uri, 'rest_route' ) ); + } + + public function get_wp_link_page( $i ) { + if ( ( ! is_singular() || is_front_page() ) && ! $this->is_rest_request() && ! $this->is_allow_to_use_custom_page_option() ) { + return get_pagenum_link( $i ); + } + + // Based on wp-includes/post-template.php:957 `_wp_link_page`. + global $wp_rewrite; + $post = get_post(); + $query_args = []; + $url = get_permalink(); + + if ( $this->is_rest_request() ) { + $link_unescaped = wp_get_referer(); + $post_id = url_to_postid( $link_unescaped ); + + if ( $post_id > 0 ) { + $post = get_post( $post_id ); + } + + $url = $this->get_base_url_for_rest_request( $post_id, $url ); + } + + if ( $i > 1 ) { + if ( '' === get_option( 'permalink_structure' ) || in_array( $post->post_status, [ 'draft', 'pending' ] ) ) { + $url = add_query_arg( $this->get_wp_pagination_query_var(), $i, $url ); + } elseif ( get_option( 'show_on_front' ) === 'page' && (int) get_option( 'page_on_front' ) === $post->ID ) { + $url = trailingslashit( $url ) . user_trailingslashit( "$wp_rewrite->pagination_base/" . $i, 'single_paged' ); + } else { + $url = trailingslashit( $url ) . user_trailingslashit( $i, 'single_paged' ); + } + } + + if ( $i > 1 && $this->is_allow_to_use_custom_page_option() ) { + $url = $this->get_wp_link_page_url_for_custom_page_option( $url, $i, $post_id ?? 0 ); + } + + if ( 1 === $i && $this->is_allow_to_use_custom_page_option() ) { + $url = $this->get_base_url(); + } + + if ( is_preview() ) { + $url = $this->get_wp_link_page_url_for_preview( $post, $query_args, $url ); + } + + if ( $this->is_rest_request() ) { + $url = $this->get_wp_link_page_url_for_rest_request( $url, $link_unescaped ); + } + + if ( ! $this->is_rest_request() && $this->current_url_contains_taxonomy_filter() && ! is_preview() ) { + $url = $this->get_wp_link_page_url_for_normal_page_load( $url ); + } + + return $url; + } + + public function is_allow_to_use_custom_page_option() { + return 'ajax' === $this->get_settings_for_display( 'pagination_load_type' ) || 'yes' === $this->get_settings_for_display( 'pagination_individual_handle' ); + } + + protected function get_base_url_for_rest_request( $post_id, $url ) { + if ( $post_id > 0 ) { + return get_permalink( $post_id ); + } + + global $wp_rewrite; + + if ( $wp_rewrite->using_permalinks() && ( $this->current_url_contains_taxonomy_filter() || $this->referer_contains_taxonomy_filter() ) ) { + $url = $this->is_allow_to_use_custom_page_option() ? get_query_var( 'pagination_base_url' ) : get_query_var( 'pagination_base_url' ) . user_trailingslashit( "$wp_rewrite->pagination_base/", 'single_paged' ); + } else { + $url = remove_query_arg( 'p', $url ); + } + + return $url; + } + + protected function get_wp_link_page_url_for_preview( $post, $query_args, $url ) { + if ( 'draft' === $post->post_status || ! isset( $_GET['preview_id'], $_GET['preview_nonce'] ) ) { + return $url; + } + + $query_args['preview_id'] = Utils::_unstable_get_super_global_value( $_GET, 'preview_id' ); + $query_args['preview_nonce'] = Utils::_unstable_get_super_global_value( $_GET, 'preview_nonce' ); + + if ( $this->is_rest_request() || ! $this->current_url_contains_taxonomy_filter() ) { + return get_preview_post_link( $post, $query_args, $url ); + } + + wp_parse_str( htmlspecialchars_decode( Utils::_unstable_get_super_global_value( $_SERVER, 'QUERY_STRING' ) ), $query_params ); + + foreach ( $query_params as $param_key => $param_value ) { + if ( false !== strpos( $param_key, 'e-filter-' ) ) { + $query_args[ $param_key ] = $param_value; + } + } + + return get_preview_post_link( $post, $query_args, $url ); + } + + protected function get_wp_link_page_url_for_rest_request( $url, $link_unescaped ) { + $url_components = wp_parse_url( $link_unescaped ); + $query_args = []; + + if ( isset( $url_components['query'] ) ) { + wp_parse_str( $url_components['query'], $query_args ); + } + + $url = ! empty( $query_args ) ? $url . '&' . http_build_query( $query_args ) : $url; + + return $this->format_query_string_concatenation( $url ); + } + + protected function get_wp_link_page_url_for_normal_page_load( $url ) { + wp_parse_str( htmlspecialchars_decode( Utils::_unstable_get_super_global_value( $_SERVER, 'QUERY_STRING' ) ), $query_params ); + + $e_filters = ''; + + foreach ( $query_params as $param_key => $param_value ) { + if ( false !== strpos( $param_key, 'e-filter' ) ) { + $e_filters .= '&' . $param_key . '=' . $param_value; + } + } + + return $this->format_query_string_concatenation( $url . $e_filters ); + } + + public function current_url_contains_taxonomy_filter() { + return false !== strpos( Utils::_unstable_get_super_global_value( $_SERVER, 'QUERY_STRING' ), 'e-filter-' ); + } + + public function referer_contains_taxonomy_filter() { + return false !== strpos( Utils::_unstable_get_super_global_value( $_SERVER, 'HTTP_REFERER' ), 'e-filter-' ); + } + + protected function format_query_string_concatenation( $input ) { + if ( false === strpos( $input, '?' ) ) { + // If "?" doesn't exist in the input URL, replace the first "&" with "?" + $input = preg_replace( '/&/', '?', $input, 1 ); + } + + return $input; + } + + public function get_posts_nav_link( $page_limit = null ) { + if ( ! $page_limit ) { + $page_limit = $this->query->max_num_pages; + } + + $return = []; + + $paged = $this->get_current_page(); + + $link_template = '%s'; + $disabled_template = '%s'; + + if ( $paged > 1 ) { + $next_page = intval( $paged ) - 1; + if ( $next_page < 1 ) { + $next_page = 1; + } + + $return['prev'] = sprintf( $link_template, 'prev', $this->get_wp_link_page( $next_page ), $this->get_settings_for_display( 'pagination_prev_label' ) ); + } else { + $return['prev'] = sprintf( $disabled_template, 'prev', $this->get_settings_for_display( 'pagination_prev_label' ) ); + } + + $next_page = intval( $paged ) + 1; + + if ( $next_page <= $page_limit ) { + $return['next'] = sprintf( $link_template, 'next', $this->get_wp_link_page( $next_page ), $this->get_settings_for_display( 'pagination_next_label' ) ); + } else { + $return['next'] = sprintf( $disabled_template, 'next', $this->get_settings_for_display( 'pagination_next_label' ) ); + } + + return $return; + } + + protected function register_controls() { + $this->start_controls_section( + 'section_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->end_controls_section(); + } + + protected function get_pagination_type_options() { + return [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'numbers' => esc_html__( 'Numbers', 'elementor-pro' ), + 'prev_next' => esc_html__( 'Previous/Next', 'elementor-pro' ), + 'numbers_and_prev_next' => esc_html__( 'Numbers', 'elementor-pro' ) . ' + ' . esc_html__( 'Previous/Next', 'elementor-pro' ), + self::LOAD_MORE_ON_CLICK => esc_html__( 'Load on Click', 'elementor-pro' ), + self::LOAD_MORE_INFINITE_SCROLL => esc_html__( 'Infinite Scroll', 'elementor-pro' ), + ]; + } + + public function render_plain_content() {} + + /** + * @param string $url + * @param int $i + * @param int $post_id + * @return string + */ + private function get_wp_link_page_url_for_custom_page_option( $url, $i, $post_id ) { + $base_raw_url = $this->is_rest_request() ? $this->get_base_url_for_rest_request( $post_id, $url ) : $this->get_base_url(); + + return $this->format_query_string_concatenation( $base_raw_url . '&e-page-' . $this->get_id() . '=' . $i ); + } + + /** + * @return string + */ + private function get_wp_pagination_query_var() { + if ( '' === get_option( 'permalink_structure' ) && $this->is_posts_page( $this->is_allow_to_use_custom_page_option() ) ) { + return 'paged'; + } + + return 'page'; + } +} diff --git a/modules/posts/widgets/posts.php b/modules/posts/widgets/posts.php new file mode 100644 index 0000000..3a36dfc --- /dev/null +++ b/modules/posts/widgets/posts.php @@ -0,0 +1,113 @@ +add_skin( new Skins\Skin_Classic( $this ) ); + $this->add_skin( new Skins\Skin_Cards( $this ) ); + $this->add_skin( new Skins\Skin_Full_Content( $this ) ); + } + + protected function register_controls() { + parent::register_controls(); + + $this->register_query_section_controls(); + $this->register_pagination_section_controls(); + } + + /** + * Get Query Name + * + * Returns the query control name used in the widget's main query. + * + * @since 3.8.0 + * + * @return string + */ + public function get_query_name() { + return $this->get_name(); + } + + public function query_posts() { + $query_args = [ + 'posts_per_page' => $this->get_posts_per_page_value(), + 'paged' => $this->get_current_page(), + 'has_custom_pagination' => $this->is_allow_to_use_custom_page_option(), + ]; + + /** @var Module_Query $elementor_query */ + $elementor_query = Module_Query::instance(); + $this->query = $elementor_query->get_query( $this, $this->get_query_name(), $query_args, [] ); + } + + /** + * Get Posts Per Page Value + * + * Returns the value of the Posts Per Page control of the widget. This method was created because in some cases, + * the control is registered in the widget, and in some cases, it is registered in a widget skin. + * + * @since 3.8.0 + * @access protected + * + * @return mixed + */ + protected function get_posts_per_page_value() { + return $this->get_current_skin()->get_instance_value( 'posts_per_page' ); + } + + protected function register_query_section_controls() { + $this->start_controls_section( + 'section_query', + [ + 'label' => esc_html__( 'Query', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_group_control( + Group_Control_Related::get_type(), + [ + 'name' => $this->get_name(), + 'presets' => [ 'full' ], + 'exclude' => [ + 'posts_per_page', //use the one from Layout section + ], + ] + ); + + $this->end_controls_section(); + } +} diff --git a/modules/pricing/module.php b/modules/pricing/module.php new file mode 100644 index 0000000..d12a2d8 --- /dev/null +++ b/modules/pricing/module.php @@ -0,0 +1,22 @@ +start_controls_section( + 'section_list', + [ + 'label' => esc_html__( 'List', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $repeater = new Repeater(); + + $repeater->add_control( + 'price', + [ + 'label' => esc_html__( 'Price', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $repeater->add_control( + 'title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'label_block' => 'true', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'item_description', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => '', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'image', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'default' => [], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'default' => [ 'url' => '#' ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'price_list', + [ + 'label' => esc_html__( 'List Items', 'elementor-pro' ), + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'default' => [ + [ + 'title' => esc_html__( 'First item on the list', 'elementor-pro' ), + 'item_description' => esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), + 'price' => '$20', + 'link' => [ 'url' => '#' ], + ], + [ + 'title' => esc_html__( 'Second item on the list', 'elementor-pro' ), + 'item_description' => esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), + 'price' => '$9', + 'link' => [ 'url' => '#' ], + ], + [ + 'title' => esc_html__( 'Third item on the list', 'elementor-pro' ), + 'item_description' => esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), + 'price' => '$32', + 'link' => [ 'url' => '#' ], + ], + ], + 'title_field' => '{{{ title }}}', + ] + ); + + $this->add_control( + 'title_tag', + [ + 'label' => esc_html__( 'Title HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + 'p' => 'p', + ], + 'default' => 'span', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'description_tag', + [ + 'label' => esc_html__( 'Description HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + 'p' => 'p', + ], + 'default' => 'p', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_list_style', + [ + 'label' => esc_html__( 'List', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'heading__title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'heading_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-list-header' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'heading_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-price-list-header', + ] + ); + + $this->add_group_control( + Group_Control_Text_Stroke::get_type(), + [ + 'name' => 'text_stroke', + 'selector' => '{{WRAPPER}} .elementor-price-list-header', + ] + ); + + $this->add_control( + 'price_title', + [ + 'label' => __( 'Price', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'price_color', + [ + 'label' => __( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-list-price' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'price_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-price-list-price', + ] + ); + + $this->add_control( + 'heading_item_description', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'description_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-list-description' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'description_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-price-list-description', + ] + ); + + $this->add_control( + 'heading_separator', + [ + 'label' => esc_html__( 'Separator', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'separator_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'solid' => esc_html__( 'Solid', 'elementor-pro' ), + 'dotted' => esc_html__( 'Dotted', 'elementor-pro' ), + 'dashed' => esc_html__( 'Dashed', 'elementor-pro' ), + 'double' => esc_html__( 'Double', 'elementor-pro' ), + 'none' => esc_html__( 'None', 'elementor-pro' ), + ], + 'default' => 'dotted', + 'render_type' => 'template', + 'selectors' => [ + '{{WRAPPER}} .elementor-price-list-separator' => 'border-bottom-style: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'separator_weight', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 10, + ], + 'em' => [ + 'max' => 1, + ], + 'rem' => [ + 'max' => 1, + ], + ], + 'condition' => [ + 'separator_style!' => 'none', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-list-separator' => 'border-bottom-width: {{SIZE}}{{UNIT}}', + ], + 'default' => [ + 'size' => 2, + ], + ] + ); + + $this->add_control( + 'separator_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-list-separator' => 'border-bottom-color: {{VALUE}};', + ], + 'condition' => [ + 'separator_style!' => 'none', + ], + ] + ); + + $this->add_control( + 'separator_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 40, + ], + 'em' => [ + 'max' => 4, + ], + 'rem' => [ + 'max' => 4, + ], + ], + 'condition' => [ + 'separator_style!' => 'none', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-list-separator' => 'margin-left: {{SIZE}}{{UNIT}}; margin-right: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_image_style', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'show_label' => false, + ] + ); + + $this->add_group_control( + Group_Control_Image_Size::get_type(), + [ + 'name' => 'image_size', + 'default' => 'thumbnail', + ] + ); + + $this->add_control( + 'border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-list-image img' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'image_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + 'body.rtl {{WRAPPER}} .elementor-price-list-image' => 'padding-left: calc({{SIZE}}{{UNIT}}/2);', + 'body.rtl {{WRAPPER}} .elementor-price-list-image + .elementor-price-list-text' => 'padding-right: calc({{SIZE}}{{UNIT}}/2);', + 'body:not(.rtl) {{WRAPPER}} .elementor-price-list-image' => 'padding-right: calc({{SIZE}}{{UNIT}}/2);', + 'body:not(.rtl) {{WRAPPER}} .elementor-price-list-image + .elementor-price-list-text' => 'padding-left: calc({{SIZE}}{{UNIT}}/2);', + ], + 'default' => [ + 'size' => 20, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_item_style', + [ + 'label' => esc_html__( 'Item', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'show_label' => false, + ] + ); + + $this->add_control( + 'row_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-list li:not(:last-child)' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + 'default' => [ + 'size' => 20, + ], + ] + ); + + $this->add_control( + 'vertical_align', + [ + 'label' => esc_html__( 'Vertical Align', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-list-item' => 'align-items: {{VALUE}};', + ], + 'selectors_dictionary' => [ + 'top' => 'flex-start', + 'bottom' => 'flex-end', + ], + 'default' => 'top', + ] + ); + + $this->end_controls_section(); + } + + private function render_image( $item, $instance ) { + $image_id = $item['image']['id']; + $image_size = $instance['image_size_size']; + if ( 'custom' === $image_size ) { + $image_src = Group_Control_Image_Size::get_attachment_image_src( $image_id, 'image_size', $instance ); + } else { + $image_src = wp_get_attachment_image_src( $image_id, $image_size ); + $image_src = $image_src[0]; + } + + return sprintf( + '%s', + esc_url( $image_src ), + esc_attr( wp_kses_post( $item['title'] ) ) + ); + } + + private function render_item_header( $item ) { + $url = $item['link']['url']; + + $item_id = $item['_id']; + + if ( $url ) { + $unique_link_id = 'item-link-' . $item_id; + + $this->add_render_attribute( $unique_link_id, 'class', 'elementor-price-list-item' ); + + $this->add_link_attributes( $unique_link_id, $item['link'] ); + + return '
  • get_render_attribute_string( $unique_link_id ) . '>'; + } else { + return '
  • '; + } + } + + private function render_item_footer( $item ) { + if ( $item['link']['url'] ) { + return '
  • '; + } else { + return ''; + } + } + + protected function render() { + $settings = $this->get_settings_for_display(); ?> + +
      + + $item ) : ?> + get_repeater_setting_key( 'title', 'price_list', $index ); + $description_repeater_setting_key = $this->get_repeater_setting_key( 'item_description', 'price_list', $index ); + $this->add_inline_editing_attributes( $title_repeater_setting_key ); + $this->add_inline_editing_attributes( $description_repeater_setting_key ); + $this->add_render_attribute( $title_repeater_setting_key, 'class', 'elementor-price-list-title' ); + $this->add_render_attribute( $description_repeater_setting_key, 'class', 'elementor-price-list-description' ); + ?> + render_item_header( $item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> + +
      + render_image( $item, $settings ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> +
      + + +
      + +
      + + < print_render_attribute_string( $title_repeater_setting_key ); ?>> + print_unescaped_setting( 'title', 'price_list', $index ); ?> + > + + + + + + print_unescaped_setting( 'price', 'price_list', $index ); ?> + +
      + + + < print_render_attribute_string( $description_repeater_setting_key ); ?>> + print_unescaped_setting( 'item_description', 'price_list', $index ); ?> + > + +
      + render_item_footer( $item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> + + + +
    + + +
      + <# + var titleTag = elementor.helpers.validateHTMLTag( settings.title_tag ); + var descriptionTag = elementor.helpers.validateHTMLTag( settings.description_tag ); + + for ( var i in settings.price_list ) { + var item = settings.price_list[i], + item_open_wrap = '
    • ', + item_close_wrap = '
    • '; + if ( item.link.url ) { + item_open_wrap = '
    • '; + item_close_wrap = '
    • '; + } + + if ( ! _.isEmpty( item.title ) || ! _.isEmpty( item.price ) || ! _.isEmpty( item.description ) || ! _.isEmpty( item.image ) ) { #> + + {{{ item_open_wrap }}} + <# if ( item.image && item.image.id ) { + + var image = { + id: item.image.id, + url: item.image.url, + size: settings.image_size_size, + dimension: settings.image_size_custom_dimension, + model: view.getEditModel() + }; + + var image_url = elementor.imagesManager.getImageUrl( image ); + + if ( image_url ) { #> +
      {{ item.title }}
      + <# } #> + + <# } #> + + + <# if ( ! _.isEmpty( item.title ) || ! _.isEmpty( item.price ) || ! _.isEmpty( item.item_description ) ) { #> +
      + + <# if ( ! _.isEmpty( item.title ) || ! _.isEmpty( item.price ) ) { #> +
      + + <# if ( ! _.isEmpty( item.title ) ) { #> + <{{ titleTag }} class="elementor-price-list-title">{{{ item.title }}} + <# } #> + + <# if ( 'none' != settings.separator_style ) { #> + + <# } #> + + <# if ( ! _.isEmpty( item.price ) ) { #> + {{{ item.price }}} + <# } #> + +
      + <# } #> + + <# if ( ! _.isEmpty( item.item_description ) ) { #> + <{{descriptionTag}} class="elementor-price-list-description">{{{ item.item_description }}} + <# } #> + +
      + <# } #> + + {{{ item_close_wrap }}} + + <# } #> + <# } #> +
    + start_controls_section( + 'section_header', + [ + 'label' => esc_html__( 'Header', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'heading', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Enter your title', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'sub_heading', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Enter your description', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'heading_tag', + [ + 'label' => esc_html__( 'Title HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + ], + 'default' => 'h3', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_pricing', + [ + 'label' => esc_html__( 'Pricing', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'currency_symbol', + [ + 'label' => esc_html__( 'Currency Symbol', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'dollar' => '$ ' . _x( 'Dollar', 'Currency', 'elementor-pro' ), + 'euro' => '€ ' . _x( 'Euro', 'Currency', 'elementor-pro' ), + 'baht' => '฿ ' . _x( 'Baht', 'Currency', 'elementor-pro' ), + 'franc' => '₣ ' . _x( 'Franc', 'Currency', 'elementor-pro' ), + 'guilder' => 'ƒ ' . _x( 'Guilder', 'Currency', 'elementor-pro' ), + 'krona' => 'kr ' . _x( 'Krona', 'Currency', 'elementor-pro' ), + 'lira' => '₤ ' . _x( 'Lira', 'Currency', 'elementor-pro' ), + 'peseta' => '₧ ' . _x( 'Peseta', 'Currency', 'elementor-pro' ), + 'peso' => '₱ ' . _x( 'Peso', 'Currency', 'elementor-pro' ), + 'pound' => '£ ' . _x( 'Pound Sterling', 'Currency', 'elementor-pro' ), + 'real' => 'R$ ' . _x( 'Real', 'Currency', 'elementor-pro' ), + 'ruble' => '₽ ' . _x( 'Ruble', 'Currency', 'elementor-pro' ), + 'rupee' => '₨ ' . _x( 'Rupee', 'Currency', 'elementor-pro' ), + 'indian_rupee' => '₹ ' . _x( 'Rupee (Indian)', 'Currency', 'elementor-pro' ), + 'shekel' => '₪ ' . _x( 'Shekel', 'Currency', 'elementor-pro' ), + 'yen' => '¥ ' . _x( 'Yen/Yuan', 'Currency', 'elementor-pro' ), + 'won' => '₩ ' . _x( 'Won', 'Currency', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'dollar', + ] + ); + + $this->add_control( + 'currency_symbol_custom', + [ + 'label' => esc_html__( 'Custom Symbol', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'condition' => [ + 'currency_symbol' => 'custom', + ], + ] + ); + + $this->add_control( + 'price', + [ + 'label' => esc_html__( 'Price', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '39.99', + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'currency_format', + [ + 'label' => esc_html__( 'Currency Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => '1,234.56 (Default)', + ',' => '1.234,56', + ], + ] + ); + + $this->add_control( + 'sale', + [ + 'label' => esc_html__( 'Sale', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'default' => '', + ] + ); + + $this->add_control( + 'original_price', + [ + 'label' => esc_html__( 'Original Price', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => '59', + 'condition' => [ + 'sale' => 'yes', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'period', + [ + 'label' => esc_html__( 'Period', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Monthly', 'elementor-pro' ), + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_features', + [ + 'label' => esc_html__( 'Features', 'elementor-pro' ), + ] + ); + + $repeater = new Repeater(); + + $repeater->add_control( + 'item_text', + [ + 'label' => esc_html__( 'Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'default' => esc_html__( 'List Item', 'elementor-pro' ), + ] + ); + + $default_icon = [ + 'value' => 'far fa-check-circle', + 'library' => 'fa-regular', + ]; + + $repeater->add_control( + 'selected_item_icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'item_icon', + 'default' => $default_icon, + ] + ); + + $repeater->add_control( + 'item_icon_color', + [ + 'label' => esc_html__( 'Icon Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} i' => 'color: {{VALUE}}', + '{{WRAPPER}} {{CURRENT_ITEM}} svg' => 'fill: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'features_list', + [ + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'default' => [ + [ + 'item_text' => esc_html__( 'List Item #1', 'elementor-pro' ), + 'selected_item_icon' => $default_icon, + ], + [ + 'item_text' => esc_html__( 'List Item #2', 'elementor-pro' ), + 'selected_item_icon' => $default_icon, + ], + [ + 'item_text' => esc_html__( 'List Item #3', 'elementor-pro' ), + 'selected_item_icon' => $default_icon, + ], + ], + 'title_field' => '{{{ item_text }}}', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_footer', + [ + 'label' => esc_html__( 'Footer', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_text', + [ + 'label' => esc_html__( 'Button Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Click Here', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'default' => [ + 'url' => '#', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'footer_additional_info', + [ + 'label' => esc_html__( 'Additional Info', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => esc_html__( 'This is text element', 'elementor-pro' ), + 'rows' => 3, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_ribbon', + [ + 'label' => esc_html__( 'Ribbon', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'show_ribbon', + [ + 'label' => esc_html__( 'Show', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'ribbon_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Popular', 'elementor-pro' ), + 'condition' => [ + 'show_ribbon' => 'yes', + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'ribbon_horizontal_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'condition' => [ + 'show_ribbon' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_header_style', + [ + 'label' => esc_html__( 'Header', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'show_label' => false, + ] + ); + + $this->add_control( + 'header_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--e-price-table-header-background-color: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'header_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__header' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'heading_heading_style', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'heading_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__heading' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'heading_typography', + 'selector' => '{{WRAPPER}} .elementor-price-table__heading', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + ] + ); + + $this->add_control( + 'heading_sub_heading_style', + [ + 'label' => esc_html__( 'Sub Title', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sub_heading_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__subheading' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sub_heading_typography', + 'selector' => '{{WRAPPER}} .elementor-price-table__subheading', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_pricing_element_style', + [ + 'label' => esc_html__( 'Pricing', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'show_label' => false, + ] + ); + + $this->add_control( + 'pricing_element_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__price' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'pricing_element_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__price' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'price_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__currency, {{WRAPPER}} .elementor-price-table__integer-part, {{WRAPPER}} .elementor-price-table__fractional-part' => 'color: {{VALUE}}', + ], + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'price_typography', + // Targeting also the .elementor-price-table class in order to get a higher specificity from the inline CSS. + 'selector' => '{{WRAPPER}} .elementor-price-table .elementor-price-table__price', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + ] + ); + + $this->add_control( + 'heading_currency_style', + [ + 'label' => esc_html__( 'Currency Symbol', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'currency_symbol!' => '', + ], + ] + ); + + $this->add_control( + 'currency_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__price > .elementor-price-table__currency' => 'font-size: calc({{SIZE}}em/100)', + ], + 'condition' => [ + 'currency_symbol!' => '', + ], + ] + ); + + $this->add_control( + 'currency_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'before', + 'options' => [ + 'before' => [ + 'title' => esc_html__( 'Before', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'after' => [ + 'title' => esc_html__( 'After', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + ] + ); + + $this->add_control( + 'currency_vertical_position', + [ + 'label' => esc_html__( 'Vertical Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'middle' => [ + 'title' => esc_html__( 'Middle', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'default' => 'top', + 'selectors_dictionary' => [ + 'top' => 'flex-start', + 'middle' => 'center', + 'bottom' => 'flex-end', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__currency' => 'align-self: {{VALUE}}', + ], + 'condition' => [ + 'currency_symbol!' => '', + ], + ] + ); + + $this->add_control( + 'fractional_part_style', + [ + 'label' => esc_html__( 'Fractional Part', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'fractional-part_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__fractional-part' => 'font-size: calc({{SIZE}}em/100)', + ], + ] + ); + + $this->add_control( + 'fractional_part_vertical_position', + [ + 'label' => esc_html__( 'Vertical Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'middle' => [ + 'title' => esc_html__( 'Middle', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'default' => 'top', + 'selectors_dictionary' => [ + 'top' => 'flex-start', + 'middle' => 'center', + 'bottom' => 'flex-end', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__after-price' => 'justify-content: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'heading_original_price_style', + [ + 'label' => esc_html__( 'Original Price', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'sale' => 'yes', + 'original_price!' => '', + ], + ] + ); + + $this->add_control( + 'original_price_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__original-price' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'sale' => 'yes', + 'original_price!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'original_price_typography', + 'selector' => '{{WRAPPER}} .elementor-price-table__original-price', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'condition' => [ + 'sale' => 'yes', + 'original_price!' => '', + ], + ] + ); + + $this->add_control( + 'original_price_vertical_position', + [ + 'label' => esc_html__( 'Vertical Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'middle' => [ + 'title' => esc_html__( 'Middle', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'selectors_dictionary' => [ + 'top' => 'flex-start', + 'middle' => 'center', + 'bottom' => 'flex-end', + ], + 'default' => 'bottom', + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__original-price' => 'align-self: {{VALUE}}', + ], + 'condition' => [ + 'sale' => 'yes', + 'original_price!' => '', + ], + ] + ); + + $this->add_control( + 'heading_period_style', + [ + 'label' => esc_html__( 'Period', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'period!' => '', + ], + ] + ); + + $this->add_control( + 'period_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__period' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'period!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'period_typography', + 'selector' => '{{WRAPPER}} .elementor-price-table__period', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + 'condition' => [ + 'period!' => '', + ], + ] + ); + + $this->add_control( + 'period_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'label_block' => false, + 'options' => [ + 'below' => esc_html__( 'Below', 'elementor-pro' ), + 'beside' => esc_html__( 'Beside', 'elementor-pro' ), + ], + 'default' => 'below', + 'condition' => [ + 'period!' => '', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_features_list_style', + [ + 'label' => esc_html__( 'Features', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'show_label' => false, + ] + ); + + $this->add_control( + 'features_list_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'separator' => 'before', + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__features-list' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'features_list_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__features-list' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'features_list_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'separator' => 'before', + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__features-list' => '--e-price-table-features-list-color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'features_list_typography', + 'selector' => '{{WRAPPER}} .elementor-price-table__features-list li', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_control( + 'features_list_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__features-list' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'item_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + '%' => [ + 'min' => 25, + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__feature-inner' => 'margin-left: calc((100% - {{SIZE}}%)/2); margin-right: calc((100% - {{SIZE}}%)/2)', + ], + ] + ); + + $this->add_control( + 'list_divider', + [ + 'label' => esc_html__( 'Divider', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'divider_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'solid' => esc_html__( 'Solid', 'elementor-pro' ), + 'double' => esc_html__( 'Double', 'elementor-pro' ), + 'dotted' => esc_html__( 'Dotted', 'elementor-pro' ), + 'dashed' => esc_html__( 'Dashed', 'elementor-pro' ), + ], + 'default' => 'solid', + 'condition' => [ + 'list_divider' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__features-list li:before' => 'border-top-style: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ddd', + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'condition' => [ + 'list_divider' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__features-list li:before' => 'border-top-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'divider_weight', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 2, + ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 10, + ], + 'em' => [ + 'max' => 1, + ], + 'rem' => [ + 'max' => 1, + ], + ], + 'condition' => [ + 'list_divider' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__features-list li:before' => 'border-top-width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'divider_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'condition' => [ + 'list_divider' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__features-list li:before' => 'margin-left: calc((100% - {{SIZE}}%)/2); margin-right: calc((100% - {{SIZE}}%)/2)', + ], + ] + ); + + $this->add_control( + 'divider_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 15, + ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'condition' => [ + 'list_divider' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__features-list li:before' => 'margin-top: {{SIZE}}{{UNIT}}; margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_footer_style', + [ + 'label' => esc_html__( 'Footer', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'show_label' => false, + ] + ); + + $this->add_control( + 'footer_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__footer' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'footer_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__footer' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'heading_footer_button', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'md', + 'options' => [ + 'xs' => esc_html__( 'Extra Small', 'elementor-pro' ), + 'sm' => esc_html__( 'Small', 'elementor-pro' ), + 'md' => esc_html__( 'Medium', 'elementor-pro' ), + 'lg' => esc_html__( 'Large', 'elementor-pro' ), + 'xl' => esc_html__( 'Extra Large', 'elementor-pro' ), + ], + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_button_style' ); + + $this->start_controls_tab( + 'tab_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__button' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'button_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}} .elementor-price-table__button', + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'button_background', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-price-table__button', + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + 'color' => [ + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), [ + 'name' => 'button_border', + 'selector' => '{{WRAPPER}} .elementor-price-table__button', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__button' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'button_text_padding', + [ + 'label' => esc_html__( 'Text Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + 'condition' => [ + 'button_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_hover_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__button:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'button_background_hover', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-price-table__button:hover', + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + ], + ] + ); + + $this->add_control( + 'button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__button:hover' => 'border-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_hover_animation', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'heading_additional_info', + [ + 'label' => esc_html__( 'Additional Info', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'footer_additional_info!' => '', + ], + ] + ); + + $this->add_control( + 'additional_info_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__additional_info' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'footer_additional_info!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'additional_info_typography', + 'selector' => '{{WRAPPER}} .elementor-price-table__additional_info', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'condition' => [ + 'footer_additional_info!' => '', + ], + ] + ); + + $this->add_control( + 'additional_info_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'top' => 15, + 'right' => 30, + 'bottom' => 0, + 'left' => 30, + 'unit' => 'px', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__additional_info' => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + 'condition' => [ + 'footer_additional_info!' => '', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_ribbon_style', + [ + 'label' => esc_html__( 'Ribbon', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'show_label' => false, + 'condition' => [ + 'show_ribbon' => 'yes', + ], + ] + ); + + $this->add_control( + 'ribbon_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__ribbon-inner' => 'background-color: {{VALUE}}', + ], + ] + ); + + $ribbon_distance_transform = is_rtl() ? 'translateY(-50%) translateX({{SIZE}}{{UNIT}}) rotate(-45deg)' : 'translateY(-50%) translateX(-50%) translateX({{SIZE}}{{UNIT}}) rotate(-45deg)'; + + $this->add_responsive_control( + 'ribbon_distance', + [ + 'label' => esc_html__( 'Distance', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__ribbon-inner' => 'margin-top: {{SIZE}}{{UNIT}}; transform: ' . $ribbon_distance_transform, + ], + ] + ); + + $this->add_control( + 'ribbon_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'separator' => 'before', + 'selectors' => [ + '{{WRAPPER}} .elementor-price-table__ribbon-inner' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'ribbon_typography', + 'selector' => '{{WRAPPER}} .elementor-price-table__ribbon-inner', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_shadow', + 'selector' => '{{WRAPPER}} .elementor-price-table__ribbon-inner', + ] + ); + + $this->end_controls_section(); + } + + private function render_currency_symbol( $symbol, $location ) { + $currency_position = $this->get_settings( 'currency_position' ); + $location_setting = ! empty( $currency_position ) ? $currency_position : 'before'; + if ( ! empty( $symbol ) && $location === $location_setting ) { + echo '' . esc_html( $symbol ) . ''; + } + } + + private function get_currency_symbol( $symbol_name ) { + $symbols = [ + 'dollar' => '$', + 'euro' => '€', + 'franc' => '₣', + 'pound' => '£', + 'ruble' => '₽', + 'shekel' => '₪', + 'baht' => '฿', + 'yen' => '¥', + 'won' => '₩', + 'guilder' => 'ƒ', + 'peso' => '₱', + 'peseta' => '₧', + 'lira' => '₤', + 'rupee' => '₨', + 'indian_rupee' => '₹', + 'real' => 'R$', + 'krona' => 'kr', + ]; + + return isset( $symbols[ $symbol_name ] ) ? $symbols[ $symbol_name ] : ''; + } + + protected function render() { + $settings = $this->get_settings_for_display(); + $symbol = ''; + + if ( ! empty( $settings['currency_symbol'] ) ) { + if ( 'custom' !== $settings['currency_symbol'] ) { + $symbol = $this->get_currency_symbol( $settings['currency_symbol'] ); + } else { + $symbol = $settings['currency_symbol_custom']; + } + } + $currency_format = empty( $settings['currency_format'] ) ? '.' : $settings['currency_format']; + $price = explode( $currency_format, $settings['price'] ); + $intpart = $price[0]; + $fraction = ''; + if ( 2 === count( $price ) ) { + $fraction = $price[1]; + } + + $this->add_render_attribute( 'button_text', 'class', [ + 'elementor-price-table__button', + 'elementor-button', + 'elementor-size-' . $settings['button_size'], + ] ); + + if ( ! empty( $settings['link']['url'] ) ) { + $this->add_link_attributes( 'button_text', $settings['link'] ); + } + + if ( ! empty( $settings['button_hover_animation'] ) ) { + $this->add_render_attribute( 'button_text', 'class', 'elementor-animation-' . $settings['button_hover_animation'] ); + } + + $this->add_render_attribute( 'heading', 'class', 'elementor-price-table__heading' ); + $this->add_render_attribute( 'sub_heading', 'class', 'elementor-price-table__subheading' ); + $this->add_render_attribute( 'period', 'class', [ 'elementor-price-table__period', 'elementor-typo-excluded' ] ); + $this->add_render_attribute( 'footer_additional_info', 'class', 'elementor-price-table__additional_info' ); + $this->add_render_attribute( 'ribbon_title', 'class', 'elementor-price-table__ribbon-inner' ); + + $this->add_inline_editing_attributes( 'heading', 'none' ); + $this->add_inline_editing_attributes( 'sub_heading', 'none' ); + $this->add_inline_editing_attributes( 'period', 'none' ); + $this->add_inline_editing_attributes( 'footer_additional_info' ); + $this->add_inline_editing_attributes( 'button_text' ); + $this->add_inline_editing_attributes( 'ribbon_title' ); + + $period_position = $settings['period_position']; + $period_element = 'get_render_attribute_string( 'period' ) . '>' . $settings['period'] . ''; + $heading_tag = Utils::validate_html_tag( $settings['heading_tag'] ); + + $migration_allowed = Icons_Manager::is_migration_allowed(); + ?> + +
    + +
    + + < print_render_attribute_string( 'heading' ); ?>> + print_unescaped_setting( 'heading' ); ?> + > + + + + print_render_attribute_string( 'sub_heading' ); ?>> + print_unescaped_setting( 'sub_heading' ); ?> + + +
    + + +
    + +
    + render_currency_symbol( $symbol, 'before' ); + $this->print_unescaped_setting( 'original_price' ); + $this->render_currency_symbol( $symbol, 'after' ); + ?> +
    + + render_currency_symbol( $symbol, 'before' ); ?> + + + + + + + +
    + + + + + + + +
    + + + render_currency_symbol( $symbol, 'after' ); ?> + + + + +
    + + +
      + $item ) : + $repeater_setting_key = $this->get_repeater_setting_key( 'item_text', 'features_list', $index ); + $this->add_inline_editing_attributes( $repeater_setting_key ); + + $migrated = isset( $item['__fa4_migrated']['selected_item_icon'] ); + // add old default + if ( ! isset( $item['item_icon'] ) && ! $migration_allowed ) { + $item['item_icon'] = 'fa fa-check-circle'; + } + $is_new = ! isset( $item['item_icon'] ) && $migration_allowed; + ?> +
    • +
      + 'true' ] ); + else : ?> + + + + print_render_attribute_string( $repeater_setting_key ); ?>> + print_unescaped_setting( 'item_text', 'features_list', $index ); ?> + + +
      +
    • + +
    + + + + + +
    + + add_render_attribute( 'ribbon-wrapper', 'class', 'elementor-price-table__ribbon' ); + + if ( ! empty( $settings['ribbon_horizontal_position'] ) ) : + $this->add_render_attribute( 'ribbon-wrapper', 'class', 'elementor-ribbon-' . $settings['ribbon_horizontal_position'] ); + endif; + + ?> +
    print_render_attribute_string( 'ribbon-wrapper' ); ?>> +
    print_render_attribute_string( 'ribbon_title' ); ?>> + print_unescaped_setting( 'ribbon_title' ); ?> +
    +
    + + <# + var symbols = { + dollar: '$', + euro: '€', + franc: '₣', + pound: '£', + ruble: '₽', + shekel: '₪', + baht: '฿', + yen: '¥', + won: '₩', + guilder: 'ƒ', + peso: '₱', + peseta: '₧', + lira: '₤', + rupee: '₨', + indian_rupee: '₹', + real: 'R$', + krona: 'kr' + }; + + var symbol = '', + iconsHTML = {}; + + if ( settings.currency_symbol ) { + if ( 'custom' !== settings.currency_symbol ) { + symbol = symbols[ settings.currency_symbol ] || ''; + } else { + symbol = settings.currency_symbol_custom; + } + } + + var buttonClasses = 'elementor-price-table__button elementor-button elementor-size-' + settings.button_size; + + if ( settings.button_hover_animation ) { + buttonClasses += ' elementor-animation-' + settings.button_hover_animation; + } + + view.addRenderAttribute( 'heading', 'class', 'elementor-price-table__heading' ); + view.addRenderAttribute( 'sub_heading', 'class', 'elementor-price-table__subheading' ); + view.addRenderAttribute( 'period', 'class', ['elementor-price-table__period', 'elementor-typo-excluded'] ); + view.addRenderAttribute( 'footer_additional_info', 'class', 'elementor-price-table__additional_info' ); + view.addRenderAttribute( 'button_text', 'class', buttonClasses ); + view.addRenderAttribute( 'ribbon_title', 'class', 'elementor-price-table__ribbon-inner' ); + + view.addInlineEditingAttributes( 'heading', 'none' ); + view.addInlineEditingAttributes( 'sub_heading', 'none' ); + view.addInlineEditingAttributes( 'period', 'none' ); + view.addInlineEditingAttributes( 'footer_additional_info' ); + view.addInlineEditingAttributes( 'button_text' ); + view.addInlineEditingAttributes( 'ribbon_title' ); + + var currencyFormat = settings.currency_format || '.', + price = settings.price.split( currencyFormat ), + intpart = price[0], + fraction = price[1], + + periodElement = '' + settings.period + ''; + + #> +
    + <# if ( settings.heading || settings.sub_heading ) { #> +
    + <# if ( settings.heading ) { #> + <# var headingTag = elementor.helpers.validateHTMLTag( settings.heading_tag ) #> + <{{ headingTag }} {{{ view.getRenderAttributeString( 'heading' ) }}}>{{{ settings.heading }}} + <# } #> + <# if ( settings.sub_heading ) { #> + {{{ settings.sub_heading }}} + <# } #> +
    + <# } #> + +
    + <# if ( settings.sale && settings.original_price ) { #> +
    + <# if ( ! _.isEmpty( symbol ) && ( 'before' == settings.currency_position || _.isEmpty( settings.currency_position ) ) ) { #> + {{{ symbol }}}{{{ settings.original_price }}} + <# } #> + <# + /* The duplicate usage of the original price setting in the "if blocks" is to avoid whitespace between the number and the symbol. */ + if ( _.isEmpty( symbol ) ) { + #> + {{{ settings.original_price }}} + <# } #> + <# if ( ! _.isEmpty( symbol ) && 'after' == settings.currency_position ) { #> + {{{ settings.original_price }}}{{{ symbol }}} + <# } #> +
    + <# } #> + + <# if ( ! _.isEmpty( symbol ) && ( 'before' == settings.currency_position || _.isEmpty( settings.currency_position ) ) ) { #> + {{{ symbol }}} + <# } #> + <# if ( intpart ) { #> + {{{ intpart }}} + <# } #> +
    + <# if ( fraction ) { #> + {{{ fraction }}} + <# } #> + <# if ( settings.period && 'beside' === settings.period_position ) { #> + {{{ periodElement }}} + <# } #> +
    + + <# if ( ! _.isEmpty( symbol ) && 'after' == settings.currency_position ) { #> + {{{ symbol }}} + <# } #> + + <# if ( settings.period && 'below' === settings.period_position ) { #> + {{{ periodElement }}} + <# } #> +
    + + <# if ( settings.features_list ) { #> +
      + <# _.each( settings.features_list, function( item, index ) { + + var featureKey = view.getRepeaterSettingKey( 'item_text', 'features_list', index ), + migrated = elementor.helpers.isIconMigrated( item, 'selected_item_icon' ); + + view.addInlineEditingAttributes( featureKey ); #> + +
    • +
      + <# if ( item.item_icon || item.selected_item_icon ) { + iconsHTML[ index ] = elementor.helpers.renderIcon( view, item.selected_item_icon, { 'aria-hidden': 'true' }, 'i', 'object' ); + if ( ( ! item.item_icon || migrated ) && iconsHTML[ index ] && iconsHTML[ index ].rendered ) { #> + {{{ iconsHTML[ index ].value }}} + <# } else { #> + + <# } + } #> + <# if ( ! _.isEmpty( item.item_text.trim() ) ) { #> + {{{ item.item_text }}} + <# } else { #> +   + <# } #> +
      +
    • + <# } ); #> +
    + <# } #> + + <# if ( settings.button_text || settings.footer_additional_info ) { #> + + <# } #> +
    + + <# if ( 'yes' === settings.show_ribbon && settings.ribbon_title ) { + var ribbonClasses = 'elementor-price-table__ribbon'; + if ( settings.ribbon_horizontal_position ) { + ribbonClasses += ' elementor-ribbon-' + settings.ribbon_horizontal_position; + } #> +
    +
    {{{ settings.ribbon_title }}}
    +
    + <# } #> + start_controls_section( + 'section_content_scrolling_tracker', + [ + 'label' => esc_html__( 'Progress Tracker', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_control( + 'type', + [ + 'label' => esc_html__( 'Tracker Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'frontend_available' => true, + 'options' => [ + 'horizontal' => esc_html__( 'Horizontal', 'elementor-pro' ), + 'circular' => esc_html__( 'Circular', 'elementor-pro' ), + ], + 'default' => 'horizontal', + ] + ); + + $this->add_control( + 'relative_to', + [ + 'label' => esc_html__( 'Progress relative to', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'frontend_available' => true, + 'options' => [ + 'entire_page' => esc_html__( 'Entire Page', 'elementor-pro' ), + 'post_content' => esc_html__( 'Post Content', 'elementor-pro' ), + 'selector' => esc_html__( 'Selector', 'elementor-pro' ), + ], + 'default' => 'entire_page', + ] + ); + + $this->add_control( + 'selector', + [ + 'label' => esc_html__( 'Selector', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'description' => esc_html__( 'Add the CSS ID or Class of a specific element on this page to track its progress separately', 'elementor-pro' ), + 'frontend_available' => true, + 'ai' => [ + 'active' => false, + ], + 'condition' => [ + 'relative_to' => 'selector', + ], + 'placeholder' => '#id, .class', + ] + ); + + $this->add_control( + 'relative_to_description', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'Note: You can only track progress relative to Post Content on a single post template.', 'elementor-pro' ), + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'relative_to' => 'post_content', + ], + ] + ); + + $this->add_control( + 'direction', + [ + 'label' => esc_html__( 'Direction', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'ltr' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'rtl' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'render_type' => 'template', + 'frontend_available' => true, + 'selectors' => [ + '{{WRAPPER}}' => '--direction: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'percentage', + [ + 'label' => esc_html__( 'Percentage', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'no', + 'frontend_available' => true, + ] + ); + + $this->add_responsive_control( + 'percentage_position', + [ + 'label' => esc_html__( 'Percentage Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'rtl' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'ltr' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'condition' => [ + 'type' => 'horizontal', + 'percentage' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--text-direction: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + } + + private function register_tracker_style_controls() { + $this->start_controls_section( + 'section_style_scrolling_tracker', + [ + 'label' => esc_html__( 'Tracker', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'circular_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--circular-width: {{SIZE}}{{UNIT}}; --circular-height: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'type' => 'circular', + ], + ] + ); + + $this->add_control( + 'heading_progress_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Progress Indicator', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'circular_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--circular-color: {{VALUE}}', + ], + 'condition' => [ + 'type' => 'circular', + ], + ] + ); + + $this->add_responsive_control( + 'circular_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 400, + ], + 'em' => [ + 'max' => 40, + ], + 'rem' => [ + 'max' => 40, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--circular-progress-width: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'type' => 'circular', + ], + ] + ); + + $this->add_responsive_control( + 'align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'condition' => [ + 'type' => 'circular', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'horizontal_color', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .current-progress', + 'condition' => [ + 'type' => 'horizontal', + ], + 'fields_options' => [ + 'background' => [ + 'label' => esc_html__( 'Progress Color', 'elementor-pro' ), + ], + ], + ] + ); + + $this->add_control( + 'horizontal_border_style', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'none', + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'solid' => _x( 'Solid', 'Border Control', 'elementor-pro' ), + 'double' => _x( 'Double', 'Border Control', 'elementor-pro' ), + 'dotted' => _x( 'Dotted', 'Border Control', 'elementor-pro' ), + 'dashed' => _x( 'Dashed', 'Border Control', 'elementor-pro' ), + 'groove' => _x( 'Groove', 'Border Control', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}}' => '--horizontal-progress-border: {{VALUE}};', + ], + 'condition' => [ + 'type' => 'horizontal', + ], + ] + ); + + $this->add_responsive_control( + 'horizontal_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--horizontal-progress-border-top-width: {{TOP}}{{UNIT}}; --horizontal-progress-border-right-width: {{RIGHT}}{{UNIT}}; --horizontal-progress-border-bottom-width: {{BOTTOM}}{{UNIT}}; --horizontal-progress-border-left-width: {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'horizontal_border_style!' => 'none', + 'type' => 'horizontal', + ], + ] + ); + + $this->add_control( + 'horizontal_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--horizontal-progress-border-color: {{VALUE}}', + ], + 'condition' => [ + 'horizontal_border_style!' => 'none', + 'type' => 'horizontal', + ], + ] + ); + + $this->add_responsive_control( + 'horizontal_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--progress-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'type' => 'horizontal', + ], + ] + ); + + $this->add_control( + 'heading_tracker_background_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Tracker Background', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'circular_background_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--circular-background-color: {{VALUE}}', + ], + 'condition' => [ + 'type' => 'circular', + ], + ] + ); + + $this->add_responsive_control( + 'circular_background_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 400, + ], + 'em' => [ + 'max' => 40, + ], + 'rem' => [ + 'max' => 40, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--circular-background-width: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'type' => 'circular', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'horizontal_background_color', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-scrolling-tracker-horizontal', + 'fields_options' => [ + 'background' => [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + ], + ], + 'condition' => [ + 'type' => 'horizontal', + ], + ] + ); + + $this->add_responsive_control( + 'horizontal_height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--horizontal-height: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'type' => 'horizontal', + ], + ] + ); + + $this->add_control( + 'horizontal_tracker_border_style', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'none', + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'solid' => _x( 'Solid', 'Border Control', 'elementor-pro' ), + 'double' => _x( 'Double', 'Border Control', 'elementor-pro' ), + 'dotted' => _x( 'Dotted', 'Border Control', 'elementor-pro' ), + 'dashed' => _x( 'Dashed', 'Border Control', 'elementor-pro' ), + 'groove' => _x( 'Groove', 'Border Control', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}}' => '--horizontal-border-style: {{VALUE}};', + ], + 'condition' => [ + 'type' => 'horizontal', + ], + ] + ); + + $this->add_responsive_control( + 'horizontal_tracker_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--horizontal-border-top-width: {{TOP}}{{UNIT}}; --horizontal-border-right-width: {{RIGHT}}{{UNIT}}; --horizontal-border-bottom-width: {{BOTTOM}}{{UNIT}}; --horizontal-border-left-width: {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'horizontal_tracker_border_style!' => 'none', + 'type' => 'horizontal', + ], + ] + ); + + $this->add_control( + 'horizontal_tracker_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--horizontal-border-color: {{VALUE}}', + ], + 'condition' => [ + 'horizontal_tracker_border_style!' => 'none', + 'type' => 'horizontal', + ], + ] + ); + + $this->add_responsive_control( + 'horizontal_tracker_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'type' => 'horizontal', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_shadow', + 'selector' => '{{WRAPPER}} .elementor-scrolling-tracker', + 'condition' => [ + 'type' => 'horizontal', + ], + ] + ); + + $this->add_responsive_control( + 'horizontal_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--tracker-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'type' => 'horizontal', + ], + ] + ); + + $this->end_controls_section(); + } + + private function register_content_style_controls() { + $this->start_controls_section( + 'section__content_style_scrolling_tracker', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'percentage' => 'yes', + ], + ] + ); + + $this->add_control( + 'heading_percentage_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Percentage', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'percentage_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--percentage-color: {{VALUE}}', + ], + 'frontend_available' => true, + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'percentage_typography', + 'selector' => '{{WRAPPER}} .current-progress-percentage', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'percentage_text_shadow', + 'selector' => '{{WRAPPER}} .current-progress-percentage', + ] + ); + + $this->end_controls_section(); + } + + protected function register_controls() { + $this->register_content_controls(); + $this->register_tracker_style_controls(); + $this->register_content_style_controls(); + } + + public function render_plain_content() {} + + protected function render() { + $settings = $this->get_settings_for_display(); + $horizontal = 'horizontal' === $settings['type']; + $this->add_render_attribute( 'scrolling-percentage', 'class', 'current-progress-percentage' ); + $this->add_render_attribute( 'scrolling-tracker', 'class', [ + 'elementor-scrolling-tracker', + 'elementor-scrolling-tracker-' . $settings['type'], + 'elementor-scrolling-tracker-alignment-' . $settings['align'], + ] ); ?> + +
    print_render_attribute_string( 'scrolling-tracker' ); ?>> + +
    +
    print_render_attribute_string( 'scrolling-percentage' ); ?>>
    +
    + + + + + + +
    print_render_attribute_string( 'scrolling-percentage' ); ?>>
    + +
    + widget = $widget; + $this->prefix = $group_query_name . '_'; + $this->query_args = $query_args; + + $settings = $this->widget->get_settings_for_display(); + $defaults = $this->get_query_defaults(); + + $this->widget_settings = wp_parse_args( $settings, $defaults ); + } + + /** + * 1) build query args + * 2) invoke callback to fine-tune query-args + * 3) generate WP_Query object + * 4) if no results & fallback is set, generate a new WP_Query with fallback args + * 5) return WP_Query + * + * @return \WP_Query + */ + public function get_query() { + $this->get_query_args(); + + $offset_control = $this->get_widget_settings( 'offset' ); + + $query_id = $this->get_widget_settings( 'query_id' ); + if ( ! empty( $query_id ) ) { + add_action( 'pre_get_posts', [ $this, 'pre_get_posts_query_filter' ] ); + } + + $post_type = $this->get_widget_settings( 'post_type' ); + + if ( 'by_id' !== $post_type && 0 < $offset_control ) { + /** + * @see https://codex.wordpress.org/Making_Custom_Queries_using_Offset_and_Pagination + */ + add_action( 'pre_get_posts', [ $this, 'fix_query_offset' ], 1 ); + add_filter( 'found_posts', [ $this, 'fix_query_found_posts' ], 1, 2 ); + } + + $query = new \WP_Query( $this->query_args );// SQL_CALC_FOUND_ROWS is used. + + remove_action( 'pre_get_posts', [ $this, 'pre_get_posts_query_filter' ] ); + remove_action( 'pre_get_posts', [ $this, 'fix_query_offset' ], 1 ); + remove_filter( 'found_posts', [ $this, 'fix_query_found_posts' ], 1 ); + + Module::add_to_avoid_list( wp_list_pluck( $query->posts, 'ID' ) ); + + /** + * Elementor Pro query results. + * + * Fires before the actual query is run. This hook allows developers + * to alter individual query results. + * + * @param \WP_Query $query An instance of WordPress query. + * @param Widget_Base $widget An instance of Elementor widget. + */ + do_action( 'elementor/query/query_results', $query, $this->widget ); + + return $query; + } + + protected function get_query_defaults() { + $defaults = [ + $this->prefix . 'post_type' => 'post', + $this->prefix . 'posts_ids' => [], + $this->prefix . 'orderby' => 'date', + $this->prefix . 'order' => 'desc', + $this->prefix . 'offset' => 0, + $this->prefix . 'posts_per_page' => 3, + ]; + + return $defaults; + } + + public function get_query_args() { + $post_type = $this->get_widget_settings( 'post_type' ); + + if ( 'current_query' === $post_type ) { + $current_query_vars = $GLOBALS['wp_query']->query_vars; + + /** + * Add custom paged option support. It's necessary for ajax and individual pagination support + */ + if ( ! empty( $this->query_args['has_custom_pagination'] ) && true === $this->query_args['has_custom_pagination'] ) { + $current_query_vars['paged'] = $this->query_args['paged']; + } + + /** + * Current query variables. + * + * Filters the query variables for the current query. This hook allows + * developers to alter those variables. + * + * @since 1.0.0 + * + * @param array $current_query_vars Current query variables. + */ + $current_query_vars = apply_filters( 'elementor/query/get_query_args/current_query', $current_query_vars ); + $this->query_args = $current_query_vars; + return $current_query_vars; + } + + $this->set_common_args(); + $this->set_order_args(); + $this->set_pagination_args(); + $this->set_post_include_args(); + + if ( 'by_id' !== $post_type ) { + + $this->set_post_exclude_args(); + $this->set_avoid_duplicates(); + $this->set_terms_args(); + $this->set_author_args(); + $this->set_date_args(); + } + + /** + * Current query arguments. + * + * Filters the query arguments for the current query. This hook allows + * developers to alter those arguments. + * + * @param array $query_args An array of WordPress query arguments. + * @param Widget_Base $widget An instance of Elementor widget. + */ + $this->query_args = apply_filters( 'elementor/query/query_args', $this->query_args, $this->widget ); + + return $this->query_args; + } + + protected function set_pagination_args() { + $this->set_query_arg( 'posts_per_page', $this->get_widget_settings( 'posts_per_page' ) ); + $sticky_post = $this->get_widget_settings( 'ignore_sticky_posts' ) ? true : false; + $this->set_query_arg( 'ignore_sticky_posts', $sticky_post ); + } + + protected function set_common_args() { + $this->query_args['post_status'] = 'publish'; // Hide drafts/private posts for admins + + $post_type = $this->get_widget_settings( 'post_type' ); + if ( 'by_id' === $post_type ) { + $post_types = Utils::get_public_post_types(); + $this->query_args['post_type'] = array_keys( $post_types ); + } else { + $this->query_args['post_type'] = $post_type; + } + } + + protected function set_post_include_args() { + + if ( 'by_id' === $this->get_widget_settings( 'post_type' ) ) { + + $this->set_query_arg( 'post__in', $this->get_widget_settings( 'posts_ids' ) ); + + if ( empty( $this->query_args['post__in'] ) ) { + // If no selection - return an empty query + $this->query_args['post__in'] = [ 0 ]; + } + } + } + + protected function set_post_exclude_args() { + + $exclude = $this->get_widget_settings( 'exclude' ); + + if ( empty( $exclude ) ) { + return; + } + + $post__not_in = []; + + if ( $this->maybe_in_array( 'current_post', $exclude ) ) { + if ( is_singular() ) { + $post__not_in[] = get_queried_object_id(); + } + } + + $exclude_ids = $this->get_widget_settings( 'exclude_ids' ); + if ( $this->maybe_in_array( 'manual_selection', $exclude ) && ! empty( $exclude_ids ) ) { + $post__not_in = array_merge( $post__not_in, $exclude_ids ); + } + + $this->set_query_arg( 'post__not_in', $post__not_in ); + } + + protected function set_avoid_duplicates() { + if ( 'yes' === $this->get_widget_settings( 'avoid_duplicates' ) ) { + $post__not_in = isset( $this->query_args['post__not_in'] ) ? $this->query_args['post__not_in'] : []; + $post__not_in = array_merge( $post__not_in, Module::$displayed_ids ); + $this->set_query_arg( 'post__not_in', $post__not_in, true ); + } + } + + protected function set_terms_args() { + + $post_type = $this->get_widget_settings( 'post_type' ); + + if ( 'by_id' === $post_type ) { + return; + } + $this->build_terms_query_include( 'include_term_ids' ); + $this->build_terms_query_exclude( 'exclude_term_ids' ); + } + + protected function build_terms_query_include( $control_id ) { + $this->build_terms_query( 'include', $control_id ); + } + + protected function build_terms_query_exclude( $control_id ) { + $this->build_terms_query( 'exclude', $control_id, true ); + } + + protected function build_terms_query( $tab_id, $control_id, $exclude = false ) { + $tab_id = $this->get_widget_settings( $tab_id ); + $settings_terms = $this->get_widget_settings( $control_id ); + if ( empty( $tab_id ) || empty( $settings_terms ) || ! $this->maybe_in_array( 'terms', $tab_id ) ) { + return; + } + + $terms = []; + + // Switch to term_id in order to get all term children (sub-categories): + foreach ( $settings_terms as $id ) { + $term_data = get_term_by( 'term_taxonomy_id', $id ); + if ( false !== $term_data ) { + $taxonomy = $term_data->taxonomy; + $terms[ $taxonomy ][] = $id; + } + } + $this->insert_tax_query( $terms, $exclude ); + } + + protected function insert_tax_query( $terms, $exclude ) { + $tax_query = []; + foreach ( $terms as $taxonomy => $ids ) { + $query = [ + 'taxonomy' => $taxonomy, + 'field' => 'term_taxonomy_id', + 'terms' => $ids, + ]; + + if ( $exclude ) { + $query['operator'] = 'NOT IN'; + } + + $tax_query[] = $query; + } + + if ( empty( $tax_query ) ) { + return; + } + + if ( empty( $this->query_args['tax_query'] ) ) { + $this->query_args['tax_query'] = $tax_query; + } else { + $this->query_args['tax_query']['relation'] = 'AND'; + $this->query_args['tax_query'][] = $tax_query; + } + } + + protected function set_author_args() { + + $include_authors = $this->get_widget_settings( 'include_authors' ); + if ( ! empty( $include_authors ) && $this->maybe_in_array( 'authors', $this->get_widget_settings( 'include' ) ) ) { + $this->set_query_arg( 'author__in', $include_authors ); + } + + $exclude_authors = $this->get_widget_settings( 'exclude_authors' ); + if ( ! empty( $exclude_authors ) && $this->maybe_in_array( 'authors', $this->get_widget_settings( 'exclude' ) ) ) { + //exclude only if not explicitly included + if ( empty( $this->query_args['author__in'] ) ) { + $this->set_query_arg( 'author__not_in', $exclude_authors ); + } + } + } + + protected function set_order_args() { + $order = $this->get_widget_settings( 'order' ); + if ( ! empty( $order ) ) { + $this->set_query_arg( 'orderby', $this->get_widget_settings( 'orderby' ) ); + $this->set_query_arg( 'order', $this->get_widget_settings( 'order' ) ); + } + } + + protected function set_date_args() { + + $select_date = $this->get_widget_settings( 'select_date' ); + if ( ! empty( $select_date ) ) { + $date_query = []; + switch ( $select_date ) { + case 'today': + $date_query['after'] = '-1 day'; + break; + case 'week': + $date_query['after'] = '-1 week'; + break; + case 'month': + $date_query['after'] = '-1 month'; + break; + case 'quarter': + $date_query['after'] = '-3 month'; + break; + case 'year': + $date_query['after'] = '-1 year'; + break; + case 'exact': + $after_date = $this->get_widget_settings( 'date_after' ); + if ( ! empty( $after_date ) ) { + $date_query['after'] = $after_date; + } + $before_date = $this->get_widget_settings( 'date_before' ); + if ( ! empty( $before_date ) ) { + $date_query['before'] = $before_date; + } + $date_query['inclusive'] = true; + break; + } + + $this->set_query_arg( 'date_query', $date_query ); + } + } + + /** + * @param string $control_name + * + * @return mixed|null + */ + protected function get_widget_settings( $control_name ) { + $control_name = $this->prefix . $control_name; + return isset( $this->widget_settings[ $control_name ] ) ? $this->widget_settings[ $control_name ] : null; + } + + /** + * @param $key + * @param $value + * @param false $override + */ + protected function set_query_arg( $key, $value, $override = false ) { + if ( ! isset( $this->query_args[ $key ] ) || $override ) { + $this->query_args[ $key ] = $value; + } + } + + /** + * @param string $value + * @param mixed $maybe_array + * + * @return bool + */ + protected function maybe_in_array( $value, $maybe_array ) { + return is_array( $maybe_array ) ? in_array( $value, $maybe_array, true ) : $value === $maybe_array; + } + + /** + * @param \WP_Query $wp_query + */ + public function pre_get_posts_query_filter( $wp_query ) { + if ( $this->widget ) { + $query_id = $this->get_widget_settings( 'query_id' ); + $widget_name = $this->widget->get_name(); + /** + * Elementor Pro query arguments. + * + * Fires before the actual query is run. This hook allows developers to + * alter individual queries by id. + * + * The dynamic portions of the hook name, `$widget_name` & `$query_id`, + * refers to the widget name and query id respectively. + * + * @since 2.1.0 + * + * @param \WP_Query $wp_query An instance of WordPress query. + * @param Widget_Base $widget An instance of Elementor widget + */ + do_action( "elementor/query/{$query_id}", $wp_query, $this->widget ); + } + } + + /** + * @param \WP_Query $query + */ + public function fix_query_offset( &$query ) { + $offset = $this->get_widget_settings( 'offset' ); + + if ( $offset && $query->is_paged ) { + $query->query_vars['offset'] = $offset + ( ( $query->query_vars['paged'] - 1 ) * $query->query_vars['posts_per_page'] ); + } else { + $query->query_vars['offset'] = $offset; + } + } + + /** + * @param int $found_posts + * @param \WP_Query $query + * + * @return int + */ + public function fix_query_found_posts( $found_posts, $query ) { + $offset = $this->get_widget_settings( 'offset' ); + + if ( $offset ) { + $found_posts -= $offset; + } + + return $found_posts; + } +} diff --git a/modules/query-control/classes/elementor-related-query.php b/modules/query-control/classes/elementor-related-query.php new file mode 100644 index 0000000..ba8e208 --- /dev/null +++ b/modules/query-control/classes/elementor-related-query.php @@ -0,0 +1,169 @@ +related_post_id = -1; + $this->fallback_args = $fallback_args; + } + + /** + * 1) build query args + * 2) invoke callback to fine-tune query-args + * 3) generate WP_Query object + * 4) if no results & fallback is set, generate a new WP_Query with fallback args + * 5) return WP_Query + * + * @return \WP_Query + */ + public function get_query() { + $query = parent::get_query(); + + if ( ! $query->post_count && $this->is_valid_fallback() ) { + $query = $this->get_fallback_query( $query ); + } + + return $query; + } + + protected function get_fallback_query( $original_query ) { + $this->set_fallback_query_args(); + $this->set_fallback_arg_by_settings( 'posts_per_page', $original_query->query_vars['posts_per_page'] ); + $this->set_fallback_arg_by_settings( 'post__not_in', $original_query->query_vars['post__not_in'] ); + + /** + * Fallback query arguments. + * + * Filters the query arguments for the fallback query. This hook allows + * developers to alter those arguments. + * + * @param array $fallback_args An array of WordPress query arguments. + * @param Widget_Base $widget An instance of Elementor widget. + */ + $this->fallback_args = apply_filters( 'elementor/query/fallback_query_args', $this->fallback_args, $this->widget ); + + return new \WP_Query( $this->fallback_args ); + } + + private function is_valid_fallback() { + $related_callback = $this->get_widget_settings( 'related_fallback' ); + if ( empty( $related_callback ) ) { + return false; + } + $valid = false; + switch ( $this->get_widget_settings( 'related_fallback' ) ) { + case 'fallback_recent': + $valid = true; + break; + case 'fallback_by_id': + $fallback_id = $this->get_widget_settings( 'fallback_ids' ); + if ( ! empty( $fallback_id ) ) { + $valid = true; + } + break; + } + + return $valid; + } + + protected function set_common_args() { + parent::set_common_args(); + $post_id = get_queried_object_id(); + $this->related_post_id = is_singular() && ( 0 !== $post_id ) ? $post_id : null; + $this->query_args['post_type'] = get_post_type( $this->related_post_id ); + } + + protected function set_post_exclude_args() { + parent::set_post_exclude_args(); + + if ( $this->related_post_id ) { + $post_not_in = isset( $this->query_args['post__not_in'] ) ? $this->query_args['post__not_in'] : []; + $post_not_in[] = $this->related_post_id; + $this->query_args['post__not_in'] = $post_not_in; + } + } + + protected function build_terms_query_include( $control_id ) { + /** + * Build tax_query for the "related posts" query: + * 1) find the list of taxonomies associated with the current-post + * 2) extract the ids for each taxonomy + * 3) build tax_query array accordingly + * + */ + if ( null === $this->get_widget_settings( 'include' ) || null === $this->get_widget_settings( 'related_taxonomies' ) || ! $this->maybe_in_array( 'terms', $this->get_widget_settings( 'include' ) ) ) { + return; + } + + $taxonomies = $this->get_widget_settings( 'related_taxonomies' ); + $terms = []; + if ( is_string( $taxonomies ) ) { + $terms[ $taxonomies ] = wp_get_post_terms( $this->related_post_id, $taxonomies, [ 'fields' => 'tt_ids' ] ); + } else { + foreach ( $taxonomies as $taxonomy ) { + $terms[ $taxonomy ] = wp_get_post_terms( $this->related_post_id, $taxonomy, [ 'fields' => 'tt_ids' ] ); + } + } + + $this->insert_tax_query( $terms, false ); + } + + protected function set_author_args() { + if ( ! $this->maybe_in_array( 'authors', $this->get_widget_settings( 'include' ) ) ) { + return; + } + + $this->query_args['author__in'] = get_post_field( 'post_author', $this->related_post_id ); + } + + /** + * @param string $key + * @param mixed $value + * @param string $control_name + */ + private function set_fallback_arg_by_settings( $key, $value, $control_name = '' ) { + if ( empty( $this->fallback_args[ $key ] ) ) { + $settings = $this->widget->get_settings(); + $this->fallback_args[ $key ] = ( '' === $control_name || empty( $settings[ $this->prefix . $control_name ] ) ) ? $value : $settings[ $this->prefix . $control_name ]; + } + } + + /** + * @return string|array + */ + public function get_post_types() { + $public_post_types = Utils::get_public_post_types(); + $active_post_type = get_post_type(); + + return ! empty( $active_post_type ) ? $active_post_type : array_keys( $public_post_types ); + } + + protected function set_fallback_query_args() { + $this->set_fallback_arg_by_settings( 'ignore_sticky_posts', true ); + $this->set_fallback_arg_by_settings( 'post_status', 'publish' ); + $this->set_fallback_arg_by_settings( 'post_type', $this->get_post_types() ); + + if ( 'fallback_by_id' === $this->get_widget_settings( 'related_fallback' ) ) { + $this->set_fallback_arg_by_settings( 'post__in', [ 0 ], 'fallback_ids' ); + $this->set_fallback_arg_by_settings( 'orderby', 'rand' ); + } else { //recent posts + $this->set_fallback_arg_by_settings( 'orderby', 'date' ); + $this->set_fallback_arg_by_settings( 'order', 'DESC' ); + } + } +} diff --git a/modules/query-control/controls/group-control-posts.php b/modules/query-control/controls/group-control-posts.php new file mode 100644 index 0000000..70ac162 --- /dev/null +++ b/modules/query-control/controls/group-control-posts.php @@ -0,0 +1,315 @@ + $label ) { + $taxonomy_filter_args = [ + 'show_in_nav_menus' => true, + 'object_type' => [ $post_type ], + ]; + + $taxonomies = get_taxonomies( $taxonomy_filter_args, 'objects' ); + + foreach ( $taxonomies as $taxonomy => $object ) { + unset( $element['settings'][ $control_id . '_' . $taxonomy . '_ids' ] ); + } + } + + return $element; + } + + protected function init_fields() { + $fields = []; + + $fields['post_type'] = [ + 'label' => esc_html__( 'Source', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + ]; + + $fields['posts_ids'] = [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => Module::QUERY_CONTROL_ID, + 'post_type' => '', + 'options' => [], + 'label_block' => true, + 'multiple' => true, + 'filter_type' => 'by_id', + 'condition' => [ + 'post_type' => 'by_id', + ], + 'export' => false, + ]; + + $fields['authors'] = [ + 'label' => esc_html__( 'Author', 'elementor-pro' ), + 'label_block' => true, + 'type' => Module::QUERY_CONTROL_ID, + 'multiple' => true, + 'default' => [], + 'options' => [], + 'filter_type' => 'author', + 'condition' => [ + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'export' => false, + ]; + + return $fields; + } + + protected function prepare_fields( $fields ) { + $args = $this->get_args(); + + $post_type_args = []; + if ( ! empty( $args['post_type'] ) ) { + $post_type_args['post_type'] = $args['post_type']; + } + + $post_types = Utils::get_public_post_types( $post_type_args ); + + $post_types_options = $post_types; + + $post_types_options['by_id'] = esc_html__( 'Manual Selection', 'elementor-pro' ); + $post_types_options['current_query'] = esc_html__( 'Current Query', 'elementor-pro' ); + + $fields['post_type']['options'] = $post_types_options; + + $fields['post_type']['default'] = key( $post_types ); + + $fields['posts_ids']['object_type'] = array_keys( $post_types ); + + $taxonomy_filter_args = [ + 'show_in_nav_menus' => true, + ]; + + $taxonomies = get_taxonomies( $taxonomy_filter_args, 'objects' ); + + // bypass bug in WP_List_Util::filter() causing wrong array comparison + // when a taxonomy belongs to several post-types (e.g. when using woocommerce-product-add-ons) + // ( using simple '==' rather than in_array() or array_intersect() ). + $filtered_taxonomies = []; + if ( ! empty( $args['post_type'] ) ) { + foreach ( $taxonomies as $taxonomy => $obj ) { + $tax_array = (array) $obj; + if ( in_array( $args['post_type'], $tax_array['object_type'] ) ) { + $filtered_taxonomies[ $taxonomy ] = $obj; + } + } + } else { + $filtered_taxonomies = $taxonomies; + } + + foreach ( $filtered_taxonomies as $taxonomy => $object ) { + $taxonomy_args = [ + 'label' => $object->label, + 'type' => Module::QUERY_CONTROL_ID, + 'label_block' => true, + 'multiple' => true, + 'object_type' => $taxonomy, + 'options' => [], + 'condition' => [ + 'post_type' => $object->object_type, + ], + 'export' => false, + ]; + + $count = wp_count_terms( $taxonomy ); + + $options = []; + + // For large websites, use Ajax to search + if ( $count > self::INLINE_MAX_RESULTS ) { + $taxonomy_args['type'] = Module::QUERY_CONTROL_ID; + + $taxonomy_args['filter_type'] = 'taxonomy'; + } else { + $taxonomy_args['type'] = Controls_Manager::SELECT2; + + $terms = get_terms( [ + 'taxonomy' => $taxonomy, + 'hide_empty' => false, + ] ); + + foreach ( $terms as $term ) { + $options[ $term->term_id ] = $term->name; + } + + $taxonomy_args['options'] = $options; + } + + $fields[ $taxonomy . '_ids' ] = $taxonomy_args; + } + + return parent::prepare_fields( $fields ); + } + + protected function get_default_options() { + return [ + 'popover' => false, + ]; + } + + protected function fix_offset( $query_args, $settings, $prefix = '' ) { + if ( 0 < $settings[ $prefix . 'offset' ] ) { + /** + * Due to a WordPress bug, the offset will be set later, in $this->fix_query_offset() + * @see https://codex.wordpress.org/Making_Custom_Queries_using_Offset_and_Pagination + */ + $query_args['offset_to_fix'] = $settings[ $prefix . 'offset' ]; + } + + return $query_args; + } + + protected function build_query_args( $settings, $control_id_prefix ) { + + $prefix = $control_id_prefix . '_'; + + $post_type = $settings[ $prefix . 'post_type' ]; + + $query_args = [ + 'orderby' => $settings['orderby'], + 'order' => $settings['order'], + 'ignore_sticky_posts' => 1, + 'post_status' => 'publish', // Hide drafts/private posts for admins + ]; + + if ( 'by_id' === $post_type ) { + $post_types = Utils::get_public_post_types(); + + $query_args['post_type'] = array_keys( $post_types ); + $query_args['posts_per_page'] = -1; + + $query_args['post__in'] = $settings[ $prefix . 'posts_ids' ]; + + if ( empty( $query_args['post__in'] ) ) { + // If no selection - return an empty query + $query_args['post__in'] = [ 0 ]; + } + } else { + $query_args['post_type'] = $post_type; + $query_args['posts_per_page'] = $settings['posts_per_page']; + $query_args['tax_query'] = []; + + $query_args = $this->fix_offset( $query_args, $settings ); + + $taxonomies = get_object_taxonomies( $post_type, 'objects' ); + + foreach ( $taxonomies as $object ) { + $setting_key = $prefix . $object->name . '_ids'; + + if ( ! empty( $settings[ $setting_key ] ) ) { + $query_args['tax_query'][] = [ + 'taxonomy' => $object->name, + 'field' => 'term_id', + 'terms' => $settings[ $setting_key ], + ]; + } + } + } + + if ( ! empty( $settings[ $prefix . 'authors' ] ) ) { + $query_args['author__in'] = $settings[ $prefix . 'authors' ]; + } + + $post__not_in = []; + if ( ! empty( $settings['exclude'] ) ) { + if ( in_array( 'current_post', $settings['exclude'], true ) ) { + $post_id = Utils::_unstable_get_super_global_value( $_REQUEST, 'post_id' ); + + if ( wp_doing_ajax() && $post_id ) { + $post__not_in[] = $post_id; + } elseif ( is_singular() ) { + $post__not_in[] = get_queried_object_id(); + } + } + + if ( in_array( 'manual_selection', $settings['exclude'], true ) && ! empty( $settings['exclude_ids'] ) ) { + $post__not_in = array_merge( $post__not_in, $settings['exclude_ids'] ); + } + } + + if ( ! empty( $settings['avoid_duplicates'] ) && 'yes' === $settings['avoid_duplicates'] ) { + $post__not_in = array_merge( $post__not_in, Module::$displayed_ids ); + } + + $query_args['post__not_in'] = $post__not_in; + + return $query_args; + } + + public function get_query_args( $control_id_prefix, $settings ) { + + $defaults = [ + $control_id_prefix . '_post_type' => 'post', + $control_id_prefix . '_posts_ids' => [], + 'orderby' => 'date', + 'order' => 'desc', + 'posts_per_page' => 3, + 'offset' => 0, + ]; + + $settings = wp_parse_args( $settings, $defaults ); + + $post_type = $settings[ $control_id_prefix . '_post_type' ]; + + if ( 'current_query' === $post_type ) { + $current_query_vars = $GLOBALS['wp_query']->query_vars; + + /** + * Current query variables. + * + * Filters the query variables for the current query. + * + * @since 1.0.0 + * + * @param array $current_query_vars Current query variables. + */ + $current_query_vars = apply_filters( 'elementor_pro/query_control/get_query_args/current_query', $current_query_vars ); + + return $current_query_vars; + } + + return $this->build_query_args( $settings, $control_id_prefix ); + } +} diff --git a/modules/query-control/controls/group-control-query.php b/modules/query-control/controls/group-control-query.php new file mode 100644 index 0000000..7d87025 --- /dev/null +++ b/modules/query-control/controls/group-control-query.php @@ -0,0 +1,568 @@ +get_args(); + static::$fields = $this->init_fields_by_name( $args['name'] ); + } + + protected function init_fields() { + $args = $this->get_args(); + + return $this->init_fields_by_name( $args['name'] ); + } + + protected function get_fields_array( $name ) { + $fields = []; + + $tab_keys = $this->get_tabs_keys( $name ); + $name .= '_'; + + $fields['post_type'] = [ + 'label' => esc_html__( 'Source', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'by_id' => esc_html__( 'Manual Selection', 'elementor-pro' ), + 'current_query' => esc_html__( 'Current Query', 'elementor-pro' ), + ], + ]; + + $fields['query_args'] = [ + 'type' => Controls_Manager::TABS, + ]; + + $tabs_wrapper = $tab_keys['tabs_wrapper']; + $include_wrapper = $tab_keys['include_wrapper']; + $exclude_wrapper = $tab_keys['exclude_wrapper']; + + $fields['query_include'] = [ + 'type' => Controls_Manager::TAB, + 'label' => esc_html__( 'Include', 'elementor-pro' ), + 'tabs_wrapper' => $tabs_wrapper, + 'condition' => [ + 'post_type!' => [ + 'current_query', + 'by_id', + ], + ], + ]; + + $fields['posts_ids'] = [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => Query_Module::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'multiple' => true, + 'autocomplete' => [ + 'object' => Query_Module::QUERY_OBJECT_POST, + ], + 'condition' => [ + 'post_type' => 'by_id', + ], + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_wrapper, + 'export' => false, + ]; + + $fields['include'] = [ + 'label' => esc_html__( 'Include By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'options' => [ + 'terms' => esc_html__( 'Term', 'elementor-pro' ), + 'authors' => esc_html__( 'Author', 'elementor-pro' ), + ], + 'condition' => [ + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'label_block' => true, + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_wrapper, + ]; + + $fields['include_term_ids'] = [ + 'label' => esc_html__( 'Term', 'elementor-pro' ), + 'description' => esc_html__( 'Terms are items in a taxonomy. The available taxonomies are: Categories, Tags, Formats and custom taxonomies.', 'elementor-pro' ), + 'type' => Query_Module::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'multiple' => true, + 'autocomplete' => [ + 'object' => Query_Module::QUERY_OBJECT_CPT_TAX, + 'display' => 'detailed', + ], + 'group_prefix' => $name, + 'condition' => [ + 'include' => 'terms', + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_wrapper, + ]; + + $fields['include_authors'] = [ + 'label' => esc_html__( 'Author', 'elementor-pro' ), + 'label_block' => true, + 'type' => Query_Module::QUERY_CONTROL_ID, + 'multiple' => true, + 'default' => [], + 'options' => [], + 'autocomplete' => [ + 'object' => Query_Module::QUERY_OBJECT_AUTHOR, + ], + 'condition' => [ + 'include' => 'authors', + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_wrapper, + 'export' => false, + ]; + + $fields['query_exclude'] = [ + 'type' => Controls_Manager::TAB, + 'label' => esc_html__( 'Exclude', 'elementor-pro' ), + 'tabs_wrapper' => $tabs_wrapper, + 'condition' => [ + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + ]; + + $fields['exclude'] = [ + 'label' => esc_html__( 'Exclude By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'options' => [ + 'current_post' => esc_html__( 'Current Post', 'elementor-pro' ), + 'manual_selection' => esc_html__( 'Manual Selection', 'elementor-pro' ), + 'terms' => esc_html__( 'Term', 'elementor-pro' ), + 'authors' => esc_html__( 'Author', 'elementor-pro' ), + ], + 'condition' => [ + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'label_block' => true, + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $exclude_wrapper, + ]; + + $fields['exclude_ids'] = [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => Query_Module::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'multiple' => true, + 'autocomplete' => [ + 'object' => Query_Module::QUERY_OBJECT_POST, + ], + 'condition' => [ + 'exclude' => 'manual_selection', + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $exclude_wrapper, + 'export' => false, + ]; + + $fields['exclude_term_ids'] = [ + 'label' => esc_html__( 'Term', 'elementor-pro' ), + 'type' => Query_Module::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'multiple' => true, + 'autocomplete' => [ + 'object' => Query_Module::QUERY_OBJECT_CPT_TAX, + 'display' => 'detailed', + ], + 'group_prefix' => $name, + 'condition' => [ + 'exclude' => 'terms', + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $exclude_wrapper, + 'export' => false, + ]; + + $fields['exclude_authors'] = [ + 'label' => esc_html__( 'Author', 'elementor-pro' ), + 'type' => Query_Module::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'multiple' => true, + 'autocomplete' => [ + 'object' => Query_Module::QUERY_OBJECT_AUTHOR, + 'display' => 'detailed', + ], + 'condition' => [ + 'exclude' => 'authors', + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $exclude_wrapper, + 'export' => false, + ]; + + $fields['avoid_duplicates'] = [ + 'label' => esc_html__( 'Avoid Duplicates', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'description' => esc_html__( 'Set to Yes to avoid duplicate posts from showing up. This only effects the frontend.', 'elementor-pro' ), + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $exclude_wrapper, + 'condition' => [ + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + ]; + + $fields['offset'] = [ + 'label' => esc_html__( 'Offset', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 0, + 'condition' => [ + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'description' => esc_html__( 'Use this setting to skip over posts (e.g. \'2\' to skip over 2 posts).', 'elementor-pro' ), + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $exclude_wrapper, + ]; + + $fields['select_date'] = [ + 'label' => esc_html__( 'Date', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'post_type' => '', + 'options' => [ + 'anytime' => esc_html__( 'All', 'elementor-pro' ), + 'today' => esc_html__( 'Past Day', 'elementor-pro' ), + 'week' => esc_html__( 'Past Week', 'elementor-pro' ), + 'month' => esc_html__( 'Past Month', 'elementor-pro' ), + 'quarter' => esc_html__( 'Past Quarter', 'elementor-pro' ), + 'year' => esc_html__( 'Past Year', 'elementor-pro' ), + 'exact' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'anytime', + 'multiple' => false, + 'condition' => [ + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'separator' => 'before', + ]; + + $fields['date_before'] = [ + 'label' => esc_html__( 'Before', 'elementor-pro' ), + 'type' => Controls_Manager::DATE_TIME, + 'post_type' => '', + 'label_block' => false, + 'multiple' => false, + 'placeholder' => esc_html__( 'Choose', 'elementor-pro' ), + 'condition' => [ + 'select_date' => 'exact', + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'description' => esc_html__( 'Setting a ‘Before’ date will show all the posts published until the chosen date (inclusive).', 'elementor-pro' ), + ]; + + $fields['date_after'] = [ + 'label' => esc_html__( 'After', 'elementor-pro' ), + 'type' => Controls_Manager::DATE_TIME, + 'post_type' => '', + 'label_block' => false, + 'multiple' => false, + 'placeholder' => esc_html__( 'Choose', 'elementor-pro' ), + 'condition' => [ + 'select_date' => 'exact', + 'post_type!' => [ + 'by_id', + 'current_query', + ], + ], + 'description' => esc_html__( 'Setting an ‘After’ date will show all the posts published since the chosen date (inclusive).', 'elementor-pro' ), + ]; + + $fields['orderby'] = [ + 'label' => esc_html__( 'Order By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'post_date', + 'options' => [ + 'post_date' => esc_html__( 'Date', 'elementor-pro' ), + 'post_title' => esc_html__( 'Title', 'elementor-pro' ), + 'menu_order' => esc_html__( 'Menu Order', 'elementor-pro' ), + 'modified' => esc_html__( 'Last Modified', 'elementor-pro' ), + 'comment_count' => esc_html__( 'Comment Count', 'elementor-pro' ), + 'rand' => esc_html__( 'Random', 'elementor-pro' ), + ], + 'condition' => [ + 'post_type!' => 'current_query', + ], + ]; + + $fields['order'] = [ + 'label' => esc_html__( 'Order', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'desc', + 'options' => [ + 'asc' => esc_html__( 'ASC', 'elementor-pro' ), + 'desc' => esc_html__( 'DESC', 'elementor-pro' ), + ], + 'condition' => [ + 'post_type!' => 'current_query', + ], + ]; + + $fields['posts_per_page'] = [ + 'label' => esc_html__( 'Posts Per Page', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 3, + 'condition' => [ + 'post_type!' => 'current_query', + ], + ]; + + $fields['ignore_sticky_posts'] = [ + 'label' => esc_html__( 'Ignore Sticky Posts', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'condition' => [ + 'post_type' => 'post', + ], + 'description' => esc_html__( 'Sticky-posts ordering is visible on frontend only', 'elementor-pro' ), + ]; + + $fields['query_id'] = [ + 'label' => esc_html__( 'Query ID', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => '', + 'ai' => [ + 'active' => false, + ], + 'description' => esc_html__( 'Give your Query a custom unique id to allow server side filtering', 'elementor-pro' ), + 'separator' => 'before', + 'dynamic' => [ + 'active' => true, + ], + ]; + + return $fields; + } + + /** + * Build the group-controls array + * Note: this method completely overrides any settings done in Group_Control_Posts + * @param string $name + * + * @return array + */ + protected function init_fields_by_name( $name ) { + $fields = $this->get_fields_array( $name ); + + static::init_presets(); + + return $fields; + } + + /** + * Presets: filter controls subsets to be be used by the specific Group_Control_Query instance. + * + * Possible values: + * 'full' : (default) all presets + * 'include' : the 'include' tab - by id, by taxonomy, by author + * 'exclude': the 'exclude' tab - by id, by taxonomy, by author + * 'advanced_exclude': extend the 'exclude' preset with 'avoid-duplicates' & 'offset' + * 'date': date query controls + * 'pagination': posts per-page + * 'order': sort & ordering controls + * 'query_id': allow saving a specific query for future usage. + * + * Usage: + * full: build a Group_Controls_Query with all possible controls, + * when 'full' is passed, the Group_Controls_Query will ignore all other preset values. + * $this->add_group_control( + * Group_Control_Query::get_type(), + * [ + * ... + * 'presets' => [ 'full' ], + * ... + * ] ); + * + * Subset: build a Group_Controls_Query with subset of the controls, + * in the following example, the Query controls will set only the 'include' & 'date' query args. + * $this->add_group_control( + * Group_Control_Query::get_type(), + * [ + * ... + * 'presets' => [ 'include', 'date' ], + * ... + * ] ); + */ + protected static function init_presets() { + + $tabs = [ + 'query_args', + 'query_include', + 'query_exclude', + ]; + + static::$presets['include'] = array_merge( $tabs, [ + 'include', + 'include_ids', + 'include_term_ids', + 'include_authors', + ] ); + + static::$presets['exclude'] = array_merge( $tabs, [ + 'exclude', + 'exclude_ids', + 'exclude_term_ids', + 'exclude_authors', + ] ); + + static::$presets['advanced_exclude'] = array_merge( static::$presets['exclude'], [ + 'avoid_duplicates', + 'offset', + ] ); + + static::$presets['date'] = [ + 'select_date', + 'date_before', + 'date_after', + ]; + + static::$presets['pagination'] = [ + 'posts_per_page', + 'ignore_sticky_posts', + ]; + + static::$presets['order'] = [ + 'orderby', + 'order', + ]; + + static::$presets['query_id'] = [ + 'query_id', + ]; + } + + private function filter_by_presets( $presets, $fields ) { + + if ( in_array( 'full', $presets, true ) ) { + return $fields; + } + + $control_ids = []; + foreach ( static::$presets as $key => $preset ) { + $control_ids = array_merge( $control_ids, $preset ); + } + + foreach ( $presets as $preset ) { + if ( array_key_exists( $preset, static::$presets ) ) { + $control_ids = array_diff( $control_ids, static::$presets[ $preset ] ); + } + } + + foreach ( $control_ids as $remove ) { + unset( $fields[ $remove ] ); + } + + return $fields; + + } + + protected function prepare_fields( $fields ) { + + $args = $this->get_args(); + + if ( ! empty( $args['presets'] ) ) { + $fields = $this->filter_by_presets( $args['presets'], $fields ); + } + + $post_type_args = []; + if ( ! empty( $args['post_type'] ) ) { + $post_type_args['post_type'] = $args['post_type']; + } + + $post_types = Utils::get_public_post_types( $post_type_args ); + + $fields['post_type']['options'] = array_merge( $post_types, $fields['post_type']['options'] ); + $fields['post_type']['default'] = key( $post_types ); + $fields['posts_ids']['object_type'] = array_keys( $post_types ); + + //skip parent, go directly to grandparent + return Group_Control_Base::prepare_fields( $fields ); + } + + protected function get_child_default_args() { + $args = parent::get_child_default_args(); + $args['presets'] = [ 'full' ]; + + return $args; + } + + protected function get_default_options() { + return [ + 'popover' => false, + ]; + } + + protected function get_tabs_keys( $name ) { + return [ + 'tabs_wrapper' => $name . '_query_args', + 'include_wrapper' => $name . '_query_include', + 'exclude_wrapper' => $name . '_query_exclude', + ]; + } +} diff --git a/modules/query-control/controls/group-control-related.php b/modules/query-control/controls/group-control-related.php new file mode 100644 index 0000000..0cc9c11 --- /dev/null +++ b/modules/query-control/controls/group-control-related.php @@ -0,0 +1,122 @@ + esc_html__( 'Term', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'options' => $this->get_supported_taxonomies(), + 'label_block' => true, + 'multiple' => true, + 'condition' => [ + 'include' => 'terms', + 'post_type' => [ + 'related', + ], + ], + 'tabs_wrapper' => $tabs_wrapper, + 'inner_tab' => $include_wrapper, + ]; + + $related_fallback = [ + 'label' => esc_html__( 'Fallback', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'fallback_none' => esc_html__( 'None', 'elementor-pro' ), + 'fallback_by_id' => esc_html__( 'Manual Selection', 'elementor-pro' ), + 'fallback_recent' => esc_html__( 'Recent Posts', 'elementor-pro' ), + ], + 'default' => 'fallback_none', + 'description' => esc_html__( 'Displayed if no relevant results are found. Manual selection display order is random', 'elementor-pro' ), + 'condition' => [ + 'post_type' => 'related', + ], + 'separator' => 'before', + ]; + + $fallback_ids = [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => Query_Module::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'multiple' => true, + 'autocomplete' => [ + 'object' => Query_Module::QUERY_OBJECT_POST, + ], + 'condition' => [ + 'post_type' => 'related', + 'related_fallback' => 'fallback_by_id', + ], + 'export' => false, + ]; + + $fields = \Elementor\Utils::array_inject( $fields, 'include_term_ids', [ 'related_taxonomies' => $related_taxonomies ] ); + $fields = \Elementor\Utils::array_inject( $fields, 'offset', [ 'related_fallback' => $related_fallback ] ); + $fields = \Elementor\Utils::array_inject( $fields, 'related_fallback', [ 'fallback_ids' => $fallback_ids ] ); + + return $fields; + } + + protected function get_supported_taxonomies() { + $supported_taxonomies = []; + + $public_types = Utils::get_public_post_types(); + + foreach ( $public_types as $type => $title ) { + $taxonomies = get_object_taxonomies( $type, 'objects' ); + foreach ( $taxonomies as $key => $tax ) { + if ( ! array_key_exists( $key, $supported_taxonomies ) ) { + $label = $tax->label; + if ( in_array( $tax->label, $supported_taxonomies ) ) { + $label = $tax->label . ' (' . $tax->name . ')'; + } + $supported_taxonomies[ $key ] = $label; + } + } + } + + return $supported_taxonomies; + } + + protected static function init_presets() { + parent::init_presets(); + static::$presets['related'] = [ + 'related_fallback', + 'fallback_ids', + ]; + } +} diff --git a/modules/query-control/controls/group-control-taxonomy.php b/modules/query-control/controls/group-control-taxonomy.php new file mode 100644 index 0000000..c77bd06 --- /dev/null +++ b/modules/query-control/controls/group-control-taxonomy.php @@ -0,0 +1,38 @@ +get_tabs_keys( $name . '_' ); + $fields = parent::get_fields_array( $name ); + $new_fields = []; + + foreach ( $fields as $key => $field ) { + if ( 'query_args' === $key ) { + $new_fields['filter_by'] = [ + 'label' => esc_html__( 'Filter By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'show_all', + 'options' => [ + 'show_all' => esc_html__( 'Show All', 'elementor-pro' ), + 'manual_selection' => esc_html__( 'Manual Selection', 'elementor-pro' ), + ], + 'tabs_wrapper' => $tab_keys['tabs_wrapper'], + 'inner_tab' => $tab_keys['include_wrapper'], + ]; + } + + $new_fields[ $key ] = $field; + } + + return $new_fields; + } +} diff --git a/modules/query-control/controls/query.php b/modules/query-control/controls/query.php new file mode 100644 index 0000000..e16477b --- /dev/null +++ b/modules/query-control/controls/query.php @@ -0,0 +1,63 @@ + '', + ] + ); + } + + /** + * Update control settings using mapping config + * + * @param $value + * @param array $control_args + * @param array $config + * + * @return mixed + */ + public function on_import_update_settings( $value, array $control_args, array $config ) { + switch ( $control_args['autocomplete']['object'] ) { + case Module::QUERY_OBJECT_POST: + case Module::QUERY_OBJECT_LIBRARY_TEMPLATE: + return $this->replace_id_from_mapping( $value, $config['post_ids'] ); + case Module::QUERY_OBJECT_TAX: + return $this->replace_id_from_mapping( $value, $config['term_ids'] ); + default: + return $value; + } + } + + /** + * replace id from config + * + * @param mixed $value + * @param array $mapping + * + * @return string + */ + private function replace_id_from_mapping( $value, array $mapping ): string { + return $mapping[ $value ] ?? $value; + } + +} diff --git a/modules/query-control/controls/template-query.php b/modules/query-control/controls/template-query.php new file mode 100644 index 0000000..96f69ed --- /dev/null +++ b/modules/query-control/controls/template-query.php @@ -0,0 +1,75 @@ + +
    + <# if ( data.actions.new.visible ) { #> + + <# } #> + <# if ( data.actions.edit.visible ) { #> + + <# } #> +
    + '', + 'actions' => [ + 'new' => [ + 'visible' => false, + 'label' => __( 'Create template', 'elementor-pro' ), + 'document_config' => [ + 'type' => null, + ], + 'after_action' => 'switch_document', + ], + 'edit' => [ + 'visible' => false, + 'label' => __( 'Edit template', 'elementor-pro' ), + 'after_action' => 'switch_document', + ], + ], + ] + ); + } +} diff --git a/modules/query-control/module.php b/modules/query-control/module.php new file mode 100644 index 0000000..bcda4eb --- /dev/null +++ b/modules/query-control/module.php @@ -0,0 +1,1010 @@ +add_actions(); + } + + public static function add_to_avoid_list( $ids ) { + self::$displayed_ids = array_unique( array_merge( self::$displayed_ids, $ids ) ); + } + + public static function get_avoid_list_ids() { + return self::$displayed_ids; + } + + /** + * @deprecated 2.5.0 Use `Group_Control_Query` class capabilities instead. + * + * @param Widget_Base $widget + */ + public static function add_exclude_controls( $widget ) { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '2.5.0', 'class Group_Control_Query' ); + + $widget->add_control( + 'exclude', + [ + 'label' => esc_html__( 'Exclude', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'options' => [ + 'current_post' => esc_html__( 'Current Post', 'elementor-pro' ), + 'manual_selection' => esc_html__( 'Manual Selection', 'elementor-pro' ), + ], + 'label_block' => true, + ] + ); + + $widget->add_control( + 'exclude_ids', + [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => self::QUERY_CONTROL_ID, + 'autocomplete' => [ + 'object' => self::QUERY_OBJECT_POST, + ], + 'options' => [], + 'label_block' => true, + 'multiple' => true, + 'condition' => [ + 'exclude' => 'manual_selection', + ], + ] + ); + + $widget->add_control( + 'avoid_duplicates', + [ + 'label' => esc_html__( 'Avoid Duplicates', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'description' => esc_html__( 'Set to Yes to avoid duplicate posts from showing up on the page. This only affects the frontend.', 'elementor-pro' ), + ] + ); + + } + + public function get_name() { + return 'query-control'; + } + + private function search_taxonomies( $query_params, $query_data, $data ) { + $by_field = $query_data['query']['by_field']; + $terms = get_terms( $query_params ); + + $results = []; + + foreach ( $terms as $term ) { + $results[] = [ + 'id' => $term->{$by_field}, + 'text' => $this->get_term_name( $term, $query_data['display'], $data ), + ]; + } + + return $results; + + } + + /** + * 'autocomplete' => [ + * 'object' => 'post|tax|user|library_template|attachment|js', // required + * 'display' => 'minimal(default)|detailed|custom_filter_name', + * 'by_field' => 'term_taxonomy_id(default)|term_id', // relevant only if `object` is tax|cpt_tax + * 'query' => [ + * 'post_type' => 'any|post|page|custom-post-type', // can be an array for multiple post types. + * // 'any' should not be used if 'object' is 'tax' or 'cpt_tax'. + * ... + * ], + * ], + * + * 'object' (required): the queried object. + * supported values: + * 'post' : will use WP_Query(), if query['post_type'] is empty or missing, will default to 'any'. + * 'tax' : will use get_terms(). + * When 'post_type' is provided, will first use get_object_taxonomies() to build 'taxonomy' + * args then invoke get_terms(). + * When both 'taxonomy' and 'post_type' are provided, 'post_type' is ignored. + * 'cpt_tax' : Used in frontend only, will be replaced to 'tax' by js. + * Will use get_object_taxonomies() to build 'taxonomy' args then use get_terms(). + * 'user' : will use WP_User_Query() with the args defined in 'query'. + * 'author' : will use WP_User_Query() with pre-defined args. + * 'library_template' : will use WP_Query() with post_type = Source_Local::CPT. + * 'attachment' : will use WP_Query() with post_type = attachment. + * 'js' : Query data is populated by JavaScript. + * By the time the data is sent to the server, + * the 'object' value should be replaced with one of the other valid 'object' values and + * the Query array populated accordingly. + * user_defined : will invoke apply_filters() using the user_defined value as filter name, + * `elementor/query/[get_value_titles|get_autocomplete]/{user_defined}`. + * + * 'display': output format + * supported values: + * 'minimal' (default) : name only + * 'detailed' : for Post & Taxonomies -> `[Taxonomy|Post-Type] : [parent] ... [parent] > name` + * for Users & Authors -> `name [email]` + * user_defined : will invoke apply_filters using the user_defined value as filter name, + * `elementor/query/[get_value_titles|get_autocomplete]/display/{user_defined}` + * + * `by_field`: value of 'id' field in taxonomy query. Relevant only if `object` is tax|cpt_tax + * supported values: + * 'term_taxonomy_id'(default) + * 'term_id' + * + * 'query': array of args to be passed "as-is" to the relevant query function (see 'object'). + * + ** + * + * @param array $data + * + * @return array | \WP_Error + */ + private function autocomplete_query_data( $data ) { + if ( empty( $data['autocomplete'] ) || empty( $data['q'] ) || empty( $data['autocomplete']['object'] ) ) { + return new \WP_Error( self::AUTOCOMPLETE_ERROR_CODE, 'Empty or incomplete data' ); + } + + $autocomplete = $data['autocomplete']; + + if ( in_array( $autocomplete['object'], self::$supported_objects_for_query, true ) ) { + $method_name = 'autocomplete_query_for_' . $autocomplete['object']; + if ( empty( $autocomplete['display'] ) ) { + $autocomplete['display'] = 'minimal'; + $data['autocomplete'] = $autocomplete; + } + $query = $this->$method_name( $data ); + if ( is_wp_error( $query ) ) { + return $query; + } + $autocomplete['query'] = $query; + } + + return $autocomplete; + } + + private function autocomplete_query_for_post( $data ) { + if ( ! isset( $data['autocomplete']['query'] ) ) { + return new \WP_Error( self::AUTOCOMPLETE_ERROR_CODE, 'Missing autocomplete[`query`] data' ); + } + $query = $data['autocomplete']['query']; + if ( empty( $query['post_type'] ) ) { + $query['post_type'] = 'any'; + } + $query['posts_per_page'] = -1; + $query['s'] = $data['q']; + + return $query; + } + + private function autocomplete_query_for_library_template( $data ) { + $query = $data['autocomplete']['query']; + + $query['post_type'] = Source_Local::CPT; + $query['orderby'] = 'meta_value'; + $query['order'] = 'ASC'; + + if ( empty( $query['posts_per_page'] ) ) { + $query['posts_per_page'] = -1; + } + $query['s'] = $data['q']; + + return $query; + } + + private function autocomplete_query_for_attachment( $data ) { + $query = $this->autocomplete_query_for_post( $data ); + if ( is_wp_error( $query ) ) { + return $query; + } + $query['post_type'] = 'attachment'; + $query['post_status'] = 'inherit'; + + return $query; + } + + private function autocomplete_query_for_tax( $data ) { + $query = $data['autocomplete']['query']; + + if ( empty( $query['taxonomy'] ) && ! empty( $query['post_type'] ) ) { + $query['taxonomy'] = get_object_taxonomies( $query['post_type'] ); + } + $query['search'] = $data['q']; + $query['hide_empty'] = false; + return $query; + } + + private function autocomplete_query_for_author( $data ) { + $query = $this->autocomplete_query_for_user( $data ); + if ( is_wp_error( $query ) ) { + return $query; + } + + return $this->add_edit_capability_to_user_query( $query ); + } + + private function autocomplete_query_for_user( $data ) { + $query = $data['autocomplete']['query']; + if ( ! empty( $query ) ) { + return $query; + } + + $query = [ + 'fields' => [ + 'ID', + 'display_name', + ], + 'search' => '*' . $data['q'] . '*', + 'search_columns' => [ + 'user_login', + 'user_nicename', + ], + ]; + if ( 'detailed' === $data['autocomplete']['display'] ) { + $query['fields'][] = 'user_email'; + } + return $query; + } + + private function get_titles_query_data( $data ) { + if ( empty( $data['get_titles'] ) || empty( $data['id'] ) || empty( $data['get_titles']['object'] ) ) { + return new \WP_Error( self::GET_TITLES_ERROR_CODE, 'Empty or incomplete data' ); + } + + $get_titles = $data['get_titles']; + if ( empty( $get_titles['query'] ) ) { + $get_titles['query'] = []; + } + + if ( in_array( $get_titles['object'], self::$supported_objects_for_query, true ) ) { + $method_name = 'get_titles_query_for_' . $get_titles['object']; + $query = $this->$method_name( $data ); + if ( is_wp_error( $query ) ) { + return $query; + } + $get_titles['query'] = $query; + } + + if ( empty( $get_titles['display'] ) ) { + $get_titles['display'] = 'minimal'; + } + + return $get_titles; + } + + private function get_titles_query_for_post( $data ) { + $query = $data['get_titles']['query']; + if ( empty( $query['post_type'] ) ) { + $query['post_type'] = 'any'; + } + $query['posts_per_page'] = -1; + $query['post__in'] = (array) $data['id']; + + return $query; + } + + private function get_titles_query_for_attachment( $data ) { + $query = $this->get_titles_query_for_post( $data ); + $query['post_type'] = 'attachment'; + $query['post_status'] = 'inherit'; + + return $query; + } + + private function get_titles_query_for_tax( $data ) { + $by_field = empty( $data['get_titles']['by_field'] ) ? 'term_taxonomy_id' : $data['get_titles']['by_field']; + return [ + $by_field => (array) $data['id'], + 'hide_empty' => false, + ]; + } + + + private function get_titles_query_for_library_template( $data ) { + $query = $data['get_titles']['query']; + + $query['post_type'] = Source_Local::CPT; + $query['orderby'] = 'meta_value'; + $query['order'] = 'ASC'; + + if ( empty( $query['posts_per_page'] ) ) { + $query['posts_per_page'] = -1; + } + + return $query; + } + + private function get_titles_query_for_author( $data ) { + $query = $this->get_titles_query_for_user( $data ); + + $query['has_published_posts'] = true; + + return $this->add_edit_capability_to_user_query( $query ); + } + + private function get_titles_query_for_user( $data ) { + $query = $data['get_titles']['query']; + if ( ! empty( $query ) ) { + return $query; + } + $query = [ + 'fields' => [ + 'ID', + 'display_name', + ], + 'include' => (array) $data['id'], + ]; + if ( 'detailed' === $data['get_titles']['display'] ) { + $query['fields'][] = 'user_email'; + } + return $query; + } + + /** + * @deprecated 2.6.0 use new `autocomplete` format + * + * @param $data + * + * @return mixed + */ + private function extract_post_type( $data ) { + + if ( ! empty( $data['query'] ) && ! empty( $data['query']['post_type'] ) ) { + return $data['query']['post_type']; + } + + return $data['object_type']; + } + + /** + * @deprecated 2.6.0 use new `autocomplete` format + * + * @param $data + * + * @return array + * @throws \Exception + */ + public function ajax_posts_filter_autocomplete_deprecated( $data ) { + $document = Utils::_unstable_get_document_for_edit( $data['editor_post_id'] ); + + if ( empty( $data['filter_type'] ) || empty( $data['q'] ) ) { + throw new \Exception( 'Bad request.' ); + } + + $results = []; + + switch ( $data['filter_type'] ) { + case 'taxonomy': + $query_params = [ + 'taxonomy' => $this->extract_post_type( $data ), + 'search' => $data['q'], + 'hide_empty' => false, + ]; + + $terms = get_terms( $query_params ); + if ( is_wp_error( $terms ) ) { + break; + } + + global $wp_taxonomies; + + foreach ( $terms as $term ) { + $term_name = $this->get_term_name_with_parents( $term ); + if ( ! empty( $data['include_type'] ) ) { + $text = $wp_taxonomies[ $term->taxonomy ]->labels->name . ': ' . $term_name; + } else { + $text = $term_name; + } + + $results[] = [ + 'id' => $term->term_taxonomy_id, + 'text' => $text, + ]; + } + + break; + + case 'by_id': + case 'post': + $query_params = [ + 'post_type' => $this->extract_post_type( $data ), + 's' => $data['q'], + 'posts_per_page' => -1, + ]; + + if ( 'attachment' === $query_params['post_type'] ) { + $query_params['post_status'] = 'inherit'; + } + + $query = new \WP_Query( $query_params ); + + foreach ( $query->posts as $post ) { + $post_type_obj = get_post_type_object( $post->post_type ); + if ( ! empty( $data['include_type'] ) ) { + $text = $post_type_obj->labels->name . ': ' . $post->post_title; + } else { + $text = ( $post_type_obj->hierarchical ) ? $this->get_post_name_with_parents( $post ) : $post->post_title; + } + + $results[] = [ + 'id' => $post->ID, + 'text' => esc_html( $text ), + ]; + } + break; + + case 'author': + $query_params = [ + 'has_published_posts' => true, + 'fields' => [ + 'ID', + 'display_name', + ], + 'search' => '*' . $data['q'] . '*', + 'search_columns' => [ + 'user_login', + 'user_nicename', + ], + ]; + + $query_params = $this->add_edit_capability_to_user_query( $query_params ); + + $user_query = new \WP_User_Query( $query_params ); + + foreach ( $user_query->get_results() as $author ) { + $results[] = [ + 'id' => $author->ID, + 'text' => $author->display_name, + ]; + } + break; + default: + $results = apply_filters( 'elementor/query/get_autocomplete/' . $data['filter_type'], [], $data ); + } + + return [ + 'results' => $results, + ]; + } + + /** + * @param array $data + * + * @return array + * @throws \Exception + */ + public function ajax_posts_filter_autocomplete( array $data ) { + if ( ! current_user_can( Editor::EDITING_CAPABILITY ) ) { + throw new \Exception( 'Access denied.' ); + } + + $query_data = $this->autocomplete_query_data( $data ); + if ( is_wp_error( $query_data ) ) { + /** @var \WP_Error $query_data */ + throw new \Exception( $query_data->get_error_code() . ':' . $query_data->get_error_message() ); + } + + $results = []; + $display = $query_data['display']; + $query_args = $query_data['query']; + $query_args['no_found_rows'] = true; + + switch ( $query_data['object'] ) { + case self::QUERY_OBJECT_TAX: + $by_field = ! empty( $query_data['by_field'] ) ? $query_data['by_field'] : 'term_taxonomy_id'; + $terms = get_terms( $query_args ); + if ( is_wp_error( $terms ) ) { + break; + } + foreach ( $terms as $term ) { + if ( apply_filters( "elementor/query/get_autocomplete/tax/{$display}", true, $term, $data ) ) { + $results[] = [ + 'id' => $term->{$by_field}, + 'text' => $this->get_term_name( $term, $display, $data ), + ]; + } + } + break; + case self::QUERY_OBJECT_ATTACHMENT: + case self::QUERY_OBJECT_POST: + $query = new \WP_Query( $query_args ); + + foreach ( $query->posts as $post ) { + if ( apply_filters( "elementor/query/get_autocomplete/custom/{$display}", true, $post, $data ) ) { + $text = $this->format_post_for_display( $post, $display, $data ); + $results[] = [ + 'id' => $post->ID, + 'text' => $text, + ]; + } + } + break; + case self::QUERY_OBJECT_LIBRARY_TEMPLATE: + $query = new \WP_Query( $query_args ); + + foreach ( $query->posts as $post ) { + $document = Plugin::elementor()->documents->get( $post->ID ); + if ( $document ) { + $text = esc_html( $post->post_title ) . ' (' . $document->get_post_type_title() . ')'; + $results[] = [ + 'id' => $post->ID, + 'text' => $text, + ]; + } + } + break; + case self::QUERY_OBJECT_USER: + case self::QUERY_OBJECT_AUTHOR: + $user_query = new \WP_User_Query( $query_args ); + + foreach ( $user_query->get_results() as $user ) { + if ( apply_filters( "elementor/query/get_autocomplete/user/{$display}", true, $user, $data ) ) { + $results[] = [ + 'id' => $user->ID, + 'text' => $this->format_user_for_display( $user, $display, $data ), + ]; + } + } + break; + default: + $results = apply_filters( 'elementor/query/get_autocomplete/' . $query_data['filter_type'], $results, $data ); + } + + return [ + 'results' => $results, + ]; + } + + /** + * @param $request + * + * @return array + * @throws \Exception + * @deprecated 2.6.0 use new `autocomplete` format + * + */ + public function ajax_posts_control_value_titles_deprecated( $request ) { + $document = Utils::_unstable_get_document_for_edit( $request['editor_post_id'] ); + + $ids = (array) $request['id']; + + $results = []; + + switch ( $request['filter_type'] ) { + case 'taxonomy': + $terms = get_terms( + [ + 'term_taxonomy_id' => $ids, + 'hide_empty' => false, + ] + ); + if ( is_wp_error( $terms ) ) { + break; + } + + global $wp_taxonomies; + foreach ( $terms as $term ) { + $term_name = $this->get_term_name_with_parents( $term ); + if ( ! empty( $request['include_type'] ) ) { + $text = $wp_taxonomies[ $term->taxonomy ]->labels->name . ': ' . $term_name; + } else { + $text = $term_name; + } + $results[ $term->{$by_field} ] = $text; + } + break; + + case 'by_id': + case 'post': + $query = new \WP_Query( + [ + 'post_type' => 'any', + 'post__in' => $ids, + 'posts_per_page' => -1, + ] + ); + + foreach ( $query->posts as $post ) { + $results[ $post->ID ] = esc_html( $post->post_title ); + } + break; + + case 'author': + $query_params = [ + 'has_published_posts' => true, + 'fields' => [ + 'ID', + 'display_name', + ], + 'include' => $ids, + ]; + + $query_params = $this->add_edit_capability_to_user_query( $query_params ); + + $user_query = new \WP_User_Query( $query_params ); + + foreach ( $user_query->get_results() as $author ) { + $results[ $author->ID ] = $author->display_name; + } + break; + default: + $results = apply_filters( 'elementor/query/get_value_titles/' . $request['filter_type'], [], $request ); + } + + return $results; + } + + /** + * @throws \Exception + */ + public function ajax_posts_control_value_titles( $request ) { + if ( ! current_user_can( Editor::EDITING_CAPABILITY ) ) { + throw new \Exception( 'Access denied.' ); + } + + $query_data = $this->get_titles_query_data( $request ); + if ( is_wp_error( $query_data ) ) { + return []; + } + $display = $query_data['display']; + $query_args = $query_data['query']; + $query_args['no_found_rows'] = true; + + $results = []; + switch ( $query_data['object'] ) { + case self::QUERY_OBJECT_TAX: + $by_field = ! empty( $query_data['by_field'] ) ? $query_data['by_field'] : 'term_taxonomy_id'; + + // The term_id is not working in 2022-01-18, this is a hack to replace the term id with + // `include`, the code is a legacy code so the solution is minimal as possible. + if ( isset( $query_args['term_id'] ) ) { + $query_args['include'] = array_map( 'intval', $query_args['term_id'] ); + } + + $terms = get_terms( $query_args ); + + if ( is_wp_error( $terms ) ) { + break; + } + foreach ( $terms as $term ) { + if ( apply_filters( "elementor/query/get_value_titles/tax/{$display}", true, $term, $request ) ) { + $results[ $term->{$by_field} ] = $this->get_term_name( $term, $display, $request, 'get_value_titles' ); + } + } + break; + + case self::QUERY_OBJECT_ATTACHMENT: + case self::QUERY_OBJECT_POST: + $query = new \WP_Query( $query_args ); + + foreach ( $query->posts as $post ) { + if ( apply_filters( "elementor/query/get_value_titles/custom/{$display}", true, $post, $request ) ) { + $results[ $post->ID ] = $this->format_post_for_display( $post, $display, $request, 'get_value_titles' ); + } + } + break; + case self::QUERY_OBJECT_LIBRARY_TEMPLATE: + $query = new \WP_Query( $query_args ); + + foreach ( $query->posts as $post ) { + $document = Plugin::elementor()->documents->get( $post->ID ); + if ( $document ) { + $results[ $post->ID ] = htmlentities( esc_html( $post->post_title ) ) . ' (' . $document->get_post_type_title() . ')'; + } + } + break; + case self::QUERY_OBJECT_AUTHOR: + case self::QUERY_OBJECT_USER: + $user_query = new \WP_User_Query( $query_args ); + + foreach ( $user_query->get_results() as $user ) { + if ( apply_filters( "elementor/query/get_value_titles/user/{$display}", true, $user, $request ) ) { + $results[ $user->ID ] = $this->format_user_for_display( $user, $display, $request, 'get_value_titles' ); + } + } + break; + default: + $results = apply_filters( "elementor/query/get_value_titles/{$query_data['filter_type']}", $results, $request ); + } + + return $results; + } + + private function get_term_name( $term, $display, $request, $filter_name = 'get_autocomplete' ) { + global $wp_taxonomies; + $term_name = $this->get_term_name_with_parents( $term ); + switch ( $display ) { + case 'detailed': + $text = $wp_taxonomies[ $term->taxonomy ]->labels->name . ': ' . $term_name; + break; + case 'minimal': + $text = $term_name; + break; + default: + $text = apply_filters( "elementor/query/{$filter_name}/display/{$display}", $term_name, $request ); + break; + } + return $text; + } + + /** + * @param \WP_Post $post + * @param string $display + * @param array $data + * @param string $filter_name + * + * @return mixed|string|void + */ + private function format_post_for_display( $post, $display, $data, $filter_name = 'get_autocomplete' ) { + $post_type_obj = get_post_type_object( $post->post_type ); + switch ( $display ) { + case 'minimal': + $text = ( $post_type_obj->hierarchical ) ? $this->get_post_name_with_parents( $post ) : $post->post_title; + break; + case 'detailed': + $text = $post_type_obj->labels->name . ': ' . ( $post_type_obj->hierarchical ) ? $this->get_post_name_with_parents( $post ) : $post->post_title; + break; + default: + $text = apply_filters( "elementor/query/{$filter_name}/display/{$display}", $post->post_title, $post->ID, $data ); + break; + } + + return esc_html( $text ); + } + + /** + * @param \WP_User $user + * @param string $display + * @param array $data + * @param string $filter_name + * + * @return string + */ + private function format_user_for_display( $user, $display, $data, $filter_name = 'get_autocomplete' ) { + switch ( $display ) { + case 'minimal': + $text = $user->display_name; + break; + case 'detailed': + $text = sprintf( '%s (%s)', $user->display_name, $user->user_email ); + break; + default: + $text = apply_filters( "elementor/query/{$filter_name}/display/{$display}", $user, $data ); + break; + } + + return $text; + } + + private function query_data_compatibility( $data ) { + if ( isset( $data['query']['filter_type'] ) ) { + $data['filter_type'] = $data['query']['filter_type']; + } + if ( isset( $data['query']['object_type'] ) ) { + $data['object_type'] = $data['query']['object_type']; + } + if ( isset( $data['query']['include_type'] ) ) { + $data['include_type'] = $data['query']['include_type']; + } + if ( isset( $data['query']['post_type'] ) ) { + $data['post_type'] = $data['query']['post_type']; + } + return $data; + } + + public function register_controls( Controls_Manager $controls_manager ) { + $controls_manager->add_group_control( Group_Control_Posts::get_type(), new Group_Control_Posts() ); + + $controls_manager->add_group_control( Group_Control_Query::get_type(), new Group_Control_Query() ); + + $controls_manager->add_group_control( Group_Control_Related::get_type(), new Group_Control_Related() ); + + $controls_manager->add_group_control( Group_Control_Taxonomy::get_type(), new Group_Control_Taxonomy() ); + + $controls_manager->register( new Query() ); + + $controls_manager->register( new Template_Query() ); + } + + /** + * get_term_name_with_parents + * @param \WP_Term $term + * @param int $max + * + * @return string + */ + private function get_term_name_with_parents( \WP_Term $term, $max = 3 ) { + if ( 0 === $term->parent ) { + return $term->name; + } + $separator = is_rtl() ? ' < ' : ' > '; + $test_term = $term; + $names = []; + while ( $test_term->parent > 0 ) { + $test_term = get_term( $test_term->parent ); + if ( ! $test_term ) { + break; + } + $names[] = $test_term->name; + } + + $names = array_reverse( $names ); + if ( count( $names ) < ( $max ) ) { + return implode( $separator, $names ) . $separator . $term->name; + } + + $name_string = ''; + for ( $i = 0; $i < ( $max - 1 ); $i++ ) { + $name_string .= $names[ $i ] . $separator; + } + return $name_string . '...' . $separator . $term->name; + } + + /** + * get post name with parents + * @param \WP_Post $post + * @param int $max + * + * @return string + */ + private function get_post_name_with_parents( $post, $max = 3 ) { + if ( 0 === $post->post_parent ) { + return $post->post_title; + } + $separator = is_rtl() ? ' < ' : ' > '; + $test_post = $post; + $names = []; + while ( $test_post->post_parent > 0 ) { + $test_post = get_post( $test_post->post_parent ); + if ( ! $test_post ) { + break; + } + $names[] = $test_post->post_title; + } + + $names = array_reverse( $names ); + if ( count( $names ) < ( $max ) ) { + return implode( $separator, $names ) . $separator . $post->post_title; + } + + $name_string = ''; + for ( $i = 0; $i < ( $max - 1 ); $i++ ) { + $name_string .= $names[ $i ] . $separator; + } + return $name_string . '...' . $separator . $post->post_title; + } + + /** + * @deprecated 2.5.0 Use `Elementor_Post_Query` class capabilities instead. + * + * @param string $control_id + * @param array $settings + * + * @return array + */ + public function get_query_args( $control_id, $settings ) { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '2.5.0', 'class Elementor_Post_Query' ); + + $controls_manager = Plugin::elementor()->controls_manager; + + /** @var Group_Control_Posts $posts_query */ + $posts_query = $controls_manager->get_control_groups( Group_Control_Posts::get_type() ); + + return $posts_query->get_query_args( $control_id, $settings ); + } + + /** + * @param \Elementor\Widget_Base $widget + * @param string $name + * @param array $query_args + * @param array $fallback_args + * + * @return \WP_Query + */ + public function get_query( $widget, $name, $query_args = [], $fallback_args = [] ) { + $prefix = $name . '_'; + $post_type = $widget->get_settings( $prefix . 'post_type' ); + if ( 'related' === $post_type ) { + $elementor_query = new Elementor_Related_Query( $widget, $name, $query_args, $fallback_args ); + } else { + $elementor_query = new Elementor_Post_Query( $widget, $name, $query_args ); + } + return $elementor_query->get_query(); + } + + /** + * @param Ajax $ajax_manager + */ + public function register_ajax_actions( $ajax_manager ) { + $ajax_manager->register_ajax_action( 'query_control_value_titles', [ $this, 'ajax_posts_control_value_titles' ] ); + $ajax_manager->register_ajax_action( 'pro_panel_posts_control_filter_autocomplete', [ $this, 'ajax_posts_filter_autocomplete' ] ); + /** + * @deprecated 2.6.0 use new `autocomplete` format + */ + $ajax_manager->register_ajax_action( 'query_control_value_titles_deprecated', [ $this, 'ajax_posts_control_value_titles_deprecated' ] ); + $ajax_manager->register_ajax_action( 'pro_panel_posts_control_filter_autocomplete_deprecated', [ $this, 'ajax_posts_filter_autocomplete_deprecated' ] ); + } + + /** + * @deprecated 3.1.0 + */ + public function localize_settings() { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); + + return []; + } + + protected function add_actions() { + add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); + add_action( 'elementor/controls/register', [ $this, 'register_controls' ] ); + } + + /** + * In WordPress 5.9 the 'who' query param was deprecated, this method + * adding the new `capability` query param to the query and still support old versions of WordPress. + * + * @param $query + * + * @return mixed + */ + private function add_edit_capability_to_user_query( $query ) { + // Capability queries were only introduced in WP 5.9. + if ( version_compare( $GLOBALS['wp_version'], '5.9-alpha', '>=' ) ) { + $query['capability'] = [ 'edit_posts' ]; + } else { + $query['who'] = 'authors'; + } + + return $query; + } +} diff --git a/modules/role-manager/module.php b/modules/role-manager/module.php new file mode 100644 index 0000000..a7286ef --- /dev/null +++ b/modules/role-manager/module.php @@ -0,0 +1,133 @@ +get_role_manager_options(); + } + + public function display_role_controls( $role_slug, $role_data ) { + static $options = false; + + if ( ! API::is_license_active() || ! API::is_licence_has_feature( static::ROLE_MANAGER_OPTION_NAME, API::BC_VALIDATION_CALLBACK ) ) { + // Promotions for PRO when the license not active. + $this->print_role_controls_promotion(); + + return; + } + + if ( ! $options ) { + $options = [ + 'excluded_options' => Plugin::elementor()->role_manager->get_role_manager_options(), + 'advanced_options' => $this->get_role_manager_options(), + ]; + } + $id = self::ROLE_MANAGER_OPTION_NAME . '_' . $role_slug . '_design'; + $name = 'elementor_' . self::ROLE_MANAGER_OPTION_NAME . '[' . $role_slug . '][]'; + $checked = isset( $options['advanced_options'][ $role_slug ] ) ? $options['advanced_options'][ $role_slug ] : []; + + ?> + + add_section( 'general', 'advanced-role-manager', [ + 'fields' => [ + self::ROLE_MANAGER_OPTION_NAME => [ + 'field_args' => [ + 'type' => 'raw_html', + 'html' => '', + ], + 'setting_args' => [ + 'sanitize_callback' => [ $this, 'save_advanced_options' ], + ], + ], + ], + ] ); + } + + private function print_role_controls_promotion() { + ?> + + get_connect_url(); + } + + private function get_connect_url() { + return Plugin::instance()->license_admin->get_connect_url( [ + 'utm_source' => 'wp-role-manager', + 'utm_medium' => 'wp-dash', + 'utm_campaign' => 'connect-and-activate-license', + ] ); + } + + public function __construct() { + parent::__construct(); + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . RoleManagerBase::PAGE_ID, [ $this, 'register_admin_fields' ], 100 ); + } + remove_action( 'elementor/role/restrictions/controls', [ Plugin::elementor()->role_manager, 'get_go_pro_link_html' ] ); + add_action( 'elementor/role/restrictions/controls', [ $this, 'display_role_controls' ], 10, 2 ); + add_filter( 'elementor/editor/user/restrictions', [ $this, 'get_user_restrictions' ] ); + } +} diff --git a/modules/screenshots/module.php b/modules/screenshots/module.php new file mode 100644 index 0000000..edc78e5 --- /dev/null +++ b/modules/screenshots/module.php @@ -0,0 +1,275 @@ +offsetGet( 'content-type' ); + + header( 'content-type: ' . $content_type ); + + return wp_remote_retrieve_body( $response ); + } + + /** + * Save screenshot and attached it to the post. + * + * @param $data + * + * @return bool|string + * @throws \Exception + */ + public function ajax_save( $data ) { + if ( empty( $data['screenshot'] ) || empty( $data['post_id'] ) ) { + return false; + } + + if ( ! $this->can_user_manage_screenshots( $data['post_id'] ) ) { + return false; + } + + $screenshot = new Screenshot( $data['post_id'], $data['screenshot'] ); + + $screenshot + ->create_dir() + ->upload() + ->remove_old_attachment() + ->create_new_attachment() + ->unmark_as_failed(); + + return $screenshot->get_screenshot_url(); + } + + /** + * Remove the screenshot image and the attachment data. + * + * @param $data + * + * @return bool + */ + public function ajax_delete( $data ) { + if ( empty( $data['post_id'] ) ) { + return false; + } + + if ( ! $this->can_user_manage_screenshots( $data['post_id'] ) ) { + return false; + } + + $screenshot = new Screenshot( $data['post_id'] ); + + $screenshot + ->remove_old_attachment() + ->remove_old_post_meta() + ->unmark_as_failed(); + + return true; + } + + /** + * Mark screenshot as failed. + * + * @param $data + * + * @return bool + */ + public function ajax_screenshot_failed( $data ) { + if ( empty( $data['post_id'] ) ) { + return false; + } + + if ( ! $this->can_user_manage_screenshots( $data['post_id'] ) ) { + return false; + } + + $screenshot = new Screenshot( $data['post_id'] ); + + $screenshot->mark_as_failed(); + + return true; + } + + /** + * Extends the json of the templates. + * sets a screenshot as a thumbnail if exists, and if not will add a url to generate screenshot for + * the specific template. + * + * @param array $template + * + * @return array + */ + public function extend_templates_json_structure( $template ) { + + if ( ! empty( $template['thumbnail'] ) ) { + return $template; + } + + $attachment_data = get_post_meta( $template['id'], Screenshot::POST_META_KEY, true ); + + if ( isset( $attachment_data['url'] ) ) { + $template['thumbnail'] = $attachment_data['url']; + + return $template; + } + + $failed_to_create_screenshot = get_post_meta( $template['id'], Screenshot::FAILED_POST_META_KEY, true ); + + // If it failed to create screenshot before, it should not set screenshot_url, and should not try + // to create another screenshot until the next edit of the template. + if ( ! $failed_to_create_screenshot ) { + $template['screenshot_url'] = Render_Mode_Screenshot::get_url( $template['id'] ); + } + + return $template; + } + + /** + * @param \WP_Query $query + */ + public function filter_screenshots_from_attachments_query( \WP_Query $query ) { + global $pagenow, $typenow; + + if ( 'upload.php' !== $pagenow || 'attachment' !== $typenow ) { + return; + } + + if ( empty( $query->query_vars['meta_query'] ) ) { + $query->query_vars['meta_query'] = []; + } + + $query->query_vars['meta_query'][] = [ + 'key' => Screenshot::IS_SCREENSHOT_POST_META_KEY, + 'compare' => 'NOT EXISTS', + ]; + } + + public function filter_screenshots_from_ajax_attachments_query( $query ) { + if ( empty( $query['meta_query'] ) ) { + $query['meta_query'] = []; + } + + $query['meta_query'][] = [ + 'key' => Screenshot::IS_SCREENSHOT_POST_META_KEY, + 'compare' => 'NOT EXISTS', + ]; + + return $query; + } + + /** + * Register screenshots action. + * + * @param \Elementor\Core\Common\Modules\Ajax\Module $ajax_manager + */ + public function register_ajax_actions( $ajax_manager ) { + $ajax_manager->register_ajax_action( 'screenshot_save', [ $this, 'ajax_save' ] ); + $ajax_manager->register_ajax_action( 'screenshot_delete', [ $this, 'ajax_delete' ] ); + $ajax_manager->register_ajax_action( 'screenshot_failed', [ $this, 'ajax_screenshot_failed' ] ); + } + + /** + * @param Render_Mode_Manager $manager + * + * @throws \Exception + */ + public function register_render_mode( Render_Mode_Manager $manager ) { + $manager->register_render_mode( Render_Mode_Screenshot::class ); + } + + /** + * Check and validate proxy mode. + * + * @param array $query_params + * + * @return bool + * @throws \Requests_Exception_HTTP_400 + * @throws \Requests_Exception_HTTP_403 + * @throws Status400 + * @throws Status403 + */ + protected function is_screenshot_proxy_mode( array $query_params ) { + $is_proxy = isset( $query_params['screenshot_proxy'] ); + + if ( $is_proxy ) { + if ( ! wp_verify_nonce( $query_params['nonce'], self::SCREENSHOT_PROXY_NONCE_ACTION ) ) { + // WP >= 6.2-alpha + if ( class_exists( '\WpOrg\Requests\Exception\Http\Status403' ) ) { + throw new \WpOrg\Requests\Exception\Http\Status403(); + } else { + throw new \Requests_Exception_HTTP_403(); + } + } + + if ( ! $query_params['href'] ) { + // WP >= 6.2-alpha + if ( class_exists( '\WpOrg\Requests\Exception\Http\Status400' ) ) { + throw new \WpOrg\Requests\Exception\Http\Status400(); + } else { + throw new \Requests_Exception_HTTP_400(); + } + } + } + + return $is_proxy; + } + + /** + * @param $post_id + * + * @return bool + * @throws \Exception + */ + private function can_user_manage_screenshots( $post_id ) { + return Utils::_unstable_get_document_for_edit( $post_id ) && current_user_can( 'upload_files' ); + } + + /** + * Module constructor. + */ + public function __construct() { + parent::__construct(); + + if ( $this->is_screenshot_proxy_mode( $_GET ) ) { // phpcs:ignore -- Checking nonce inside the method. + echo $this->get_proxy_data( htmlspecialchars( $_GET['href'] ) ); // phpcs:ignore -- Nonce was checked on the above method + die; + } + + add_action( 'elementor/frontend/render_mode/register', [ $this, 'register_render_mode' ] ); + add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); + add_action( 'parse_query', [ $this, 'filter_screenshots_from_attachments_query' ] ); + add_filter( 'ajax_query_attachments_args', [ $this, 'filter_screenshots_from_ajax_attachments_query' ] ); + add_filter( 'elementor-pro/site-editor/data/template', [ $this, 'extend_templates_json_structure' ] ); + } +} diff --git a/modules/screenshots/render-mode-screenshot.php b/modules/screenshots/render-mode-screenshot.php new file mode 100644 index 0000000..bf8df92 --- /dev/null +++ b/modules/screenshots/render-mode-screenshot.php @@ -0,0 +1,75 @@ +get_locations_manager(), 'builder_wrapper' ], + 9999999 + ); + + add_filter( 'template_include', [ $this, 'filter_template' ] ); + } + + public function filter_template() { + return ELEMENTOR_PATH . 'modules/page-templates/templates/canvas.php'; + } + + public function is_static() { + return true; + } + + public function enqueue_scripts() { + $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || defined( 'ELEMENTOR_TESTS' ) && ELEMENTOR_PRO_TESTS ) ? '' : '.min'; + + wp_enqueue_script( + 'dom-to-image', + ELEMENTOR_PRO_ASSETS_URL . "/lib/dom-to-image/js/dom-to-image{$suffix}.js", + [], + '2.6.0', + true + ); + + wp_enqueue_script( + 'html2canvas', + ELEMENTOR_PRO_ASSETS_URL . "/lib/html2canvas/js/html2canvas{$suffix}.js", + [], + '1.0.0-rc.5', + true + ); + + wp_enqueue_script( + 'elementor-screenshot', + ELEMENTOR_PRO_ASSETS_URL . "/js/screenshot{$suffix}.js", + [ 'dom-to-image', 'html2canvas' ], + ELEMENTOR_PRO_VERSION, + true + ); + + $config = [ + 'selector' => '.elementor-' . $this->post_id, + 'nonce' => wp_create_nonce( Module::SCREENSHOT_PROXY_NONCE_ACTION ), + 'home_url' => home_url(), + 'post_id' => $this->post_id, + ]; + + wp_add_inline_script( 'elementor-screenshot', 'var ElementorScreenshotConfig = ' . wp_json_encode( $config ) . ';' ); + } +} diff --git a/modules/screenshots/screenshot.php b/modules/screenshots/screenshot.php new file mode 100644 index 0000000..85264dc --- /dev/null +++ b/modules/screenshots/screenshot.php @@ -0,0 +1,251 @@ +post_id = $post_id; + $this->base64_image = $base64_image; + } + + /** + * Creates the directory if needed + add index.html file for security reasons. + * + * @return $this + */ + public function create_dir() { + $dir = wp_upload_dir()['basedir'] . '/' . self::SCREENSHOT_DIR; + $html_file = $dir . '/index.html'; + + if ( file_exists( $html_file ) ) { + return $this; + }; + + if ( ! file_exists( $dir ) ) { + wp_mkdir_p( $dir ); + } + + touch( $html_file ); + + return $this; + } + + /** + * Uploads the base64 image it self. + * + * TODO: Use Upload Manager when ready. + * + * @return $this + * @throws \Exception + */ + public function upload() { + if ( ! $this->base64_image ) { + throw new \Exception( 'Cannot upload an image with out base64_image.' ); + } + + $file_content = substr( $this->base64_image, strlen( 'data:image/png;base64,' ) ); + + add_filter( 'wp_unique_filename', [ $this, 'get_file_name' ] ); + add_filter( 'upload_dir', [ $this, 'extend_upload_dirs_array' ] ); + + $this->upload_bits = wp_upload_bits( + $this->get_file_name(), + null, + base64_decode( $file_content ) + ); + + remove_filter( 'wp_unique_filename', [ $this, 'get_file_name' ] ); + remove_filter( 'upload_dir', [ $this, 'extend_upload_dirs_array' ] ); + + return $this; + } + + /** + * Removes the old attachment if there is an old screenshot image. + * + * @return $this + */ + public function remove_old_attachment() { + $post_meta = get_post_meta( $this->post_id, self::POST_META_KEY, true ); + + if ( ! $post_meta ) { + return $this; + } + + wp_delete_attachment( $post_meta['id'] ); + + return $this; + } + + /** + * Removes the old post meta of the current post. + * + * @return $this + */ + public function remove_old_post_meta() { + delete_post_meta( $this->post_id, self::POST_META_KEY ); + + return $this; + } + + /** + * Creates an attachment to the new screenshot and attach it to the original post + * via post_meta. + * + * @return $this + * @throws \Exception + */ + public function create_new_attachment() { + $upload_bits = $this->get_upload_bits(); + + $info = wp_check_filetype( $upload_bits['file'] ); + $post_mime_type = null; + + if ( $info ) { + $post_mime_type = $info['type']; + } + + $attachment_id = wp_insert_attachment( + [ + 'post_title' => $this->get_file_name(), + 'guid' => $upload_bits['url'], + 'post_mime_type' => $post_mime_type, + 'meta_input' => [ + self::IS_SCREENSHOT_POST_META_KEY => true, + ], + ], + $upload_bits['file'], + $this->post_id + ); + + update_post_meta( $this->post_id, self::POST_META_KEY, [ + 'id' => $attachment_id, + 'url' => $upload_bits['url'], + ] ); + + return $this; + } + + /** + * Mark the post that the screenshot capture was failed. + * + * @return $this + */ + public function mark_as_failed() { + update_post_meta( + $this->post_id, + self::FAILED_POST_META_KEY, + ( new \DateTime() )->format( 'Y-m-d H:i:s' ) + ); + + return $this; + } + + /** + * Remove the failed_screenshot post meta. + * + * @return $this + */ + public function unmark_as_failed() { + delete_post_meta( $this->post_id, self::FAILED_POST_META_KEY ); + + return $this; + } + + /** + * Get the file name, + * if not exists will generate it. + * + * @return string + */ + public function get_file_name() { + if ( ! $this->file_name ) { + $now = ( new \DateTime() )->format( 'Y-m-d-H-i-s' ); + $random_str = Utils::generate_random_string(); + + $this->file_name = "Elementor-post-screenshot_{$this->post_id}_{$now}_{$random_str}.png"; + } + + return $this->file_name; + } + + /** + * Extend and change the upload_dirs original method + * to update the current screenshot to custom directory. + * + * @param $upload_dirs + * + * @return array + */ + public function extend_upload_dirs_array( $upload_dirs ) { + return array_merge( $upload_dirs, [ + 'subdir' => $subdir = self::SCREENSHOT_DIR, + 'path' => "{$upload_dirs['basedir']}/{$subdir}", + 'url' => "{$upload_dirs['baseurl']}/{$subdir}", + ] ); + } + + /** + * Get wp_upload_bits result. + * + * This method will be throw an exception if was called before actually upload a screenshot. + * + * @return array + * @throws \Exception + */ + protected function get_upload_bits() { + if ( ! $this->upload_bits ) { + throw new \Exception( 'File was not uploaded yet.' ); + } + + return $this->upload_bits; + } + + /** + * Get the url of the screenshot. + * + * @return string + * @throws \Exception + */ + public function get_screenshot_url() { + return $this->get_upload_bits()['url']; + } +} diff --git a/modules/scroll-snap/module.php b/modules/scroll-snap/module.php new file mode 100644 index 0000000..b8d5abb --- /dev/null +++ b/modules/scroll-snap/module.php @@ -0,0 +1,133 @@ +add_actions(); + } + + public function get_name() { + return 'scroll-snap'; + } + + public function register_controls( Controls_Stack $controls_stack, $section_id ) { + + if ( ( ! $controls_stack instanceof Theme_Page_Document && ! $controls_stack instanceof PageBase ) || 'section_custom_css_pro' !== $section_id ) { + return; + } + + $scroll_snap_children = '.elementor-section:not(.elementor-inner-section), .elementor-location-header, .elementor-location-footer, .page-header, .site-header, .elementor-add-section, .e-con:not(.e-child)'; + + $controls_stack->start_controls_section( + 'section_scroll_snap', + [ + 'label' => esc_html__( 'Scroll Snap', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_ADVANCED, + ] + ); + + $controls_stack->add_control( + 'scroll_snap', + [ + 'label' => esc_html__( 'Scroll Snap', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'description' => esc_html__( 'Scroll Snap makes the viewport stop on a specific position of a section when scrolling ends.', 'elementor-pro' ), + 'selectors' => [ + 'html' => 'height: 100vh; margin: 0; overflow: hidden;', + 'body' => 'height: 100vh; overflow: auto; scroll-snap-type: y mandatory;', + ], + 'frontend_available' => true, + ] + ); + + $controls_stack->add_responsive_control( + 'scroll_snap_position', + [ + 'label' => esc_html__( 'Snap Position', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Top', 'elementor-pro' ), + 'center' => esc_html__( 'Center', 'elementor-pro' ), + 'end' => esc_html__( 'Bottom', 'elementor-pro' ), + ], + 'selectors_dictionary' => [ + '' => 'start', + ], + 'condition' => [ + 'scroll_snap!' => '', + ], + 'selectors' => [ + $scroll_snap_children => 'scroll-snap-align: {{VALUE}}', + ], + ] + ); + + $controls_stack->add_responsive_control( + 'scroll_snap_padding', + [ + 'label' => esc_html__( 'Scroll Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'condition' => [ + 'scroll_snap!' => '', + ], + 'selectors' => [ + 'body' => 'scroll-padding: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $controls_stack->add_responsive_control( + 'force_stop', + [ + 'label' => esc_html__( 'Scroll Snap Stop', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Normal', 'elementor-pro' ), + 'always' => esc_html__( 'Always', 'elementor-pro' ), + ], + 'selectors_dictionary' => [ + '' => 'normal', + ], + 'condition' => [ + 'scroll_snap!' => '', + ], + 'selectors' => [ + $scroll_snap_children => 'scroll-snap-stop: {{VALUE}}', + ], + ] + ); + + $controls_stack->end_controls_section(); + } + + private function add_actions() { + add_action( 'elementor/element/after_section_end', [ $this, 'register_controls' ], 10, 2 ); + } +} diff --git a/modules/share-buttons/module.php b/modules/share-buttons/module.php new file mode 100644 index 0000000..161705d --- /dev/null +++ b/modules/share-buttons/module.php @@ -0,0 +1,127 @@ + [ + 'title' => 'Facebook', + 'has_counter' => true, + ], + 'twitter' => [ + 'title' => 'Twitter', + ], + 'linkedin' => [ + 'title' => 'LinkedIn', + 'has_counter' => true, + ], + 'pinterest' => [ + 'title' => 'Pinterest', + 'has_counter' => true, + ], + 'reddit' => [ + 'title' => 'Reddit', + 'has_counter' => true, + ], + 'vk' => [ + 'title' => 'VK', + 'has_counter' => true, + ], + 'odnoklassniki' => [ + 'title' => 'OK', + 'has_counter' => true, + ], + 'tumblr' => [ + 'title' => 'Tumblr', + ], + 'digg' => [ + 'title' => 'Digg', + ], + 'skype' => [ + 'title' => 'Skype', + ], + 'stumbleupon' => [ + 'title' => 'StumbleUpon', + 'has_counter' => true, + ], + 'mix' => [ + 'title' => 'Mix', + ], + 'telegram' => [ + 'title' => 'Telegram', + ], + 'pocket' => [ + 'title' => 'Pocket', + 'has_counter' => true, + ], + 'xing' => [ + 'title' => 'XING', + 'has_counter' => true, + ], + 'whatsapp' => [ + 'title' => 'WhatsApp', + ], + 'email' => [ + 'title' => 'Email', + ], + 'print' => [ + 'title' => 'Print', + ], + ]; + + public static function get_networks( $network_name = null ) { + // TODO: Remove the class_exists check and move X-twitter and Threads to self::$networks permanently when Elementor 3.22 is released. + if ( class_exists( 'Elementor\Widget_Share_Buttons' ) ) { + self::$networks = array_merge( self::$networks, [ + 'x-twitter' => [ + 'title' => 'X', + ], + 'threads' => [ + 'title' => 'Threads', + ], + ] ); + + $supported_networks = \Elementor\Widget_Share_Buttons::get_supported_networks(); + + self::$networks = array_filter( self::$networks, function( $network_name ) use ( $supported_networks ) { + return in_array( $network_name, $supported_networks, true ); + }, ARRAY_FILTER_USE_KEY ); + } + + if ( $network_name ) { + return self::$networks[ $network_name ] ?? null; + } + + return self::$networks; + } + + public function get_widgets() { + return [ + 'Share_Buttons', + ]; + } + + public function get_name() { + return 'share-buttons'; + } + + public function add_localize_data( $settings ) { + $settings['shareButtonsNetworks'] = self::get_networks(); + + return $settings; + } + + public function __construct() { + parent::__construct(); + + add_filter( 'elementor_pro/frontend/localize_settings', [ $this, 'add_localize_data' ] ); + + add_filter( 'elementor_pro/editor/localize_settings', [ $this, 'add_localize_data' ] ); + } +} diff --git a/modules/share-buttons/widgets/share-buttons.php b/modules/share-buttons/widgets/share-buttons.php new file mode 100644 index 0000000..7977a3c --- /dev/null +++ b/modules/share-buttons/widgets/share-buttons.php @@ -0,0 +1,700 @@ + [ + 'value' => 'fa fa-get-pocket', + ], + 'email' => [ + 'value' => 'fa fa-envelope', + ], + ]; + + private static $networks_icon_mapping = [ + 'pocket' => [ + 'value' => 'fab fa-get-pocket', + 'library' => 'fa-brands', + ], + 'email' => [ + 'value' => 'fas fa-envelope', + 'library' => 'fa-solid', + ], + 'print' => [ + 'value' => 'fas fa-print', + 'library' => 'fa-solid', + ], + ]; + + public function get_style_depends() { + if ( Icons_Manager::is_migration_allowed() ) { + return [ + 'elementor-icons-fa-solid', + 'elementor-icons-fa-brands', + ]; + } + return []; + } + + private static function get_network_icon_data( $network_name ) { + $prefix = 'fa '; + $library = ''; + + if ( Icons_Manager::is_migration_allowed() ) { + if ( isset( self::$networks_icon_mapping[ $network_name ] ) ) { + return self::$networks_icon_mapping[ $network_name ]; + } + $prefix = 'fab '; + $library = 'fa-brands'; + } + if ( isset( self::$networks_class_dictionary[ $network_name ] ) ) { + return self::$networks_class_dictionary[ $network_name ]; + } + + return [ + 'value' => $prefix . 'fa-' . $network_name, + 'library' => $library, + ]; + } + + public function get_name() { + return 'share-buttons'; + } + + public function get_title() { + return esc_html__( 'Share Buttons', 'elementor-pro' ); + } + + public function get_icon() { + return 'eicon-share'; + } + + public function get_keywords() { + return [ 'sharing', 'social', 'icon', 'button', 'like' ]; + } + + protected function register_controls() { + $this->start_controls_section( + 'section_buttons_content', + [ + 'label' => esc_html__( 'Share Buttons', 'elementor-pro' ), + ] + ); + + $repeater = new Repeater(); + + $networks = Module::get_networks(); + + $networks_names = array_keys( $networks ); + + $repeater->add_control( + 'button', + [ + 'label' => esc_html__( 'Network', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => array_reduce( $networks_names, function( $options, $network_name ) use ( $networks ) { + $options[ $network_name ] = $networks[ $network_name ]['title']; + + return $options; + }, [] ), + 'default' => 'facebook', + ] + ); + + $repeater->add_control( + 'text', + [ + 'label' => esc_html__( 'Custom Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'share_buttons', + [ + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'default' => [ + [ + 'button' => 'facebook', + ], + [ + 'button' => 'twitter', + ], + [ + 'button' => 'linkedin', + ], + ], + 'title_field' => ' {{{ elementorPro.modules.shareButtons.getNetworkTitle( obj ) }}}', + ] + ); + + $this->add_control( + 'view', + [ + 'label' => esc_html__( 'View', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'icon-text' => 'Icon & Text', + 'icon' => 'Icon', + 'text' => 'Text', + ], + 'default' => 'icon-text', + 'separator' => 'before', + 'prefix_class' => 'elementor-share-buttons--view-', + 'render_type' => 'template', + ] + ); + + $this->add_control( + 'show_label', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + 'condition' => [ + 'view' => 'icon-text', + ], + ] + ); + + $this->add_control( + 'skin', + [ + 'label' => esc_html__( 'Skin', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'gradient' => esc_html__( 'Gradient', 'elementor-pro' ), + 'minimal' => esc_html__( 'Minimal', 'elementor-pro' ), + 'framed' => esc_html__( 'Framed', 'elementor-pro' ), + 'boxed' => esc_html__( 'Boxed Icon', 'elementor-pro' ), + 'flat' => esc_html__( 'Flat', 'elementor-pro' ), + ], + 'default' => 'gradient', + 'prefix_class' => 'elementor-share-buttons--skin-', + ] + ); + + $this->add_control( + 'shape', + [ + 'label' => esc_html__( 'Shape', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'square' => esc_html__( 'Square', 'elementor-pro' ), + 'rounded' => esc_html__( 'Rounded', 'elementor-pro' ), + 'circle' => esc_html__( 'Circle', 'elementor-pro' ), + ], + 'default' => 'square', + 'prefix_class' => 'elementor-share-buttons--shape-', + ] + ); + + $this->add_responsive_control( + 'columns', + [ + 'label' => esc_html__( 'Columns', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '0', + 'options' => [ + '0' => 'Auto', + '1' => '1', + '2' => '2', + '3' => '3', + '4' => '4', + '5' => '5', + '6' => '6', + ], + 'prefix_class' => 'elementor-grid%s-', + ] + ); + + $this->add_responsive_control( + 'alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justify', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + /* TODO: `prefix_class` is redundant since v3.1.0 + * It is only here for backwards compatibility reasons. + * It should be removed in the future. + */ + 'prefix_class' => 'elementor-share-buttons%s--align-', + /*---------------------------------------------------*/ + 'condition' => [ + 'columns' => '0', + ], + /* `selectors` was added on v3.1.0 as a superior alternative to the previous `prefix_class` solution */ + 'selectors' => [ + '{{WRAPPER}}' => '--alignment: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'share_url_type', + [ + 'label' => esc_html__( 'Target URL', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'current_page' => esc_html__( 'Current Page', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'current_page', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'share_url', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'dynamic' => [ + 'active' => true, + ], + 'options' => false, + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'condition' => [ + 'share_url_type' => 'custom', + ], + 'show_label' => false, + 'frontend_available' => true, + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_buttons_style', + [ + 'label' => esc_html__( 'Share Buttons', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'column_gap', + [ + 'label' => esc_html__( 'Columns Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 10, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--grid-side-margin: {{SIZE}}{{UNIT}}; --grid-column-gap: {{SIZE}}{{UNIT}}; --grid-row-gap: {{SIZE}}{{UNIT}}', + '(tablet) {{WRAPPER}}' => '--grid-side-margin: {{SIZE}}{{UNIT}}; --grid-column-gap: {{SIZE}}{{UNIT}}', + '(mobile) {{WRAPPER}}' => '--grid-side-margin: {{SIZE}}{{UNIT}}; --grid-column-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'row_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 10, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--grid-row-gap: {{SIZE}}{{UNIT}}; --grid-bottom-margin: {{SIZE}}{{UNIT}}', + '(tablet) {{WRAPPER}}' => '--grid-row-gap: {{SIZE}}{{UNIT}}; --grid-bottom-margin: {{SIZE}}{{UNIT}}', + '(mobile) {{WRAPPER}}' => '--grid-row-gap: {{SIZE}}{{UNIT}}; --grid-bottom-margin: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'button_size', + [ + 'label' => esc_html__( 'Button Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0.5, + 'max' => 2, + 'step' => 0.05, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-share-btn' => 'font-size: calc({{SIZE}}{{UNIT}} * 10);', + ], + ] + ); + + $this->add_responsive_control( + 'icon_size', + [ + 'label' => esc_html__( 'Icon Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'default' => [ + 'unit' => 'em', + ], + 'tablet_default' => [ + 'unit' => 'em', + ], + 'mobile_default' => [ + 'unit' => 'em', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-share-btn__icon' => '--e-share-buttons-icon-size: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'view!' => 'text', + ], + ] + ); + + $this->add_responsive_control( + 'button_height', + [ + 'label' => esc_html__( 'Button Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'default' => [ + 'unit' => 'em', + ], + 'tablet_default' => [ + 'unit' => 'em', + ], + 'mobile_default' => [ + 'unit' => 'em', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-share-btn' => 'height: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'border_size', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'size' => 2, + ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-share-btn' => 'border-width: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'skin' => [ 'framed', 'boxed' ], + ], + ] + ); + + $this->add_control( + 'color_source', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'official' => esc_html__( 'Official', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'official', + 'prefix_class' => 'elementor-share-buttons--color-', + 'separator' => 'before', + ] + ); + + $this->start_controls_tabs( + 'tabs_button_style', + [ + 'condition' => [ + 'color_source' => 'custom', + ], + ] + ); + + $this->start_controls_tab( + 'tab_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'primary_color', + [ + 'label' => esc_html__( 'Primary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}}' => '--e-share-buttons-primary-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'secondary_color', + [ + 'label' => esc_html__( 'Secondary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--e-share-buttons-secondary-color: {{VALUE}}', + ], + 'separator' => 'after', + 'condition' => [ + 'skin!' => 'framed', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'primary_color_hover', + [ + 'label' => esc_html__( 'Primary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-share-btn:hover' => '--e-share-buttons-primary-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'secondary_color_hover', + [ + 'label' => esc_html__( 'Secondary Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-share-btn:hover' => '--e-share-buttons-secondary-color: {{VALUE}}', + ], + 'separator' => 'after', + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'typography', + 'selector' => '{{WRAPPER}} .elementor-share-btn__title', + 'exclude' => [ 'line_height' ], + ] + ); + + $this->add_control( + 'text_padding', + [ + 'label' => esc_html__( 'Text Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} a.elementor-button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'before', + 'condition' => [ + 'view' => 'text', + ], + ] + ); + + $this->end_controls_section(); + + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + if ( empty( $settings['share_buttons'] ) ) { + return; + } + + $button_classes = 'elementor-share-btn'; + + $show_text = 'text' === $settings['view'] || 'yes' === $settings['show_label']; + ?> +
    + +
    +
    + + + + + + +
    + + + + + +
    + +
    +
    + +
    + + <# + var shareButtonsEditorModule = elementorPro.modules.shareButtons, + buttonClass = 'elementor-share-btn'; + + var showText = 'icon-text' === settings.view ? 'yes' === settings.show_label : 'text' === settings.view; + #> +
    + <# + _.each( settings.share_buttons, function( button ) { + // A deprecated network. + if ( ! shareButtonsEditorModule.getNetworkData( button ) ) { + return; + } + + var networkName = button.button, + socialNetworkClass = 'elementor-share-btn_' + networkName; + #> +
    +
    + <# if ( 'icon' === settings.view || 'icon-text' === settings.view ) { #> + + + + <# } #> + <# if ( showText ) { #> +
    + <# if ( 'yes' === settings.show_label || 'text' === settings.view ) { #> + {{{ shareButtonsEditorModule.getNetworkTitle( button ) }}} + <# } #> +
    + <# } #> +
    +
    + <# } ); #> +
    + experiments->is_feature_active( 'e_font_icon_svg' ) ) { + $icon = Icons_Manager::render_font_icon( $network_icon_data ); + } else { + $icon = sprintf( '', $network_icon_data['value'] ); + } + + Utils::print_unescaped_internal_string( $icon ); + } +} diff --git a/modules/slides/module.php b/modules/slides/module.php new file mode 100644 index 0000000..e6daecc --- /dev/null +++ b/modules/slides/module.php @@ -0,0 +1,21 @@ + esc_html__( 'Extra Small', 'elementor-pro' ), + 'sm' => esc_html__( 'Small', 'elementor-pro' ), + 'md' => esc_html__( 'Medium', 'elementor-pro' ), + 'lg' => esc_html__( 'Large', 'elementor-pro' ), + 'xl' => esc_html__( 'Extra Large', 'elementor-pro' ), + ]; + } + + protected function register_controls() { + $this->start_controls_section( + 'section_slides', + [ + 'label' => esc_html__( 'Slides', 'elementor-pro' ), + ] + ); + + $repeater = new Repeater(); + + $repeater->start_controls_tabs( 'slides_repeater' ); + + $repeater->start_controls_tab( + 'background', + [ + 'label' => esc_html__( 'Background', 'elementor-pro' ), + ] + ); + + $repeater->add_control( + 'background_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#bbbbbb', + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-bg' => 'background-color: {{VALUE}}', + ], + ] + ); + + $repeater->add_control( + 'background_image', + [ + 'label' => _x( 'Image', 'Background Control', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-bg' => 'background-image: url({{URL}})', + ], + ] + ); + + $repeater->add_control( + 'background_size', + [ + 'label' => _x( 'Size', 'Background Control', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'cover', + 'options' => [ + 'cover' => _x( 'Cover', 'Background Control', 'elementor-pro' ), + 'contain' => _x( 'Contain', 'Background Control', 'elementor-pro' ), + 'auto' => _x( 'Auto', 'Background Control', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-bg' => 'background-size: {{VALUE}}', + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'background_image[url]', + 'operator' => '!=', + 'value' => '', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'background_ken_burns', + [ + 'label' => esc_html__( 'Ken Burns Effect', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'background_image[url]', + 'operator' => '!=', + 'value' => '', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'zoom_direction', + [ + 'label' => esc_html__( 'Zoom Direction', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'in', + 'options' => [ + 'in' => esc_html__( 'In', 'elementor-pro' ), + 'out' => esc_html__( 'Out', 'elementor-pro' ), + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'background_ken_burns', + 'operator' => '!=', + 'value' => '', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'background_overlay', + [ + 'label' => esc_html__( 'Background Overlay', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'background_image[url]', + 'operator' => '!=', + 'value' => '', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'background_overlay_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => 'rgba(0,0,0,0.5)', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'background_overlay', + 'value' => 'yes', + ], + ], + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .elementor-background-overlay' => 'background-color: {{VALUE}}', + ], + ] + ); + + $repeater->add_control( + 'background_overlay_blend_mode', + [ + 'label' => esc_html__( 'Blend Mode', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Normal', 'elementor-pro' ), + 'multiply' => 'Multiply', + 'screen' => 'Screen', + 'overlay' => 'Overlay', + 'darken' => 'Darken', + 'lighten' => 'Lighten', + 'color-dodge' => 'Color Dodge', + 'color-burn' => 'Color Burn', + 'hue' => 'Hue', + 'saturation' => 'Saturation', + 'color' => 'Color', + 'exclusion' => 'Exclusion', + 'luminosity' => 'Luminosity', + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'background_overlay', + 'value' => 'yes', + ], + ], + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .elementor-background-overlay' => 'mix-blend-mode: {{VALUE}}', + ], + ] + ); + + $repeater->end_controls_tab(); + + $repeater->start_controls_tab( + 'content', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + ] + ); + + $repeater->add_control( + 'heading', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Slide Heading', 'elementor-pro' ), + 'label_block' => true, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'description', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'button_text', + [ + 'label' => esc_html__( 'Button Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Click Here', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'link_click', + [ + 'label' => esc_html__( 'Apply Link On', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'slide' => esc_html__( 'Whole Slide', 'elementor-pro' ), + 'button' => esc_html__( 'Button Only', 'elementor-pro' ), + ], + 'default' => 'slide', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'link[url]', + 'operator' => '!=', + 'value' => '', + ], + ], + ], + ] + ); + + $repeater->end_controls_tab(); + + $repeater->start_controls_tab( + 'style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + ] + ); + + $repeater->add_control( + 'custom_style', + [ + 'label' => esc_html__( 'Custom', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'description' => esc_html__( 'Set custom style that will only affect this specific slide.', 'elementor-pro' ), + ] + ); + + $repeater->add_control( + 'horizontal_position', + [ + 'label' => esc_html__( 'Horizontal Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-h-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-contents' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'left' => 'margin-right: auto', + 'center' => 'margin: 0 auto', + 'right' => 'margin-left: auto', + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'custom_style', + 'value' => 'yes', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'vertical_position', + [ + 'label' => esc_html__( 'Vertical Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'middle' => [ + 'title' => esc_html__( 'Middle', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-inner' => 'align-items: {{VALUE}}', + ], + 'selectors_dictionary' => [ + 'top' => 'flex-start', + 'middle' => 'center', + 'bottom' => 'flex-end', + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'custom_style', + 'value' => 'yes', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'text_align', + [ + 'label' => esc_html__( 'Text Align', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-inner' => 'text-align: {{VALUE}}', + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'custom_style', + 'value' => 'yes', + ], + ], + ], + ] + ); + + $repeater->add_control( + 'content_color', + [ + 'label' => esc_html__( 'Content Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-inner .elementor-slide-heading' => 'color: {{VALUE}}', + '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-inner .elementor-slide-description' => 'color: {{VALUE}}', + '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-inner .elementor-slide-button' => 'color: {{VALUE}}; border-color: {{VALUE}}', + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'custom_style', + 'value' => 'yes', + ], + ], + ], + ] + ); + + $repeater->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'repeater_text_shadow', + 'selector' => '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-contents', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'custom_style', + 'value' => 'yes', + ], + ], + ], + ] + ); + + $repeater->end_controls_tab(); + + $repeater->end_controls_tabs(); + + $this->add_control( + 'slides', + [ + 'label' => esc_html__( 'Slides', 'elementor-pro' ), + 'type' => Controls_Manager::REPEATER, + 'show_label' => true, + 'fields' => $repeater->get_controls(), + 'default' => [ + [ + 'heading' => esc_html__( 'Slide 1 Heading', 'elementor-pro' ), + 'description' => esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), + 'button_text' => esc_html__( 'Click Here', 'elementor-pro' ), + 'background_color' => '#833ca3', + ], + [ + 'heading' => esc_html__( 'Slide 2 Heading', 'elementor-pro' ), + 'description' => esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), + 'button_text' => esc_html__( 'Click Here', 'elementor-pro' ), + 'background_color' => '#4054b2', + ], + [ + 'heading' => esc_html__( 'Slide 3 Heading', 'elementor-pro' ), + 'description' => esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), + 'button_text' => esc_html__( 'Click Here', 'elementor-pro' ), + 'background_color' => '#1abc9c', + ], + ], + 'title_field' => '{{{ heading }}}', + ] + ); + + $this->add_responsive_control( + 'slides_height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 100, + 'max' => 1000, + ], + 'em' => [ + 'min' => 10, + 'max' => 100, + ], + 'rem' => [ + 'min' => 10, + 'max' => 100, + ], + 'vh' => [ + 'min' => 10, + 'max' => 100, + ], + ], + 'default' => [ + 'size' => 400, + ], + 'selectors' => [ + '{{WRAPPER}} .swiper-slide' => 'height: {{SIZE}}{{UNIT}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'slides_title_tag', + [ + 'label' => esc_html__( 'Title HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + 'p' => 'p', + ], + 'default' => 'div', + ] + ); + + $this->add_control( + 'slides_description_tag', + [ + 'label' => esc_html__( 'Description HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + 'p' => 'p', + ], + 'default' => 'div', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_slider_options', + [ + 'label' => esc_html__( 'Slider Options', 'elementor-pro' ), + 'type' => Controls_Manager::SECTION, + ] + ); + + $this->add_control( + 'navigation', + [ + 'label' => esc_html__( 'Navigation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'both', + 'options' => [ + 'both' => esc_html__( 'Arrows and Dots', 'elementor-pro' ), + 'arrows' => esc_html__( 'Arrows', 'elementor-pro' ), + 'dots' => esc_html__( 'Dots', 'elementor-pro' ), + 'none' => esc_html__( 'None', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'autoplay', + [ + 'label' => esc_html__( 'Autoplay', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'pause_on_hover', + [ + 'label' => esc_html__( 'Pause on Hover', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'render_type' => 'none', + 'frontend_available' => true, + 'condition' => [ + 'autoplay!' => '', + ], + ] + ); + + $this->add_control( + 'pause_on_interaction', + [ + 'label' => esc_html__( 'Pause on Interaction', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'render_type' => 'none', + 'frontend_available' => true, + 'condition' => [ + 'autoplay!' => '', + ], + ] + ); + + $this->add_control( + 'autoplay_speed', + [ + 'label' => esc_html__( 'Autoplay Speed', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 5000, + 'condition' => [ + 'autoplay' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}} .swiper-slide' => 'transition-duration: calc({{VALUE}}ms*1.2)', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'infinite', + [ + 'label' => esc_html__( 'Infinite Loop', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'transition', + [ + 'label' => esc_html__( 'Transition', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'slide', + 'options' => [ + 'slide' => esc_html__( 'Slide', 'elementor-pro' ), + 'fade' => esc_html__( 'Fade', 'elementor-pro' ), + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'transition_speed', + [ + 'label' => esc_html__( 'Transition Speed', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::NUMBER, + 'default' => 500, + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'content_animation', + [ + 'label' => esc_html__( 'Content Animation', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'fadeInUp', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'fadeInDown' => esc_html__( 'Down', 'elementor-pro' ), + 'fadeInUp' => esc_html__( 'Up', 'elementor-pro' ), + 'fadeInRight' => esc_html__( 'Right', 'elementor-pro' ), + 'fadeInLeft' => esc_html__( 'Left', 'elementor-pro' ), + 'zoomIn' => esc_html__( 'Zoom', 'elementor-pro' ), + ], + 'assets' => [ + 'styles' => [ + [ + 'name' => 'e-animations', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'content_animation', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_slides', + [ + 'label' => esc_html__( 'Slides', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'content_max_width', + [ + 'label' => esc_html__( 'Content Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 1000, + ], + 'em' => [ + 'max' => 100, + ], + 'rem' => [ + 'max' => 100, + ], + ], + 'default' => [ + 'size' => 66, + 'unit' => '%', + ], + 'tablet_default' => [ + 'unit' => '%', + ], + 'mobile_default' => [ + 'unit' => '%', + ], + 'selectors' => [ + '{{WRAPPER}} .swiper-slide-contents' => 'max-width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'slides_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .swiper-slide-inner' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'slides_horizontal_position', + [ + 'label' => esc_html__( 'Horizontal Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'center', + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-h-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'prefix_class' => 'elementor--h-position-', + ] + ); + + $this->add_control( + 'slides_vertical_position', + [ + 'label' => esc_html__( 'Vertical Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'middle', + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'middle' => [ + 'title' => esc_html__( 'Middle', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'prefix_class' => 'elementor--v-position-', + ] + ); + + $this->add_control( + 'slides_text_align', + [ + 'label' => esc_html__( 'Text Align', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'default' => 'center', + 'selectors' => [ + '{{WRAPPER}} .swiper-slide-inner' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'text_shadow', + 'selector' => '{{WRAPPER}} .swiper-slide-contents', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'heading_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .swiper-slide-inner .elementor-slide-heading:not(:last-child)' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'heading_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-slide-heading' => 'color: {{VALUE}}', + + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'heading_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-slide-heading', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_description', + [ + 'label' => esc_html__( 'Description', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'description_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .swiper-slide-inner .elementor-slide-description:not(:last-child)' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'description_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-slide-description' => 'color: {{VALUE}}', + + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'description_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + 'selector' => '{{WRAPPER}} .elementor-slide-description', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_button', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'button_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'sm', + 'options' => self::get_button_sizes(), + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'button_typography', + 'selector' => '{{WRAPPER}} .elementor-slide-button', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + ] + ); + + $this->add_control( + 'button_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-slide-button' => 'border-width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-slide-button' => 'border-radius: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->start_controls_tabs( 'button_tabs' ); + + $this->start_controls_tab( + 'normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-slide-button' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'button_background', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-slide-button', + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + ], + ] + ); + + $this->add_control( + 'button_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-slide-button' => 'border-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-slide-button:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'button_hover_background', + 'types' => [ 'classic', 'gradient' ], + 'exclude' => [ 'image' ], + 'selector' => '{{WRAPPER}} .elementor-slide-button:hover', + 'fields_options' => [ + 'background' => [ + 'default' => 'classic', + ], + ], + ] + ); + + $this->add_control( + 'button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-slide-button:hover' => 'border-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-slide-button' => 'transition-duration: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_navigation', + [ + 'label' => esc_html__( 'Navigation', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'navigation' => [ 'arrows', 'dots', 'both' ], + ], + ] + ); + + $this->add_control( + 'heading_style_arrows', + [ + 'label' => esc_html__( 'Arrows', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'navigation' => [ 'arrows', 'both' ], + ], + ] + ); + + $this->add_control( + 'arrows_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'inside', + 'options' => [ + 'inside' => esc_html__( 'Inside', 'elementor-pro' ), + 'outside' => esc_html__( 'Outside', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-arrows-position-', + 'condition' => [ + 'navigation' => [ 'arrows', 'both' ], + ], + ] + ); + + $this->add_responsive_control( + 'arrows_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-swiper-button' => 'font-size: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'navigation' => [ 'arrows', 'both' ], + ], + ] + ); + + $this->add_control( + 'arrows_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-swiper-button' => 'color: {{VALUE}}', + '{{WRAPPER}} .elementor-swiper-button svg' => 'fill: {{VALUE}}', + ], + 'condition' => [ + 'navigation' => [ 'arrows', 'both' ], + ], + ] + ); + + $this->add_control( + 'heading_style_dots', + [ + 'label' => esc_html__( 'Pagination', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'navigation' => [ 'dots', 'both' ], + ], + ] + ); + + $this->add_control( + 'dots_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'inside', + 'options' => [ + 'outside' => esc_html__( 'Outside', 'elementor-pro' ), + 'inside' => esc_html__( 'Inside', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-pagination-position-', + 'condition' => [ + 'navigation' => [ 'dots', 'both' ], + ], + ] + ); + + $swiper_class = Plugin::elementor()->experiments->is_feature_active( 'e_swiper_latest' ) ? 'swiper' : 'swiper-container'; + + $this->add_responsive_control( + 'dots_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .swiper-pagination-bullet' => 'height: {{SIZE}}{{UNIT}}; width: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .' . $swiper_class . '-horizontal .swiper-pagination-progressbar' => 'height: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .swiper-pagination-fraction' => 'font-size: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'navigation' => [ 'dots', 'both' ], + ], + ] + ); + + $this->add_control( + 'dots_color_inactive', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + // The opacity property will override the default inactive dot color which is opacity 0.2. + '{{WRAPPER}} .swiper-pagination-bullet:not(.swiper-pagination-bullet-active)' => 'background-color: {{VALUE}}; opacity: 1;', + ], + 'condition' => [ + 'navigation' => [ 'dots', 'both' ], + ], + ] + ); + + $this->add_control( + 'dots_color', + [ + 'label' => esc_html__( 'Active Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .swiper-pagination-bullet-active' => 'background-color: {{VALUE}};', + ], + 'condition' => [ + 'navigation' => [ 'dots', 'both' ], + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + if ( empty( $settings['slides'] ) ) { + return; + } + + $title_tag = Utils::validate_html_tag( $settings['slides_title_tag'] ); + $description_tag = Utils::validate_html_tag( $settings['slides_description_tag'] ); + + $this->add_render_attribute( 'button', 'class', [ 'elementor-button', 'elementor-slide-button' ] ); + + if ( ! empty( $settings['button_size'] ) ) { + $this->add_render_attribute( 'button', 'class', 'elementor-size-' . $settings['button_size'] ); + } + + $slides = []; + $slide_count = 0; + + foreach ( $settings['slides'] as $slide ) { + $slide_html = ''; + $btn_attributes = ''; + $slide_attributes = ''; + $slide_element = 'div'; + $btn_element = 'div'; + + if ( ! empty( $slide['link']['url'] ) ) { + $this->add_link_attributes( 'slide_link' . $slide_count, $slide['link'] ); + + if ( 'button' === $slide['link_click'] ) { + $btn_element = 'a'; + $btn_attributes = $this->get_render_attribute_string( 'slide_link' . $slide_count ); + } else { + $slide_element = 'a'; + $slide_attributes = $this->get_render_attribute_string( 'slide_link' . $slide_count ); + } + } + + $slide_html .= '<' . $slide_element . ' class="swiper-slide-inner" ' . $slide_attributes . '>'; + + $slide_html .= '
    '; + + if ( $slide['heading'] ) { + $slide_html .= '<' . $title_tag . ' class="elementor-slide-heading">' . $slide['heading'] . ''; + } + + if ( $slide['description'] ) { + $slide_html .= '<' . $description_tag . ' class="elementor-slide-description">' . $slide['description'] . ''; + } + + if ( $slide['button_text'] ) { + $slide_html .= '<' . $btn_element . ' ' . $btn_attributes . ' ' . $this->get_render_attribute_string( 'button' ) . '>' . $slide['button_text'] . ''; + } + + $slide_html .= '
    '; + + if ( 'yes' === $slide['background_overlay'] ) { + $slide_html = '
    ' . $slide_html; + } + + $ken_class = ''; + + if ( $slide['background_ken_burns'] ) { + $ken_class = ' elementor-ken-burns elementor-ken-burns--' . $slide['zoom_direction']; + } + + $slide_html = '' . $slide_html; + + $slides[] = '
    ' . $slide_html . '
    '; + $slide_count++; + } + + $direction = is_rtl() ? 'rtl' : 'ltr'; + + $show_dots = ( in_array( $settings['navigation'], [ 'dots', 'both' ] ) ); + $show_arrows = ( in_array( $settings['navigation'], [ 'arrows', 'both' ] ) ); + + $slides_count = count( $settings['slides'] ); + $swiper_class = Plugin::elementor()->experiments->is_feature_active( 'e_swiper_latest' ) ? 'swiper' : 'swiper-container'; + ?> +
    +
    +
    + + +
    + + +
    + + +
    + render_swiper_button( 'previous' ); ?> + +
    +
    + render_swiper_button( 'next' ); ?> + +
    + + +
    +
    + + <# + var direction = elementorFrontend.config.is_rtl ? 'rtl' : 'ltr', + next = elementorFrontend.config.is_rtl ? 'left' : 'right', + prev = elementorFrontend.config.is_rtl ? 'right' : 'left', + navi = settings.navigation, + showDots = ( 'dots' === navi || 'both' === navi ), + showArrows = ( 'arrows' === navi || 'both' === navi ), + buttonSize = settings.button_size, + titleTag = elementor.helpers.validateHTMLTag( settings.slides_title_tag ), + descriptionTag = elementor.helpers.validateHTMLTag( settings.slides_description_tag ); + #> +
    +
    +
    + <# jQuery.each( settings.slides, function( index, slide ) { #> +
    + <# + var kenClass = ''; + + if ( '' != slide.background_ken_burns ) { + kenClass = ' elementor-ken-burns elementor-ken-burns--' + _.escape( slide.zoom_direction ); + } + #> + + <# if ( 'yes' === slide.background_overlay ) { #> +
    + <# } #> +
    +
    + <# if ( slide.heading ) { #> + <{{ titleTag }} class="elementor-slide-heading">{{{ slide.heading }}} + <# } + if ( slide.description ) { #> + <{{descriptionTag}} class="elementor-slide-description">{{{ slide.description }}} + <# } + if ( slide.button_text ) { #> +
    {{{ slide.button_text }}}
    + <# } #> +
    +
    +
    + <# } ); #> +
    + <# if ( 1 < settings.slides.length ) { #> + <# if ( showDots ) { #> +
    + <# } #> + <# if ( showArrows ) { #> +
    + + +
    +
    + + +
    + <# } #> + <# } #> +
    +
    + 'eicons', + 'value' => $icon_value, + ], [ 'aria-hidden' => 'true' ] ); + } +} diff --git a/modules/social/classes/facebook-sdk-manager.php b/modules/social/classes/facebook-sdk-manager.php new file mode 100644 index 0000000..f4108f9 --- /dev/null +++ b/modules/social/classes/facebook-sdk-manager.php @@ -0,0 +1,181 @@ +', esc_attr( $app_id ) ); + } + } + + /** + * @param Widget_Base $widget + */ + public static function add_app_id_control( $widget ) { + if ( ! self::get_app_id() ) { + $content = sprintf( + /* translators: 1: Setting Page Link opening tag, 2: Link closing tag. */ + esc_html__( 'Set your Facebook App ID in the %1$sIntegrations Settings%2$s', 'elementor-pro' ), + sprintf( '', Settings::get_url() . '#tab-integrations' ), + '' + ); + $alert_type = 'warning'; + } else { + $content = sprintf( + /* translators: 1: App ID, 2: Setting Page Link opening tag, 3: Link closing tag. */ + esc_html__( 'You are connected to Facebook App %1$s, %2$sChange App%3$s', 'elementor-pro' ), + self::get_app_id(), + sprintf( '', Settings::get_url() . '#tab-integrations' ), + '' + ); + $alert_type = 'info'; + } + + $widget->add_control( + 'app_id', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => $alert_type, + 'content' => $content, + ] + ); + + $widget->add_control( + 'app_eu_message', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'For visitors from the EU, Facebook widgets will only work for site visitors if they have logged into Facebook and consented to cookies. %1$sLearn more%2$s', 'elementor-pro' ), + sprintf( '', 'https://developers.facebook.com/docs/plugins/' ), + '' + ), + ] + ); + } + + public function localize_settings( $settings ) { + $settings['facebook_sdk'] = [ + 'lang' => self::get_lang(), + 'app_id' => self::get_app_id(), + ]; + + return $settings; + } + + public function __construct() { + add_action( 'wp_head', [ __CLASS__, 'enqueue_meta_app_id' ] ); + add_filter( 'elementor_pro/frontend/localize_settings', [ $this, 'localize_settings' ] ); + + // The nonce already validated on the options page, + if ( ! empty( $_POST['option_page'] ) && 'elementor' === $_POST['option_page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing + $this->validate_sdk(); + } + + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ] ); + } + } + + public static function get_permalink( $settings = [] ) { + $post_id = get_the_ID(); + + if ( isset( $settings['url_format'] ) && Module::URL_FORMAT_PRETTY === $settings['url_format'] ) { + return get_permalink( $post_id ); + } + + // Use plain url to avoid losing comments after change the permalink. + return add_query_arg( 'p', $post_id, home_url() ); + } + + public function register_admin_fields( Settings $settings ) { + $settings->add_section( Settings::TAB_INTEGRATIONS, 'facebook_sdk', [ + 'callback' => function() { + echo '

    ' . esc_html__( 'Facebook SDK', 'elementor-pro' ) . '

    '; + + echo sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'Facebook SDK lets you connect to your %1$sdedicated application%2$s so you can track the Facebook Widgets analytics on your site.', 'elementor-pro' ), + '', + '' + ); + echo '

    '; + + echo esc_html__( 'If you are using the Facebook Comments Widget, you can add moderating options through your application. Note that this option will not work on local sites and on domains that don\'t have public access.', 'elementor-pro' ); + }, + 'fields' => [ + 'pro_facebook_app_id' => [ + 'label' => esc_html__( 'App ID', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'text', + 'desc' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'Remember to add the domain to your %1$sApp Domains%2$s', 'elementor-pro' ), + sprintf( '', $this->get_app_settings_url() ), + '' + ), + ], + ], + ], + ] ); + } + + private function get_app_settings_url() { + $app_id = self::get_app_id(); + if ( $app_id ) { + return sprintf( 'https://developers.facebook.com/apps/%d/settings/', $app_id ); + } else { + return 'https://developers.facebook.com/apps/'; + } + } + + private function validate_sdk() { + $errors = []; + + // The nonce already validated on the options page, + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $app_id = Utils::_unstable_get_super_global_value( $_POST, 'elementor_pro_facebook_app_id' ); + + if ( $app_id ) { + $response = wp_remote_get( 'https://graph.facebook.com/' . $app_id ); + + if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) { + $errors[] = esc_html__( 'Facebook App ID is not valid', 'elementor-pro' ); + } + } + + $message = implode( '
    ', $errors ); + + if ( ! empty( $errors ) ) { + wp_die( $message, esc_html__( 'Facebook SDK', 'elementor-pro' ), [ 'back_link' => true ] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + } +} diff --git a/modules/social/module.php b/modules/social/module.php new file mode 100644 index 0000000..124e498 --- /dev/null +++ b/modules/social/module.php @@ -0,0 +1,35 @@ +add_component( 'facebook_sdk', new Facebook_SDK_Manager() ); + } +} diff --git a/modules/social/widgets/facebook-button.php b/modules/social/widgets/facebook-button.php new file mode 100644 index 0000000..d2f98e7 --- /dev/null +++ b/modules/social/widgets/facebook-button.php @@ -0,0 +1,217 @@ +start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + ] + ); + + Facebook_SDK_Manager::add_app_id_control( $this ); + + $this->add_control( + 'type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'like', + 'options' => [ + 'like' => esc_html__( 'Like', 'elementor-pro' ), + 'recommend' => esc_html__( 'Recommend', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'standard', + 'options' => [ + 'standard' => esc_html__( 'Standard', 'elementor-pro' ), + 'button' => esc_html__( 'Button', 'elementor-pro' ), + 'button_count' => esc_html__( 'Button Count', 'elementor-pro' ), + 'box_count' => esc_html__( 'Box Count', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'small', + 'options' => [ + 'small' => esc_html__( 'Small', 'elementor-pro' ), + 'large' => esc_html__( 'Large', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'color_scheme', + [ + 'label' => esc_html__( 'Color Scheme', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'light', + 'options' => [ + 'light' => esc_html__( 'Light', 'elementor-pro' ), + 'dark' => esc_html__( 'Dark', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'show_share', + [ + 'label' => esc_html__( 'Share Button', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'condition' => [ + 'type!' => 'follow', + ], + ] + ); + + $this->add_control( + 'show_faces', + [ + 'label' => esc_html__( 'Faces', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + ] + ); + + $this->add_control( + 'url_type', + [ + 'label' => esc_html__( 'Target URL', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + Module::URL_TYPE_CURRENT_PAGE => esc_html__( 'Current Page', 'elementor-pro' ), + Module::URL_TYPE_CUSTOM => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => Module::URL_TYPE_CURRENT_PAGE, + 'separator' => 'before', + 'condition' => [ + 'type' => [ 'like', 'recommend' ], + ], + ] + ); + + $this->add_control( + 'url_format', + [ + 'label' => esc_html__( 'URL Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + Module::URL_FORMAT_PLAIN => esc_html__( 'Plain Permalink', 'elementor-pro' ), + Module::URL_FORMAT_PRETTY => esc_html__( 'Pretty Permalink', 'elementor-pro' ), + ], + 'default' => Module::URL_FORMAT_PLAIN, + 'condition' => [ + 'url_type' => Module::URL_TYPE_CURRENT_PAGE, + ], + ] + ); + + $this->add_control( + 'url', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'label_block' => true, + 'condition' => [ + 'type' => [ 'like', 'recommend' ], + 'url_type' => Module::URL_TYPE_CUSTOM, + ], + ] + ); + + $this->end_controls_section(); + } + + public function render() { + $settings = $this->get_settings(); + + // Validate URL + switch ( $settings['type'] ) { + case 'like': + case 'recommend': + if ( Module::URL_TYPE_CUSTOM === $settings['url_type'] && ! filter_var( $settings['url'], FILTER_VALIDATE_URL ) ) { + if ( Plugin::elementor()->editor->is_edit_mode() ) { + echo $this->get_title() . ': ' . esc_html__( 'Please enter a valid URL', 'elementor-pro' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + return; + } + break; + } + + $attributes = [ + 'data-layout' => $settings['layout'], + 'data-colorscheme' => $settings['color_scheme'], + 'data-size' => $settings['size'], + 'data-show-faces' => $settings['show_faces'] ? 'true' : 'false', + ]; + + switch ( $settings['type'] ) { + case 'like': + case 'recommend': + if ( Module::URL_TYPE_CURRENT_PAGE === $settings['url_type'] ) { + $permalink = Facebook_SDK_Manager::get_permalink( $settings ); + } else { + $permalink = esc_url( $settings['url'] ); + } + + $attributes['class'] = 'elementor-facebook-widget fb-like'; + $attributes['data-href'] = $permalink; + $attributes['data-share'] = $settings['show_share'] ? 'true' : 'false'; + $attributes['data-action'] = $settings['type']; + break; + } + + $this->add_render_attribute( 'embed_div', $attributes ); + ?> +
    print_render_attribute_string( 'embed_div' ); ?>>
    + start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'Comments Box', 'elementor-pro' ), + ] + ); + + Facebook_SDK_Manager::add_app_id_control( $this ); + + $this->add_control( + 'comments_number', + [ + 'label' => esc_html__( 'Comment Count', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'min' => 5, + 'max' => 100, + 'default' => '10', + 'description' => esc_html__( 'Minimum number of comments: 5', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'order_by', + [ + 'label' => esc_html__( 'Order By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'social', + 'options' => [ + 'social' => esc_html__( 'Social', 'elementor-pro' ), + 'reverse_time' => esc_html__( 'Reverse Time', 'elementor-pro' ), + 'time' => esc_html__( 'Time', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'url_type', + [ + 'label' => esc_html__( 'Target URL', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + Module::URL_TYPE_CURRENT_PAGE => esc_html__( 'Current Page', 'elementor-pro' ), + Module::URL_TYPE_CUSTOM => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => Module::URL_TYPE_CURRENT_PAGE, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'url_format', + [ + 'label' => esc_html__( 'URL Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + Module::URL_FORMAT_PLAIN => esc_html__( 'Plain Permalink', 'elementor-pro' ), + Module::URL_FORMAT_PRETTY => esc_html__( 'Pretty Permalink', 'elementor-pro' ), + ], + 'default' => Module::URL_FORMAT_PLAIN, + 'condition' => [ + 'url_type' => Module::URL_TYPE_CURRENT_PAGE, + ], + ] + ); + + $this->add_control( + 'url', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'label_block' => true, + 'condition' => [ + 'url_type' => Module::URL_TYPE_CUSTOM, + ], + ] + ); + + $this->end_controls_section(); + } + + public function render() { + $settings = $this->get_settings_for_display(); + + if ( Module::URL_TYPE_CURRENT_PAGE === $settings['url_type'] ) { + $permalink = Facebook_SDK_Manager::get_permalink( $settings ); + } else { + if ( ! filter_var( $settings['url'], FILTER_VALIDATE_URL ) ) { + echo $this->get_title() . ': ' . esc_html__( 'Please enter a valid URL', 'elementor-pro' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + + return; + } + + $permalink = esc_url( $settings['url'] ); + } + + $attributes = [ + 'class' => 'elementor-facebook-widget fb-comments', + 'data-href' => $permalink, + 'data-width' => '100%', + 'data-numposts' => $settings['comments_number'], + 'data-order-by' => $settings['order_by'], + // The style prevent's the `widget.handleEmptyWidget` to set it as an empty widget + 'style' => 'min-height: 1px', + ]; + + $this->add_render_attribute( 'embed_div', $attributes ); + ?> +
    print_render_attribute_string( 'embed_div' ); ?>>
    + start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'Embed', 'elementor-pro' ), + ] + ); + + Facebook_SDK_Manager::add_app_id_control( $this ); + + $this->add_control( + 'type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'post', + 'options' => [ + 'post' => esc_html__( 'Post', 'elementor-pro' ), + 'video' => esc_html__( 'Video', 'elementor-pro' ), + 'comment' => esc_html__( 'Comment', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'post_url', + [ + 'label' => esc_html__( 'URL', 'elementor-pro' ), + 'default' => 'https://www.facebook.com/elemntor/posts/2624214124556197', + 'dynamic' => [ + 'active' => true, + ], + 'label_block' => true, + 'condition' => [ + 'type' => 'post', + ], + 'description' => esc_html__( 'Hover over the date next to the post, and copy its link address.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'video_url', + [ + 'label' => esc_html__( 'URL', 'elementor-pro' ), + 'default' => 'https://www.facebook.com/elemntor/videos/1683988961912056/', + 'dynamic' => [ + 'active' => true, + ], + 'label_block' => true, + 'condition' => [ + 'type' => 'video', + ], + 'description' => esc_html__( 'Hover over the date next to the video, and copy its link address.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'comment_url', + [ + 'label' => esc_html__( 'URL', 'elementor-pro' ), + 'default' => 'https://www.facebook.com/elemntor/videos/1811703749140576/?comment_id=1812873919023559', + 'dynamic' => [ + 'active' => true, + ], + 'label_block' => true, + 'condition' => [ + 'type' => 'comment', + ], + 'description' => esc_html__( 'Hover over the date next to the comment, and copy its link address.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'include_parent', + [ + 'label' => esc_html__( 'Parent Comment', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'description' => esc_html__( 'Set to include parent comment (if URL is a reply).', 'elementor-pro' ), + 'condition' => [ + 'type' => 'comment', + ], + ] + ); + + $this->add_control( + 'show_text', + [ + 'label' => esc_html__( 'Full Post', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'description' => esc_html__( 'Show the full text of the post', 'elementor-pro' ), + 'condition' => [ + 'type' => [ 'post', 'video' ], + ], + ] + ); + + $this->add_control( + 'video_allowfullscreen', + [ + 'label' => esc_html__( 'Allow Full Screen', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'condition' => [ + 'type' => 'video', + ], + ] + ); + + $this->add_control( + 'video_autoplay', + [ + 'label' => esc_html__( 'Autoplay', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'condition' => [ + 'type' => 'video', + ], + ] + ); + + $this->add_control( + 'video_show_captions', + [ + 'label' => esc_html__( 'Captions', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'description' => esc_html__( 'Show captions if available (only on desktop).', 'elementor-pro' ), + 'condition' => [ + 'type' => 'video', + ], + ] + ); + + $this->end_controls_section(); + } + + public function render() { + $settings = $this->get_settings_for_display(); + + if ( empty( $settings['type'] ) ) { + esc_html_e( 'Please set the embed type', 'elementor-pro' ); + + return; + } + + if ( 'comment' === $settings['type'] && empty( $settings['comment_url'] ) || 'post' === $settings['type'] && empty( $settings['post_url'] ) || 'video' === $settings['type'] && empty( $settings['video_url'] ) ) { + esc_html_e( 'Please enter a valid URL', 'elementor-pro' ); + + return; + } + + $attributes = [ + // The style prevent's the `widget.handleEmptyWidget` to set it as an empty widget + 'style' => 'min-height: 1px', + ]; + + switch ( $settings['type'] ) { + case 'comment': + $attributes['class'] = 'elementor-facebook-widget fb-comment-embed'; + $attributes['data-href'] = esc_url( $settings['comment_url'] ); + $attributes['data-include-parent'] = 'yes' === $settings['include_parent'] ? 'true' : 'false'; + break; + case 'post': + $attributes['class'] = 'elementor-facebook-widget fb-post'; + $attributes['data-href'] = esc_url( $settings['post_url'] ); + $attributes['data-show-text'] = 'yes' === $settings['show_text'] ? 'true' : 'false'; + break; + case 'video': + $attributes['class'] = 'elementor-facebook-widget fb-video'; + $attributes['data-href'] = esc_url( $settings['video_url'] ); + $attributes['data-show-text'] = 'yes' === $settings['show_text'] ? 'true' : 'false'; + $attributes['data-allowfullscreen'] = 'yes' === $settings['video_allowfullscreen'] ? 'true' : 'false'; + $attributes['data-autoplay'] = 'yes' === $settings['video_autoplay'] ? 'true' : 'false'; + $attributes['data-show-captions'] = 'yes' === $settings['video_show_captions'] ? 'true' : 'false'; + break; + } + + $this->add_render_attribute( 'embed_div', $attributes ); + + echo '
    get_render_attribute_string( 'embed_div' ) . '>
    '; // XSS ok. + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'social'; + } +} diff --git a/modules/social/widgets/facebook-page.php b/modules/social/widgets/facebook-page.php new file mode 100644 index 0000000..e514263 --- /dev/null +++ b/modules/social/widgets/facebook-page.php @@ -0,0 +1,169 @@ +start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'Page', 'elementor-pro' ), + ] + ); + + Facebook_SDK_Manager::add_app_id_control( $this ); + + $this->add_control( + 'url', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'placeholder' => 'https://www.facebook.com/your-page/', + 'default' => 'https://www.facebook.com/elemntor/', + 'label_block' => true, + 'description' => esc_html__( 'Paste the URL of the Facebook page.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'tabs', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'label_block' => true, + 'default' => [ + 'timeline', + ], + 'options' => [ + 'timeline' => esc_html__( 'Timeline', 'elementor-pro' ), + 'events' => esc_html__( 'Events', 'elementor-pro' ), + 'messages' => esc_html__( 'Messages', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'small_header', + [ + 'label' => esc_html__( 'Small Header', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + ] + ); + + $this->add_control( + 'show_cover', + [ + 'label' => esc_html__( 'Cover Photo', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + ] + ); + + $this->add_control( + 'show_facepile', + [ + 'label' => esc_html__( 'Profile Photos', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + ] + ); + + $this->add_control( + 'show_cta', + [ + 'label' => esc_html__( 'Custom CTA Button', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + ] + ); + + $this->add_control( + 'height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 500, + ], + 'range' => [ + 'px' => [ + 'min' => 50, + 'max' => 1000, + ], + 'em' => [ + 'min' => 5, + 'max' => 100, + ], + 'rem' => [ + 'min' => 5, + 'max' => 100, + ], + ], + ] + ); + + $this->end_controls_section(); + } + + public function render() { + $settings = $this->get_settings_for_display(); + + if ( empty( $settings['url'] ) ) { + echo $this->get_title() . ': ' . esc_html__( 'Please enter a valid URL', 'elementor-pro' ); // XSS ok. + + return; + } + + $height = $settings['height']['size'] . $settings['height']['unit']; + + $attributes = [ + 'class' => 'elementor-facebook-widget fb-page', + 'data-href' => $settings['url'], + 'data-tabs' => implode( ',', $settings['tabs'] ), + 'data-height' => $height, + 'data-width' => '500px', // Try the max possible width + 'data-small-header' => $settings['small_header'] ? 'true' : 'false', + 'data-hide-cover' => $settings['show_cover'] ? 'false' : 'true', // if `show` - don't hide. + 'data-show-facepile' => $settings['show_facepile'] ? 'true' : 'false', + 'data-hide-cta' => $settings['show_cta'] ? 'false' : 'true', // if `show` - don't hide. + // The style prevent's the `widget.handleEmptyWidget` to set it as an empty widget. + 'style' => 'min-height: 1px;height:' . $height, + ]; + + $this->add_render_attribute( 'embed_div', $attributes ); + + echo '
    get_render_attribute_string( 'embed_div' ) . '>
    '; // XSS ok. + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'social'; + } +} diff --git a/modules/sticky/module.php b/modules/sticky/module.php new file mode 100644 index 0000000..4a05f6f --- /dev/null +++ b/modules/sticky/module.php @@ -0,0 +1,201 @@ +add_actions(); + } + + public function get_name() { + return 'sticky'; + } + + /** + * Check if `$element` is an instance of a class in the `$types` array. + * + * @param $element + * @param $types + * + * @return bool + */ + private function is_instance_of( $element, array $types ) { + foreach ( $types as $type ) { + if ( $element instanceof $type ) { + return true; + } + } + + return false; + } + + public function register_controls( Element_Base $element ) { + $element->add_control( + 'sticky', + [ + 'label' => esc_html__( 'Sticky', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'top' => esc_html__( 'Top', 'elementor-pro' ), + 'bottom' => esc_html__( 'Bottom', 'elementor-pro' ), + ], + 'separator' => 'before', + 'render_type' => 'none', + 'frontend_available' => true, + 'assets' => $this->get_asset_conditions_data(), + ] + ); + + // TODO: In Pro 3.5.0, get the active devices using Breakpoints/Manager::get_active_devices_list(). + $active_breakpoint_instances = Plugin::elementor()->breakpoints->get_active_breakpoints(); + // Devices need to be ordered from largest to smallest. + $active_devices = array_reverse( array_keys( $active_breakpoint_instances ) ); + + // Add desktop in the correct position. + if ( in_array( 'widescreen', $active_devices, true ) ) { + $active_devices = array_merge( array_slice( $active_devices, 0, 1 ), [ 'desktop' ], array_slice( $active_devices, 1 ) ); + } else { + $active_devices = array_merge( [ 'desktop' ], $active_devices ); + } + + $sticky_device_options = []; + + foreach ( $active_devices as $device ) { + $label = 'desktop' === $device ? esc_html__( 'Desktop', 'elementor-pro' ) : $active_breakpoint_instances[ $device ]->get_label(); + $sticky_device_options[ $device ] = $label; + } + + $element->add_control( + 'sticky_on', + [ + 'label' => esc_html__( 'Sticky On', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'label_block' => true, + 'default' => $active_devices, + 'options' => $sticky_device_options, + 'condition' => [ + 'sticky!' => '', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $element->add_responsive_control( + 'sticky_offset', + [ + 'label' => esc_html__( 'Offset', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 0, + 'min' => 0, + 'max' => 500, + 'required' => true, + 'condition' => [ + 'sticky!' => '', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + $element->add_responsive_control( + 'sticky_effects_offset', + [ + 'label' => esc_html__( 'Effects Offset', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 0, + 'min' => 0, + 'max' => 1000, + 'required' => true, + 'condition' => [ + 'sticky!' => '', + ], + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + + // Add `Stay In Column` only to the following types: + $types = [ + Element_Section::class, + Widget_Base::class, + ]; + + // TODO: Remove when Container is the default. + if ( Plugin::elementor()->experiments->is_feature_active( 'container' ) ) { + $types[] = \Elementor\Includes\Elements\Container::class; + } + + if ( $this->is_instance_of( $element, $types ) ) { + $conditions = [ + 'sticky!' => '', + ]; + + // Target only inner sections. + // Checking for `$element->get_data( 'isInner' )` in both editor & frontend causes it to work properly on the frontend but + // break on the editor, because the inner section is created in JS and not rendered in PHP. + // So this is a hack to force the editor to show the `sticky_parent` control, and still make it work properly on the frontend. + if ( $element instanceof Element_Section && Plugin::elementor()->editor->is_edit_mode() ) { + $conditions['isInner'] = true; + } + + $element->add_control( + 'sticky_parent', + [ + 'label' => esc_html__( 'Stay In Column', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => $conditions, + 'render_type' => 'none', + 'frontend_available' => true, + ] + ); + } + + $element->add_control( + 'sticky_divider', + [ + 'type' => Controls_Manager::DIVIDER, + ] + ); + } + + private function get_asset_conditions_data() { + return [ + 'scripts' => [ + [ + 'name' => 'e-sticky', + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'sticky', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ]; + } + + private function add_actions() { + add_action( 'elementor/element/section/section_effects/after_section_start', [ $this, 'register_controls' ] ); + add_action( 'elementor/element/container/section_effects/after_section_start', [ $this, 'register_controls' ] ); + add_action( 'elementor/element/common/section_effects/after_section_start', [ $this, 'register_controls' ] ); + } +} diff --git a/modules/table-of-contents/module.php b/modules/table-of-contents/module.php new file mode 100644 index 0000000..504fb5d --- /dev/null +++ b/modules/table-of-contents/module.php @@ -0,0 +1,31 @@ +modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); + + return []; + } +} diff --git a/modules/table-of-contents/widgets/table-of-contents.php b/modules/table-of-contents/widgets/table-of-contents.php new file mode 100644 index 0000000..04df611 --- /dev/null +++ b/modules/table-of-contents/widgets/table-of-contents.php @@ -0,0 +1,924 @@ +experiments->is_feature_active( 'e_font_icon_svg' ) && ! empty( $frontend_settings['icon']['value'] ) ) { + $frontend_settings['icon']['rendered_tag'] = Icons_Manager::render_font_icon( $frontend_settings['icon'] ); + } + + return $frontend_settings; + } + + protected function register_controls() { + $this->start_controls_section( + 'table_of_contents', + [ + 'label' => esc_html__( 'Table of Contents', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'label_block' => true, + 'default' => esc_html__( 'Table of Contents', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'html_tag', + [ + 'label' => esc_html__( 'HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + ], + 'default' => 'h4', + ] + ); + + $this->start_controls_tabs( 'include_exclude_tags', [ 'separator' => 'before' ] ); + + $this->start_controls_tab( 'include', + [ + 'label' => esc_html__( 'Include', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'headings_by_tags', + [ + 'label' => esc_html__( 'Anchors By Tags', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'default' => [ 'h2', 'h3', 'h4', 'h5', 'h6' ], + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + ], + 'label_block' => true, + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'container', + [ + 'label' => esc_html__( 'Container', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'label_block' => true, + 'description' => esc_html__( 'This control confines the Table of Contents to heading elements under a specific container', 'elementor-pro' ), + 'frontend_available' => true, + ] + ); + + $this->end_controls_tab(); // include + + $this->start_controls_tab( 'exclude', + [ + 'label' => esc_html__( 'Exclude', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'exclude_headings_by_selector', + [ + 'label' => esc_html__( 'Anchors By Selector', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'description' => esc_html__( 'CSS selectors, in a comma-separated list', 'elementor-pro' ), + 'default' => [], + 'label_block' => true, + 'frontend_available' => true, + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->end_controls_tab(); // exclude + + $this->end_controls_tabs(); // include_exclude_tags + + $this->add_control( + 'marker_view', + [ + 'label' => esc_html__( 'Marker View', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'numbers', + 'options' => [ + 'numbers' => esc_html__( 'Numbers', 'elementor-pro' ), + 'bullets' => esc_html__( 'Bullets', 'elementor-pro' ), + ], + 'separator' => 'before', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'default' => [ + 'value' => 'fas fa-circle', + 'library' => 'fa-solid', + ], + 'recommended' => [ + 'fa-solid' => [ + 'circle', + 'dot-circle', + 'square-full', + ], + 'fa-regular' => [ + 'circle', + 'dot-circle', + 'square-full', + ], + ], + 'condition' => [ + 'marker_view' => 'bullets', + ], + 'skin' => 'inline', + 'label_block' => false, + 'exclude_inline_options' => [ 'svg' ], + 'frontend_available' => true, + ] + ); + + $this->end_controls_section(); // table_of_contents + + $this->start_controls_section( + 'additional_options', + [ + 'label' => esc_html__( 'Additional Options', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'word_wrap', + [ + 'label' => esc_html__( 'Word Wrap', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'return_value' => 'ellipsis', + 'prefix_class' => 'elementor-toc--content-', + ] + ); + + $this->add_control( + 'minimize_box', + [ + 'label' => esc_html__( 'Minimize Box', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'frontend_available' => true, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'expand_icon', + [ + 'label' => esc_html__( 'Expand Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'default' => [ + 'value' => 'fas fa-chevron-down', + 'library' => 'fa-solid', + ], + 'recommended' => [ + 'fa-solid' => [ + 'chevron-down', + 'angle-down', + 'angle-double-down', + 'caret-down', + 'caret-square-down', + ], + 'fa-regular' => [ + 'caret-square-down', + ], + ], + 'skin' => 'inline', + 'label_block' => false, + 'condition' => [ + 'minimize_box' => 'yes', + ], + ] + ); + + $this->add_control( + 'collapse_icon', + [ + 'label' => esc_html__( 'Collapse Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'default' => [ + 'value' => 'fas fa-chevron-up', + 'library' => 'fa-solid', + ], + 'recommended' => [ + 'fa-solid' => [ + 'chevron-up', + 'angle-up', + 'angle-double-up', + 'caret-up', + 'caret-square-up', + ], + 'fa-regular' => [ + 'caret-square-up', + ], + ], + 'skin' => 'inline', + 'label_block' => false, + 'condition' => [ + 'minimize_box' => 'yes', + ], + ] + ); + + // TODO: For Pro 3.6.0, convert this to the breakpoints utility method introduced in core 3.5.0. + $breakpoints = Plugin::elementor()->breakpoints->get_active_breakpoints(); + + $minimized_on_options = []; + + foreach ( $breakpoints as $breakpoint_key => $breakpoint ) { + // This feature is meant for mobile screens. + if ( 'widescreen' === $breakpoint_key ) { + continue; + } + + $minimized_on_options[ $breakpoint_key ] = sprintf( + /* translators: 1: Breakpoint label, 2: `<` character, 3: Breakpoint value. */ + esc_html__( '%1$s (%2$s %3$dpx)', 'elementor-pro' ), + $breakpoint->get_label(), + '<', + $breakpoint->get_value() + ); + } + + $minimized_on_options['desktop'] = esc_html__( 'Desktop (or smaller)', 'elementor-pro' ); + + $this->add_control( + 'minimized_on', + [ + 'label' => esc_html__( 'Minimized On', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'tablet', + 'options' => $minimized_on_options, + 'prefix_class' => 'elementor-toc--minimized-on-', + 'condition' => [ + 'minimize_box!' => '', + ], + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'hierarchical_view', + [ + 'label' => esc_html__( 'Hierarchical View', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'frontend_available' => true, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'collapse_subitems', + [ + 'label' => esc_html__( 'Collapse Subitems', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'description' => esc_html__( 'The "Collapse" option should only be used if the Table of Contents is made sticky', 'elementor-pro' ), + 'condition' => [ + 'hierarchical_view' => 'yes', + ], + 'frontend_available' => true, + ] + ); + + $this->end_controls_section(); // settings + + $this->start_controls_section( + 'box_style', + [ + 'label' => esc_html__( 'Box', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}}' => '--box-background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--box-border-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'loader_color', + [ + 'label' => esc_html__( 'Loader Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + // Not using CSS var for BC, when not configured: the loader should get the color from the body tag. + '{{WRAPPER}} .elementor-toc__spinner' => 'color: {{VALUE}}; fill: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--box-border-width: {{SIZE}}{{UNIT}}', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--box-border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'header_separator_width', + [ + 'label' => esc_html__( 'Separator Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--separator-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--box-padding: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'min_height', + [ + 'label' => esc_html__( 'Min Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 1000, + ], + 'em' => [ + 'max' => 100, + ], + 'rem' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--box-min-height: {{SIZE}}{{UNIT}}', + ], + 'frontend_available' => true, + 'separator' => 'after', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_shadow', + 'selector' => '{{WRAPPER}}', + ] + ); + + $this->end_controls_section(); // box_style + + $this->start_controls_section( + 'header_style', + [ + 'label' => esc_html__( 'Header', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $logical_start = is_rtl() ? 'right' : 'left'; + $logical_end = is_rtl() ? 'left' : 'right'; + + $this->add_responsive_control( + 'header_text_align', + [ + 'label' => esc_html__( 'Text Align', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => "eicon-text-align-$logical_start", + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => "eicon-text-align-$logical_end", + ], + ], + 'default' => 'start', + 'selectors' => [ + '{{WRAPPER}} .elementor-toc__header-title' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'header_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--header-background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'header_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--header-color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'header_typography', + 'selector' => '{{WRAPPER}} .elementor-toc__header, {{WRAPPER}} .elementor-toc__header-title', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + ] + ); + + $this->add_control( + 'toggle_button_color', + [ + 'label' => esc_html__( 'Icon Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'condition' => [ + 'minimize_box' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-button-color: {{VALUE}}', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'toggle_button_position', + [ + 'label' => esc_html__( 'Icon Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'row-reverse' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => "eicon-h-align-$logical_start", + ], + 'row' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => "eicon-h-align-$logical_end", + ], + ], + 'default' => 'row', + 'toggle' => false, + 'selectors' => [ + '{{WRAPPER}} .elementor-toc__header' => 'flex-direction: {{VALUE}};', + ], + 'condition' => [ + 'minimize_box' => 'yes', + ], + ] + ); + + $this->add_responsive_control( + 'heading_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-toc__header' => 'column-gap: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'minimize_box' => 'yes', + ], + ] + ); + + $this->end_controls_section(); // header_style + + $this->start_controls_section( + 'list_style', + [ + 'label' => esc_html__( 'List', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'max_height', + [ + 'label' => esc_html__( 'Max Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 1000, + ], + 'em' => [ + 'max' => 100, + ], + 'rem' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--toc-body-max-height: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'list_typography', + 'selector' => '{{WRAPPER}} .elementor-toc__list-item', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_responsive_control( + 'list_indent', + [ + 'label' => esc_html__( 'Indent', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'unit' => 'em', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--nested-list-indent: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->start_controls_tabs( 'item_text_style' ); + + $this->start_controls_tab( 'normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'item_text_color_normal', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--item-text-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'item_text_underline_normal', + [ + 'label' => esc_html__( 'Underline', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'selectors' => [ + '{{WRAPPER}}' => '--item-text-decoration: underline', + ], + ] + ); + + $this->end_controls_tab(); // normal + + $this->start_controls_tab( 'hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'item_text_color_hover', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--item-text-hover-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'item_text_underline_hover', + [ + 'label' => esc_html__( 'Underline', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'selectors' => [ + '{{WRAPPER}}' => '--item-text-hover-decoration: underline', + ], + ] + ); + + $this->add_control( + 'item_text_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--item-text-transition-duration: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_tab(); // hover + + $this->start_controls_tab( 'active', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'item_text_color_active', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--item-text-active-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'item_text_underline_active', + [ + 'label' => esc_html__( 'Underline', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'selectors' => [ + '{{WRAPPER}}' => '--item-text-active-decoration: underline', + ], + ] + ); + + $this->end_controls_tab(); // active + + $this->end_controls_tabs(); // item_text_style + + $this->add_control( + 'heading_marker', + [ + 'label' => esc_html__( 'Marker', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'marker_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--marker-color: {{VALUE}}', + ], + ] + ); + + $this->add_responsive_control( + 'marker_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--marker-size: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); // list_style + } + + protected function render() { + $settings = $this->get_settings_for_display(); + $toc_id = 'elementor-toc__' . $this->get_id(); + + $this->add_render_attribute( 'header', 'class', 'elementor-toc__header' ); + + $this->add_render_attribute( + 'body', + [ + 'id' => $toc_id, + 'class' => 'elementor-toc__body', + ] + ); + + if ( $settings['collapse_subitems'] ) { + $this->add_render_attribute( 'body', 'class', 'elementor-toc__list-items--collapsible' ); + } + + if ( 'yes' === $settings['minimize_box'] ) { + $this->add_render_attribute( + 'expand-button', + [ + 'class' => 'elementor-toc__toggle-button elementor-toc__toggle-button--expand', + 'role' => 'button', + 'tabindex' => '0', + 'aria-controls' => $toc_id, + 'aria-expanded' => 'true', + 'aria-label' => esc_html__( 'Open table of contents', 'elementor-pro' ), + ] + ); + $this->add_render_attribute( + 'collapse-button', + [ + 'class' => 'elementor-toc__toggle-button elementor-toc__toggle-button--collapse', + 'role' => 'button', + 'tabindex' => '0', + 'aria-controls' => $toc_id, + 'aria-expanded' => 'true', + 'aria-label' => esc_html__( 'Close table of contents', 'elementor-pro' ), + ] + ); + } + + $html_tag = Utils::validate_html_tag( $settings['html_tag'] ); + ?> +
    print_render_attribute_string( 'header' ); ?>> + < class="elementor-toc__header-title"> + print_unescaped_setting( 'title' ); ?> + > + +
    print_render_attribute_string( 'expand-button' ); ?>> 'true' ] ); ?>
    +
    print_render_attribute_string( 'collapse-button' ); ?>> 'true' ] ); ?>
    + +
    +
    print_render_attribute_string( 'body' ); ?>> +
    + 'eicons', + 'value' => 'eicon-loading', + ], + [ + 'class' => [ + 'elementor-toc__spinner', + 'eicon-animation-spin', + ], + 'aria-hidden' => 'true', + ] + ); ?> +
    +
    + get_locations_manager()->do_location( $location ); +} + +function elementor_location_exits( $location, $check_match = false ) { + /** @var Theme_Builder_Module $theme_builder_module */ + $theme_builder_module = Theme_Builder_Module::instance(); + + return $theme_builder_module->get_locations_manager()->location_exits( $location, $check_match ); +} diff --git a/modules/theme-builder/assets/images/conditions-tab.svg b/modules/theme-builder/assets/images/conditions-tab.svg new file mode 100644 index 0000000..59b8a72 --- /dev/null +++ b/modules/theme-builder/assets/images/conditions-tab.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/theme-builder/classes/conditions-cache.php b/modules/theme-builder/classes/conditions-cache.php new file mode 100644 index 0000000..dc9d32d --- /dev/null +++ b/modules/theme-builder/classes/conditions-cache.php @@ -0,0 +1,143 @@ +refresh(); + } + + /** + * @param Theme_Document $document + * @param array $conditions + * + * @return $this + */ + public function add( Theme_Document $document, array $conditions ) { + $location = $document->get_location(); + if ( $location ) { + if ( ! isset( $this->conditions[ $location ] ) ) { + $this->conditions[ $location ] = []; + } + $this->conditions[ $location ][ $document->get_main_id() ] = $conditions; + } + + return $this; + } + + /** + * @param int $post_id + * + * @return $this + */ + public function remove( $post_id ) { + $post_id = absint( $post_id ); + + foreach ( $this->conditions as $location => $templates ) { + foreach ( $templates as $id => $template ) { + if ( $post_id === $id ) { + unset( $this->conditions[ $location ][ $id ] ); + } + } + } + + return $this; + } + + /** + * @param Theme_Document $document + * @param array $conditions + * + * @return $this + */ + public function update( $document, $conditions ) { + return $this->remove( $document->get_main_id() )->add( $document, $conditions ); + } + + public function save() { + return update_option( self::OPTION_NAME, $this->conditions ); + } + + public function refresh() { + $this->conditions = get_option( self::OPTION_NAME, [] ); + + return $this; + } + + public function clear() { + $this->conditions = []; + + return $this; + } + + public function get_by_location( $location ) { + if ( isset( $this->conditions[ $location ] ) ) { + return $this->conditions[ $location ]; + } + + return []; + } + + public function regenerate() { + $this->clear(); + + $document_types = Plugin::elementor()->documents->get_document_types(); + + $post_types = [ + Source_Local::CPT, + ]; + + foreach ( $document_types as $document_type ) { + if ( $document_type::get_property( 'support_conditions' ) && $document_type::get_property( 'cpt' ) ) { + $post_types = array_merge( $post_types, $document_type::get_property( 'cpt' ) ); + } + } + + $query_args = [ + 'posts_per_page' => -1, + 'post_type' => $post_types, + 'fields' => 'ids', + 'meta_key' => '_elementor_conditions', + ]; + + /** + * Query args for regenerating conditions cache. + * + * Filters the query arguments used for regenerating conditions cache. This hook + * allows developers to alter those arguments. + * + * @since 3.7.0 + * + * @param array $query_args An array of WordPress query arguments. + */ + $query_args = apply_filters( 'elementor/theme/conditions/cache/regenerate/query_args', $query_args ); + + $query = new \WP_Query( $query_args ); + + foreach ( $query->posts as $post_id ) { + $document = Module::instance()->get_document( $post_id ); + + if ( $document ) { + $conditions = $document->get_meta( '_elementor_conditions' ); + $this->add( $document, $conditions ); + } + } + + $this->save(); + + return $this; + } +} diff --git a/modules/theme-builder/classes/conditions-manager.php b/modules/theme-builder/classes/conditions-manager.php new file mode 100644 index 0000000..228f2e6 --- /dev/null +++ b/modules/theme-builder/classes/conditions-manager.php @@ -0,0 +1,558 @@ +cache = new Conditions_Cache(); + + add_action( 'wp_loaded', [ $this, 'register_conditions' ] ); // After Plugins Registered CPT. + add_action( 'wp_trash_post', [ $this, 'purge_post_from_cache' ] ); + add_action( 'untrashed_post', [ $this, 'on_untrash_post' ] ); + add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); + + add_action( 'manage_' . Source_Local::CPT . '_posts_columns', [ $this, 'admin_columns_headers' ] ); + add_action( 'manage_' . Source_Local::CPT . '_posts_custom_column', [ $this, 'admin_columns_content' ], 10, 2 ); + } + + public function on_untrash_post( $post_id ) { + /** @var Module $theme_builder_module */ + $theme_builder_module = Module::instance(); + + $document = $theme_builder_module->get_document( $post_id ); + + if ( $document ) { + $conditions = $document->get_meta( '_elementor_conditions' ); + + if ( $conditions ) { + $this->cache->add( $document, $conditions )->save(); + } + } + } + + public function admin_columns_headers( $posts_columns ) { + $offset = 3; + + $posts_columns = array_slice( $posts_columns, 0, $offset, true ) + [ + 'instances' => esc_html__( 'Instances', 'elementor-pro' ), + ] + array_slice( $posts_columns, $offset, null, true ); + + return $posts_columns; + } + + public function admin_columns_content( $column_name, $post_id ) { + if ( 'instances' !== $column_name ) { + return; + } + + $instances = $this->get_document_instances( $post_id ); + + if ( ! empty( $instances ) ) { + // PHPCS - the method get_document_instances is safe. + echo implode( '
    ', $instances ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } else { + echo esc_html__( 'None', 'elementor-pro' ); + } + } + + /** + * @access public + * + * @param Ajax $ajax_manager + */ + public function register_ajax_actions( $ajax_manager ) { + $ajax_manager->register_ajax_action( 'pro_theme_builder_save_conditions', [ $this, 'ajax_save_theme_template_conditions' ] ); + $ajax_manager->register_ajax_action( 'pro_theme_builder_conditions_check_conflicts', [ $this, 'ajax_check_conditions_conflicts' ] ); + } + + /** + * @throws \Exception + */ + public function ajax_check_conditions_conflicts( $request ) { + $document = Utils::_unstable_get_document_for_edit( $request['editor_post_id'] ); + + $condition = $request['condition']; + + unset( $condition['_id'] ); + + $condition = rtrim( implode( '/', $condition ), '/' ); + + $conflicted = array_map( function ( $conflict ) { + return sprintf( + '%s', $conflict['edit_url'], $conflict['template_title'] + ); + }, $this->get_conditions_conflicts( $document->get_main_id(), $condition ) ); + + if ( empty( $conflicted ) ) { + return ''; + } + + return esc_html__( 'Elementor recognized that you have set this location for other templates: ', 'elementor-pro' ) . + ' ' . + implode( ', ', $conflicted ); + } + + public function get_conditions_conflicts_by_location( $condition, $location, $ignore_post_id = null ) { + /** @var Module $theme_builder_module */ + $theme_builder_module = Module::instance(); + + $location_settings = $theme_builder_module->get_locations_manager()->get_location( $location ); + + if ( ! empty( $location_settings['multiple'] ) ) { + return []; + } + + $conditions_groups = $this->cache->get_by_location( $location ); + + $conflicted = []; + + if ( ! empty( $conditions_groups ) ) { + foreach ( $conditions_groups as $template_id => $conditions ) { + if ( ! get_post( $template_id ) ) { + $this->purge_post_from_cache( $template_id ); + } + + if ( $ignore_post_id === $template_id ) { + continue; + } + + if ( false !== array_search( $condition, $conditions, true ) ) { + $template_title = esc_html( get_the_title( $template_id ) ); + $document = $theme_builder_module->get_document( $template_id ); + + if ( ! $document instanceof Theme_Document ) { + Plugin::$instance->logger->get_logger()->error( "Error fetching document in conditions manager. Template: $template_title" ); + } + + $edit_url = isset( $document ) ? $document->get_edit_url() : ''; + + $conflicted[] = [ + 'template_id' => $template_id, + 'template_title' => $template_title, + 'edit_url' => $edit_url, + ]; + } + } + } + + return $conflicted; + } + + public function get_conditions_conflicts( $post_id, $condition ) { + /** @var Module $theme_builder_module */ + $theme_builder_module = Module::instance(); + + $document = $theme_builder_module->get_document( $post_id ); + + return $this->get_conditions_conflicts_by_location( $condition, $document->get_location(), $post_id ); + } + + /** + * @throws \Exception + */ + public function ajax_save_theme_template_conditions( $request ) { + $document = Utils::_unstable_get_document_for_edit( $request['editor_post_id'] ); + + if ( ! isset( $request['conditions'] ) ) { + $request['conditions'] = []; + } + + $is_saved = $this->save_conditions( $document->get_main_id(), $request['conditions'] ); + + if ( ! $is_saved ) { + throw new \Exception( 'Error while saving conditions.', Exceptions::INTERNAL_SERVER_ERROR ); + } + } + + private function register_condition( $id, $args = [] ) { + if ( isset( $this->conditions[ $id ] ) ) { + return; + } + + $class_name = ucfirst( $id ); + $class_name = '\\ElementorPro\\Modules\\ThemeBuilder\\Conditions\\' . $class_name; + /** @var Condition_Base $condition */ + $condition = new $class_name( $args ); + $this->register_condition_instance( $condition ); + + foreach ( $condition->get_sub_conditions() as $key => $val ) { + if ( is_numeric( $key ) ) { + $id = $val; + $args = []; + } else { + $id = $key; + $args = $val; + } + $this->register_condition( $id, $args ); + } + } + + /** + * @param Condition_Base $instance + */ + public function register_condition_instance( $instance ) { + $this->conditions[ $instance->get_name() ] = $instance; + } + + /** + * @param $id + * + * @return Condition_Base|bool + */ + public function get_condition( $id ) { + return isset( $this->conditions[ $id ] ) ? $this->conditions[ $id ] : false; + } + + public function get_conditions_config() { + $config = []; + + foreach ( $this->conditions as $condition ) { + $config[ $condition->get_name() ] = $condition->get_config(); + } + + return $config; + } + + public function get_document_instances( $post_id ) { + /** @var Module $theme_builder_module */ + $theme_builder_module = Module::instance(); + + $document = $theme_builder_module->get_document( $post_id ); + + $summary = []; + + if ( ! $document ) { + return $summary; + } + + $document_conditions = $this->get_document_conditions( $document ); + + if ( ! empty( $document_conditions ) ) { + foreach ( $document_conditions as $document_condition ) { + if ( 'exclude' === $document_condition['type'] ) { + continue; + } + + $condition_name = ! empty( $document_condition['sub_name'] ) ? $document_condition['sub_name'] : $document_condition['name']; + + $condition = $this->get_condition( $condition_name ); + if ( ! $condition ) { + continue; + } + + if ( ! empty( $document_condition['sub_id'] ) ) { + $instance_label = $condition->get_label() . " #{$document_condition['sub_id']}"; + } else { + $instance_label = $condition->get_all_label(); + } + + $summary[ $condition->get_name() ] = $instance_label; + } + } + + return $summary; + } + + public function register_conditions() { + $this->register_condition( 'general' ); + + /** + * Elementor theme conditions registration. + * + * Fires when a new theme condition is registered. This hook allows developers + * to register new theme conditions. + * + * @param Conditions_Manager $this An instance of conditions manager. + */ + do_action( 'elementor/theme/register_conditions', $this ); + } + + public function save_conditions( $post_id, $conditions ) { + $conditions_to_save = []; + + foreach ( $conditions as $condition ) { + unset( $condition['_id'] ); + $conditions_to_save[] = rtrim( implode( '/', $condition ), '/' ); + } + + /** @var Module $theme_builder_module */ + $theme_builder_module = Module::instance(); + + $document = $theme_builder_module->get_document( $post_id ); + + if ( ! $document ) { + return false; + } + + if ( empty( $conditions_to_save ) ) { + $is_saved = $document->delete_meta( '_elementor_conditions' ); + } else { + $is_saved = $document->update_meta( '_elementor_conditions', $conditions_to_save ); + } + + $this->cache->regenerate(); + + return $is_saved; + } + + public function get_location_templates( $location ) { + $conditions_priority = []; + + $conditions_groups = $this->cache->get_by_location( $location ); + + if ( empty( $conditions_groups ) ) { + return $conditions_priority; + } + + /** @var Module $theme_builder_module */ + $theme_builder_module = Module::instance(); + + $location_manager = $theme_builder_module->get_locations_manager(); + $excludes = []; + + foreach ( $conditions_groups as $theme_template_id => $conditions ) { + /** + * Template ID for theme location templates. + * + * Filters the template ID for theme location templates. + * + * @param int $theme_template_id Template ID. + * @param string $location Theme location. + */ + $theme_template_id = apply_filters( 'elementor/theme/get_location_templates/template_id', $theme_template_id, $location ); + + foreach ( $conditions as $condition ) { + $parsed_condition = $this->parse_condition( $condition ); + + $include = $parsed_condition['type']; + $name = $parsed_condition['name']; + $sub_name = $parsed_condition['sub_name']; + $sub_id = $parsed_condition['sub_id']; + + $is_include = 'include' === $include; + $condition_instance = $this->get_condition( $name ); + + if ( ! $condition_instance ) { + continue; + } + + $condition_pass = $condition_instance->check( [] ); + $sub_condition_instance = null; + + if ( $condition_pass && $sub_name ) { + $sub_condition_instance = $this->get_condition( $sub_name ); + if ( ! $sub_condition_instance ) { + continue; + } + + $args = [ + 'id' => apply_filters( 'elementor/theme/get_location_templates/condition_sub_id', $sub_id, $parsed_condition ), + ]; + + $condition_pass = $sub_condition_instance->check( $args ); + } + + if ( $condition_pass ) { + + $post_status = get_post_status( $theme_template_id ); + + if ( 'publish' !== $post_status ) { + $location_manager->inspector_log( [ + 'location' => $location, + 'document' => $theme_builder_module->get_document( $theme_template_id ), + 'description' => 'Skipped, is not Published', + ] ); + continue; + } + + if ( $is_include ) { + $conditions_priority[ $theme_template_id ] = $this->get_condition_priority( $condition_instance, $sub_condition_instance, $sub_id ); + } else { + $excludes[] = $theme_template_id; + } + } + } // End foreach(). + } // End foreach(). + + foreach ( $excludes as $exclude_id ) { + unset( $conditions_priority[ $exclude_id ] ); + } + + asort( $conditions_priority ); + + return $conditions_priority; + } + + public function get_theme_templates_ids( $location ) { + /** @var Module $theme_builder_module */ + $theme_builder_module = Module::instance(); + + $location_manager = $theme_builder_module->get_locations_manager(); + + // In case the user want to preview any page with a theme_template_id, + // like http://domain.com/any-post/?preview=1&theme_template_id=6453 + $force_template_id = Utils::_unstable_get_super_global_value( $_GET, 'theme_template_id' ); + if ( $force_template_id ) { + $document = $theme_builder_module->get_document( $force_template_id ); + // e.g. header / header + if ( $document && $location === $document->get_location() ) { + $location_manager->inspector_log( [ + 'location' => $location, + 'document' => $document, + 'description' => 'Force Template by URL param', + ] ); + + return [ + $force_template_id => 1, + ]; + } + } + + $current_post_id = get_the_ID(); + $document = $theme_builder_module->get_document( $current_post_id ); + if ( $document && $location === $document->get_location() ) { + $location_manager->inspector_log( [ + 'location' => $location, + 'document' => $document, + 'description' => 'Current Edited Template', + ] ); + + return [ + $current_post_id => 1, + ]; + } + + $templates = $this->get_location_templates( $location ); + + return $templates; + } + + /** + * @param Condition_Base $condition_instance + * @param Condition_Base $sub_condition_instance + * @param int $sub_id + * + * @return mixed + * @throws \Exception + */ + private function get_condition_priority( $condition_instance, $sub_condition_instance, $sub_id ) { + $priority = $condition_instance::get_priority(); + + if ( $sub_condition_instance ) { + if ( $sub_condition_instance::get_priority() < $priority ) { + $priority = $sub_condition_instance::get_priority(); + } + + $priority -= 10; + + if ( $sub_id ) { + $priority -= 10; + } elseif ( 0 === count( $sub_condition_instance->get_sub_conditions() ) ) { + // if no sub conditions - it's more specific. + $priority -= 5; + } + } + + return $priority; + } + + /** + * @param Theme_Document $document + * + * @return array + */ + public function get_document_conditions( $document ) { + $saved_conditions = $document->get_main_meta( '_elementor_conditions' ); + $conditions = []; + + if ( is_array( $saved_conditions ) ) { + foreach ( $saved_conditions as $condition ) { + $conditions[] = $this->parse_condition( $condition ); + } + } + + return $conditions; + } + + protected function parse_condition( $condition ) { + list ( $type, $name, $sub_name, $sub_id ) = array_pad( explode( '/', $condition ), 4, '' ); + + return compact( 'type', 'name', 'sub_name', 'sub_id' ); + } + + /** + * @param $location + * + * @return Theme_Document[] + */ + public function get_documents_for_location( $location ) { + if ( isset( $this->location_cache[ $location ] ) ) { + return $this->location_cache[ $location ]; + } + + $theme_templates_ids = $this->get_theme_templates_ids( $location ); + + /** @var Module $theme_builder_module */ + $theme_builder_module = Module::instance(); + + $location_settings = $theme_builder_module->get_locations_manager()->get_location( $location ); + + $documents = []; + + foreach ( $theme_templates_ids as $theme_template_id => $priority ) { + $document = $theme_builder_module->get_document( $theme_template_id ); + if ( $document ) { + $documents[ $theme_template_id ] = $document; + } + + if ( empty( $location_settings['multiple'] ) ) { + break; + } + } + + $this->location_cache[ $location ] = $documents; + + return $documents; + } + + public function purge_post_from_cache( $post_id ) { + return $this->cache->remove( $post_id )->save(); + } + + public function get_cache() { + return $this->cache; + } + + public function clear_cache() { + $this->cache->clear(); + } + + public function clear_location_cache() { + $this->location_cache = []; + } +} diff --git a/modules/theme-builder/classes/conditions-repeater.php b/modules/theme-builder/classes/conditions-repeater.php new file mode 100644 index 0000000..1211abd --- /dev/null +++ b/modules/theme-builder/classes/conditions-repeater.php @@ -0,0 +1,78 @@ + 'none', + 'fields' => [ + [ + 'name' => 'type', + 'type' => Controls_Manager::SELECT, + 'default' => 'include', + 'options' => [ + 'include' => esc_html__( 'Include', 'elementor-pro' ), + 'exclude' => esc_html__( 'Exclude', 'elementor-pro' ), + ], + ], + [ + 'name' => 'name', + 'type' => Controls_Manager::SELECT, + 'default' => 'general', + 'groups' => [ + [ + 'label' => esc_html__( 'General', 'elementor-pro' ), + 'options' => [], + ], + ], + ], + [ + 'name' => 'sub_name', + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'All', 'elementor-pro' ), + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'name', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + [ + 'name' => 'sub_id', + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'All', 'elementor-pro' ), + ], + 'conditions' => [ + 'terms' => [ + [ + 'name' => 'sub_name', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] ); + } +} diff --git a/modules/theme-builder/classes/control-media-preview.php b/modules/theme-builder/classes/control-media-preview.php new file mode 100644 index 0000000..43e289f --- /dev/null +++ b/modules/theme-builder/classes/control-media-preview.php @@ -0,0 +1,35 @@ + +
    + +
    +
    +
    +
    +
    + <# if ( data.description ) { #> +
    {{{ data.description }}}
    + <# } #> + +
    + set_core_locations(); + + add_filter( 'the_content', [ $this, 'builder_wrapper' ], 9999999 ); // 9999999 = after preview->builder_wrapper + add_filter( 'template_include', [ $this, 'template_include' ], 11 ); // 11 = after WooCommerce. + add_action( 'template_redirect', [ $this, 'register_locations' ] ); + + add_filter( 'elementor/admin/create_new_post/meta', [ $this, 'filter_add_location_meta_on_create_new_post' ] ); + + if ( ! Module::is_preview() ) { + add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_styles' ] ); + } + + add_filter( 'pre_handle_404', [ $this, 'should_allow_pagination_on_single_templates' ], 10, 2 ); + } + + /** + * Fix WP 5.5 pagination issue. + * + * Return true to mark that it's handled and avoid WP to set it as 404. + * + * @see https://github.com/elementor/elementor/issues/12126 + * @see https://core.trac.wordpress.org/ticket/50976 + * + * Based on the logic at \WP::handle_404. + * + * @param $handled - Default false. + * @param $wp_query + * + * @return bool + */ + public function should_allow_pagination_on_single_templates( $handled, $wp_query ) { + if ( $handled || empty( $wp_query->query_vars['page'] ) || empty( $wp_query->post ) ) { + return $handled; + } + + $current_post_id = get_the_ID(); + $documents = Module::instance()->get_conditions_manager()->get_documents_for_location( 'single' ); + + if ( empty( $documents ) ) { + return $handled; + } + + foreach ( $documents as $document ) { + $post_id = $document->get_post()->ID; + + // Will be handled by the pre_handle_404 filter in the posts module. + if ( $current_post_id === $post_id ) { + continue; + } + + $document = Plugin::elementor()->documents->get( $post_id ); + + if ( $this->is_valid_pagination( $document->get_elements_data(), $wp_query->query_vars['page'] ) ) { + $handled = true; + } + } + + return $handled; + } + + public function register_locations() { + // Run Once. + if ( ! did_action( 'elementor/theme/register_locations' ) ) { + /** + * Elementor theme locations registration. + * + * Fires after template files where included but before locations have + * been registered. This hook allows theme developers to register new + * theme locations. + * + * @since 2.0.0 + * + * @param Locations_Manager $this An instance of locations manager. + */ + do_action( 'elementor/theme/register_locations', $this ); + } + } + + public function enqueue_styles() { + $locations = $this->get_locations(); + + if ( empty( $locations ) ) { + return; + } + + if ( ! empty( $this->current_page_template ) ) { + $locations = $this->filter_page_template_locations( $locations ); + } + + $current_post_id = get_the_ID(); + + /** @var Post_CSS[] $css_files */ + $css_files = []; + + foreach ( $locations as $location => $settings ) { + $documents = Module::instance()->get_conditions_manager()->get_documents_for_location( $location ); + foreach ( $documents as $document ) { + $post_id = $document->get_post()->ID; + + // Don't enqueue current post here (let the preview/frontend components to handle it) + if ( $current_post_id !== $post_id ) { + $css_file = new Post_CSS( $post_id ); + $css_files[] = $css_file; + } + } + } + + if ( ! empty( $css_files ) ) { + // Enqueue the frontend styles manually also for pages that don't built with Elementor. + Plugin::elementor()->frontend->enqueue_styles(); + + // Enqueue after the frontend styles to override them. + foreach ( $css_files as $css_file ) { + $css_file->enqueue(); + } + } + } + + public function template_include( $template ) { + $location = ''; + + if ( is_singular() ) { + $document = Plugin::elementor()->documents->get_doc_for_frontend( get_the_ID() ); + if ( $document && $document::get_property( 'support_wp_page_templates' ) ) { + $wp_page_template = $document->get_meta( '_wp_page_template' ); + if ( $wp_page_template && 'default' !== $wp_page_template ) { + $this->inspector_log( [ + 'template' => $template, + 'description' => 'Template File: WP Page Template', + ] ); + $this->current_page_template = $wp_page_template; + return $template; + } + } + } else { + $document = false; + } + + if ( $document && $document instanceof Theme_Document ) { + // For editor preview iframe. + $location = $document->get_location(); + } elseif ( function_exists( 'is_shop' ) && is_shop() ) { + $location = 'archive'; + } elseif ( is_archive() || is_tax() || is_home() || is_search() ) { + $location = 'archive'; + } elseif ( is_singular() || is_404() ) { + $location = 'single'; + } + + if ( $location ) { + $location_settings = $this->get_location( $location ); + $location_documents = Module::instance()->get_conditions_manager()->get_documents_for_location( $location ); + if ( empty( $location_documents ) ) { + $this->inspector_log( [ + 'template' => $template, + 'description' => 'Template File: No Templates for condition', + ] ); + + return $template; + } + + if ( 'single' === $location || 'archive' === $location ) { + $first_key = key( $location_documents ); + $theme_document = $location_documents[ $first_key ]; + + if ( Module::is_preview() && $theme_document->get_autosave_id() ) { + $theme_document = $theme_document->get_autosave(); + } + + $document_page_template = $theme_document->get_settings( 'page_template' ); + if ( $document_page_template ) { + $page_template = $document_page_template; + $this->inspector_log( [ + 'document' => $theme_document, + 'template' => $template, + 'description' => 'Template File: Document Page Template', + ] ); + } + } + } + + /** + * @var \Elementor\Modules\PageTemplates\Module $page_templates_module + */ + $page_templates_module = Plugin::elementor()->modules_manager->get_modules( 'page-templates' ); + + // If is a `content` document or the theme is not support the document location (top header/ sidebar and etc.). + $location_exist = ! empty( $location_settings ); + $is_header_footer = 'header' === $location || 'footer' === $location; + $need_override_location = ! empty( $location_settings['overwrite'] ) && ! $is_header_footer; + + /** + * Override theme location. + * + * Filters the ability to override any Elementor theme location. + * + * @param bool $need_override_location Whether to override theme location. + * @param string $location Location name. + * @param Locations_Manager $this An instance of location manager. + */ + $need_override_location = apply_filters( 'elementor/theme/need_override_location', $need_override_location, $location, $this ); + + if ( $location && empty( $page_template ) && ( ! $location_exist || $need_override_location ) ) { + $page_template = $page_templates_module::TEMPLATE_HEADER_FOOTER; + } + + if ( ! empty( $page_template ) ) { + $template_path = $page_templates_module->get_template_path( $page_template ); + + if ( $template_path ) { + $page_templates_module->set_print_callback( function() use ( $location ) { + Module::instance()->get_locations_manager()->do_location( $location ); + } ); + + $this->inspector_log( [ + 'location' => $location, + 'template' => $template_path, + 'description' => $location_exist ? 'Template File: Location Settings (Override)' : 'Template File: Location not exit', + ] ); + + $template = $template_path; + } + } + + return $template; + } + + /** + * @param string $location + * @param integer $document_id + */ + public function add_doc_to_location( $location, $document_id ) { + if ( isset( $this->locations_skipped[ $location ][ $document_id ] ) ) { + // Don't re-add skipped documents. + return; + } + + if ( ! isset( $this->locations_queue[ $location ] ) ) { + $this->locations_queue[ $location ] = []; + } + + $this->locations_queue[ $location ][ $document_id ] = $document_id; + } + + public function remove_doc_from_location( $location, $document_id ) { + unset( $this->locations_queue[ $location ][ $document_id ] ); + } + + public function skip_doc_in_location( $location, $document_id ) { + $this->remove_doc_from_location( $location, $document_id ); + + if ( ! isset( $this->locations_skipped[ $location ] ) ) { + $this->locations_skipped[ $location ] = []; + } + + $this->locations_skipped[ $location ][ $document_id ] = $document_id; + } + + public function is_printed( $location, $document_id ) { + return isset( $this->locations_printed[ $location ][ $document_id ] ); + } + + public function set_is_printed( $location, $document_id ) { + if ( ! isset( $this->locations_printed[ $location ] ) ) { + $this->locations_printed[ $location ] = []; + } + + $this->locations_printed[ $location ][ $document_id ] = $document_id; + $this->remove_doc_from_location( $location, $document_id ); + } + + public function do_location( $location ) { + /** @var Theme_Document[] $documents_by_conditions */ + $documents_by_conditions = Module::instance()->get_conditions_manager()->get_documents_for_location( $location ); + + foreach ( $documents_by_conditions as $document_id => $document ) { + $this->add_doc_to_location( $location, $document_id ); + } + + // Locations Queue can contain documents that added manually. + if ( empty( $this->locations_queue[ $location ] ) ) { + return false; + } + + if ( is_singular() ) { + Utils::set_global_authordata(); + } + + /** + * Before location content printed. + * + * Fires before Elementor theme location is printed. + * + * The dynamic portion of the hook name, `$location`, refers to the location name. + * + * @since 2.0.0 + * + * @param Locations_Manager $this An instance of locations manager. + */ + do_action( "elementor/theme/before_do_{$location}", $this ); + + while ( ! empty( $this->locations_queue[ $location ] ) ) { + $document_id = key( $this->locations_queue[ $location ] ); + $document = Module::instance()->get_document( $document_id ); + + if ( ! $document || $this->is_printed( $location, $document_id ) ) { + $this->skip_doc_in_location( $location, $document_id ); + continue; + } + + // `$documents_by_conditions` can pe current post even if it's a draft. + if ( empty( $documents_by_conditions[ $document_id ] ) ) { + + $post_status = get_post_status( $document_id ); + + if ( 'publish' !== $post_status ) { + $this->inspector_log( [ + 'location' => $location, + 'document' => $document, + 'description' => 'Added manually but skipped because is not Published', + ] ); + + $this->skip_doc_in_location( $location, $document_id ); + continue; + } + } + + $this->inspector_log( [ + 'location' => $location, + 'document' => $document, + 'description' => isset( $documents_by_conditions[ $document_id ] ) ? 'Added By Condition' : 'Added Manually', + + ] ); + + $this->current_location = $location; + $document->print_content(); + $this->did_locations[] = $this->current_location; + $this->current_location = null; + + $this->set_is_printed( $location, $document_id ); + } + + /** + * After location content printed. + * + * Fires after Elementor theme location is printed. + * + * The dynamic portion of the hook name, `$location`, refers to the location name. + * + * @since 2.0.0 + * + * @param Locations_Manager $this An instance of locations manager. + */ + do_action( "elementor/theme/after_do_{$location}", $this ); + + return true; + } + + public function did_location( $location ) { + return in_array( $location, $this->did_locations, true ); + } + + public function get_current_location() { + return $this->current_location; + } + + public function builder_wrapper( $content ) { + $post_id = get_the_ID(); + + if ( $post_id ) { + $document = Module::instance()->get_document( $post_id ); + if ( $document ) { + $document_location = $document->get_location(); + $location_settings = $this->get_location( $document_location ); + // If is a `content` document or the theme is not support the document location (header/footer and etc.). + if ( $location_settings && ! $location_settings['edit_in_content'] ) { + $content = '
    ' . esc_html__( 'Content Area', 'elementor-pro' ) . '
    '; + } + } + } + + return $content; + } + + public function get_locations( $filter_args = [] ) { + $this->register_locations(); + + if ( is_string( $filter_args ) ) { + _deprecated_argument( __FUNCTION__, '2.4.0', 'Passing a location name is deprecated. Use `get_location` instead.' ); + return $this->get_location( $filter_args ); + } + + return wp_list_filter( $this->locations, $filter_args ); + } + + public function get_location( $location ) { + $locations = $this->get_locations(); + + if ( isset( $locations[ $location ] ) ) { + $location_config = $locations[ $location ]; + } else { + $location_config = []; + } + + return $location_config; + } + + public function get_doc_location( $post_id ) { + /** @var Theme_Document $document */ + $document = Plugin::elementor()->documents->get( $post_id ); + + return $document->get_location(); + } + + public function get_core_locations() { + return $this->core_locations; + } + + public function register_all_core_location() { + foreach ( $this->core_locations as $location => $settings ) { + $this->register_location( $location, $settings ); + } + } + + public function register_location( $location, $args = [] ) { + $args = wp_parse_args( $args, [ + 'label' => $location, + 'multiple' => false, + 'public' => true, + 'edit_in_content' => true, + 'hook' => 'elementor/theme/' . $location, + ] ); + + $this->locations[ $location ] = $args; + + add_action( $args['hook'], function() use ( $location, $args ) { + $did_location = Module::instance()->get_locations_manager()->do_location( $location ); + + if ( $did_location && ! empty( $args['remove_hooks'] ) ) { + foreach ( $args['remove_hooks'] as $item ) { + remove_action( $args['hook'], $item ); + } + } + }, 5 ); + } + + public function register_core_location( $location, $args = [] ) { + if ( ! isset( $this->core_locations[ $location ] ) ) { + /* translators: %s: Location name. */ + wp_die( esc_html( sprintf( esc_html__( 'Location \'%s\' is not a core location.', 'elementor-pro' ), $location ) ) ); + } + + $args = array_replace_recursive( $this->core_locations[ $location ], $args ); + + $this->register_location( $location, $args ); + } + + public function location_exits( $location = '', $check_match = false ) { + $location_exits = ! ! $this->get_location( $location ); + + if ( $location_exits && $check_match ) { + $location_exits = ! ! Module::instance()->get_conditions_manager()->get_documents_for_location( $location ); + } + + return $location_exits; + } + + public function filter_add_location_meta_on_create_new_post( $meta ) { + //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here. + $meta_location = Utils::_unstable_get_super_global_value( $_GET, 'meta_location' ); + if ( $meta_location ) { + $meta[ Theme_Document::LOCATION_META_KEY ] = $meta_location; + } + + return $meta; + } + + private function set_core_locations() { + $this->core_locations = [ + 'header' => [ + 'is_core' => true, + 'public' => false, + 'label' => esc_html__( 'Header', 'elementor-pro' ), + 'edit_in_content' => false, + ], + 'footer' => [ + 'is_core' => true, + 'public' => false, + 'label' => esc_html__( 'Footer', 'elementor-pro' ), + 'edit_in_content' => false, + ], + 'archive' => [ + 'is_core' => true, + 'public' => false, + 'overwrite' => true, + 'label' => esc_html__( 'Archive', 'elementor-pro' ), + 'edit_in_content' => true, + ], + 'single' => [ + 'is_core' => true, + 'public' => false, + 'label' => esc_html__( 'Single', 'elementor-pro' ), + 'edit_in_content' => true, + ], + ]; + } + + public function inspector_log( $args ) { + $inspector_enabled = method_exists( Plugin::elementor()->inspector, 'is_enabled' ) && Plugin::elementor()->inspector->is_enabled(); + if ( ! $inspector_enabled ) { + return; + } + + $title = []; + $url = ''; + + if ( isset( $args['location'] ) ) { + $location_settings = $this->get_location( $args['location'] ); + if ( $location_settings ) { + $args['location'] = $location_settings['label']; + } + $title[] = 'Location: ' . $args['location']; + } + + if ( isset( $args['description'] ) ) { + $title[] = $args['description']; + } + + if ( ! empty( $args['document'] ) ) { + $title[] = esc_html( $args['document']->get_post()->post_title ); + $url = $args['document']->get_edit_url(); + } + + if ( isset( $args['template'] ) ) { + $title[] = Plugin::elementor()->inspector->parse_template_path( $args['template'] ); + } + + $title = implode( ' > ', $title ); + + Plugin::elementor()->inspector->add_log( 'Theme', $title, $url ); + } + + private function filter_page_template_locations( array $locations ) { + $templates_to_filter = [ + PageTemplatesModule::TEMPLATE_CANVAS, + PageTemplatesModule::TEMPLATE_HEADER_FOOTER, + ]; + + if ( ! in_array( $this->current_page_template, $templates_to_filter, true ) ) { + return $locations; + } + + if ( PageTemplatesModule::TEMPLATE_CANVAS === $this->current_page_template ) { + $allowed_core = []; + } else { + $allowed_core = [ 'header', 'footer' ]; + } + + foreach ( $locations as $location => $settings ) { + if ( ! empty( $settings['is_core'] ) && ! in_array( $location, $allowed_core, true ) ) { + unset( $locations[ $location ] ); + } + } + + return $locations; + } +} diff --git a/modules/theme-builder/classes/preview-manager.php b/modules/theme-builder/classes/preview-manager.php new file mode 100644 index 0000000..3516827 --- /dev/null +++ b/modules/theme-builder/classes/preview-manager.php @@ -0,0 +1,78 @@ +get_document( $current_post_id ); + + if ( $document ) { + // Show all taxonomies + unset( $taxonomy_args['object_type'] ); + } + + return $taxonomy_args; + } + + /** + * @access public + * + * @param $query_vars array + * + * @return array + */ + public function filter_query_control_args( $query_vars ) { + $document = Plugin::elementor()->documents->get_doc_or_auto_save( get_the_ID() ); + + if ( $document && $document instanceof Theme_Document ) { + $query_vars = $document->get_preview_as_query_args(); + } + + return $query_vars; + } + + /** + * @access public + */ + public function switch_to_preview_query() { + $current_post_id = get_the_ID(); + $document = Plugin::elementor()->documents->get_doc_or_auto_save( $current_post_id ); + + if ( ! $document || ! $document instanceof Theme_Document ) { + return; + } + + $new_query_vars = $document->get_preview_as_query_args(); + + Plugin::elementor()->db->switch_to_query( $new_query_vars, true ); + + $document->after_preview_switch_to_query(); + } + + /** + * @access public + */ + public function restore_current_query() { + Plugin::elementor()->db->restore_current_query(); + } +} diff --git a/modules/theme-builder/classes/template-conditions.php b/modules/theme-builder/classes/template-conditions.php new file mode 100644 index 0000000..21a8f04 --- /dev/null +++ b/modules/theme-builder/classes/template-conditions.php @@ -0,0 +1,27 @@ +add_control( + 'conditions', + [ + 'section' => 'settings', + 'type' => Conditions_Repeater::CONTROL_TYPE, + ] + ); + } +} diff --git a/modules/theme-builder/classes/templates-types-manager.php b/modules/theme-builder/classes/templates-types-manager.php new file mode 100644 index 0000000..557518e --- /dev/null +++ b/modules/theme-builder/classes/templates-types-manager.php @@ -0,0 +1,51 @@ +documents->get_document_types( $args ); + + foreach ( $document_types as $type => $document_type ) { + $properties = $document_type::get_properties(); + + if ( ( new $document_type() ) instanceof Documents\Theme_Document ) { + $config[ $type ] = $properties; + } + } + + return $config; + } + + public function register_documents() { + $this->docs_types = [ + 'section' => Documents\Section::get_class_full_name(), + 'header' => Documents\Header::get_class_full_name(), + 'footer' => Documents\Footer::get_class_full_name(), + 'single' => Documents\Single::get_class_full_name(), + 'single-post' => Documents\Single_Post::get_class_full_name(), + 'single-page' => Documents\Single_Page::get_class_full_name(), + 'archive' => Documents\Archive::get_class_full_name(), + 'search-results' => Documents\Search_Results::get_class_full_name(), + 'error-404' => Documents\Error_404::get_class_full_name(), + ]; + + foreach ( $this->docs_types as $type => $class_name ) { + Plugin::elementor()->documents->register_document_type( $type, $class_name ); + } + } +} diff --git a/modules/theme-builder/classes/theme-support.php b/modules/theme-builder/classes/theme-support.php new file mode 100644 index 0000000..1ca79b3 --- /dev/null +++ b/modules/theme-builder/classes/theme-support.php @@ -0,0 +1,130 @@ +get_template() ) { + case 'generatepress': + new GeneratePress_Theme_Support(); + break; + case 'elementor-safe': + new Safe_Mode_Theme_Support(); + break; + } + + add_action( 'elementor/theme/register_locations', [ $this, 'after_register_locations' ], 99 ); + } + + /** + * @param Locations_Manager $location_manager + */ + public function after_register_locations( $location_manager ) { + $core_locations = $location_manager->get_core_locations(); + $overwrite_header_location = false; + $overwrite_footer_location = false; + + foreach ( $core_locations as $location => $settings ) { + if ( ! $location_manager->get_location( $location ) ) { + if ( 'header' === $location ) { + $overwrite_header_location = true; + } elseif ( 'footer' === $location ) { + $overwrite_footer_location = true; + } + $location_manager->register_core_location( $location, [ + 'overwrite' => true, + ] ); + } + } + + if ( $overwrite_header_location || $overwrite_footer_location ) { + /** @var Module $theme_builder_module */ + $theme_builder_module = Module::instance(); + + $conditions_manager = $theme_builder_module->get_conditions_manager(); + + $headers = $conditions_manager->get_documents_for_location( 'header' ); + $footers = $conditions_manager->get_documents_for_location( 'footer' ); + + if ( ! empty( $headers ) || ! empty( $footers ) ) { + add_action( 'get_header', [ $this, 'get_header' ] ); + add_action( 'get_footer', [ $this, 'get_footer' ] ); + add_filter( 'show_admin_bar', [ $this, 'filter_admin_bar_from_body_open' ] ); + } + } + } + + public function get_header( $name ) { + require __DIR__ . '/../views/theme-support-header.php'; + + $templates = []; + $name = (string) $name; + if ( '' !== $name ) { + $templates[] = "header-{$name}.php"; + } + + $templates[] = 'header.php'; + + // Avoid running wp_head hooks again + remove_all_actions( 'wp_head' ); + ob_start(); + // It cause a `require_once` so, in the get_header it self it will not be required again. + locate_template( $templates, true ); + ob_get_clean(); + } + + /** + * Don't show admin bar on `wp_body_open` because the theme header HTML is ignored via `$this->get_header()`. + * + * @param bool $show_admin_bar + * + * @return bool + */ + public function filter_admin_bar_from_body_open( $show_admin_bar ) { + global $wp_current_filter; + + // A flag to mark if $show_admin_bar is switched to false during this filter, + // if so, it needed to switch back on the next filter (wp_footer). + static $switched = false; + + if ( $show_admin_bar && in_array( 'wp_body_open', $wp_current_filter ) ) { + $show_admin_bar = false; + $switched = true; + } elseif ( $switched ) { + $show_admin_bar = true; + } + + return $show_admin_bar; + } + + public function get_footer( $name ) { + require __DIR__ . '/../views/theme-support-footer.php'; + + $templates = []; + $name = (string) $name; + if ( '' !== $name ) { + $templates[] = "footer-{$name}.php"; + } + + $templates[] = 'footer.php'; + + ob_start(); + // It cause a `require_once` so, in the get_header it self it will not be required again. + locate_template( $templates, true ); + ob_get_clean(); + } +} diff --git a/modules/theme-builder/conditions/any-child-of-term.php b/modules/theme-builder/conditions/any-child-of-term.php new file mode 100644 index 0000000..1c5f6ae --- /dev/null +++ b/modules/theme-builder/conditions/any-child-of-term.php @@ -0,0 +1,46 @@ +taxonomy->name; + } + + public function get_label() { + /* translators: %s: Singular taxonomy label. */ + return sprintf( esc_html__( 'Any Child %s Of', 'elementor-pro' ), $this->taxonomy->labels->singular_name ); + } + + public function __construct( $data ) { + parent::__construct( $data ); + + $this->taxonomy = $data['object']; + } + + public function check( $args ) { + $id = (int) $args['id']; + /** + * @var \WP_Term $current + */ + $current = get_queried_object(); + if ( ! $this->is_term() || 0 === $current->parent ) { + return false; + } + + while ( $current->parent > 0 ) { + if ( $id === $current->parent ) { + return true; + } + $current = get_term_by( 'id', $current->parent, $current->taxonomy ); + } + + return $id === $current->parent; + } +} diff --git a/modules/theme-builder/conditions/any-child-of.php b/modules/theme-builder/conditions/any-child-of.php new file mode 100644 index 0000000..4acf2f6 --- /dev/null +++ b/modules/theme-builder/conditions/any-child-of.php @@ -0,0 +1,28 @@ + $label ) { + if ( ! get_post_type_archive_link( $post_type ) ) { + continue; + } + + $condition = new Post_Type_Archive( [ + 'post_type' => $post_type, + ] ); + + $this->register_sub_condition( $condition ); + } + } + + public function check( $args ) { + $is_archive = is_archive() || is_home() || is_search(); + + // WooCommerce is handled by `woocommerce` module. + if ( $is_archive && class_exists( 'woocommerce' ) && is_woocommerce() ) { + $is_archive = false; + } + + return $is_archive; + } +} diff --git a/modules/theme-builder/conditions/author.php b/modules/theme-builder/conditions/author.php new file mode 100644 index 0000000..9f4acda --- /dev/null +++ b/modules/theme-builder/conditions/author.php @@ -0,0 +1,47 @@ +add_control( + 'author_id', + [ + 'section' => 'settings', + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'dropdownCssClass' => 'elementor-conditions-select2-dropdown', + ], + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_AUTHOR, + ], + ] + ); + } +} diff --git a/modules/theme-builder/conditions/by-author.php b/modules/theme-builder/conditions/by-author.php new file mode 100644 index 0000000..62b1f00 --- /dev/null +++ b/modules/theme-builder/conditions/by-author.php @@ -0,0 +1,47 @@ +add_control( + 'author_id', + [ + 'section' => 'settings', + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'dropdownCssClass' => 'elementor-conditions-select2-dropdown', + ], + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_AUTHOR, + ], + ] + ); + } +} diff --git a/modules/theme-builder/conditions/child-of-term.php b/modules/theme-builder/conditions/child-of-term.php new file mode 100644 index 0000000..642bf0d --- /dev/null +++ b/modules/theme-builder/conditions/child-of-term.php @@ -0,0 +1,39 @@ +taxonomy->name; + } + + public function get_label() { + /* translators: %s: Singular taxonomy label. */ + return sprintf( esc_html__( 'Direct Child %s Of', 'elementor-pro' ), $this->taxonomy->labels->singular_name ); + } + + public function __construct( $data ) { + parent::__construct( $data ); + + $this->taxonomy = $data['object']; + } + + public function is_term() { + $taxonomy = $this->taxonomy->name; + $current = get_queried_object(); + return ( $current && isset( $current->taxonomy ) && $taxonomy === $current->taxonomy ); + } + + public function check( $args ) { + $id = (int) $args['id']; + $current = get_queried_object(); + + return $this->is_term() && $id === $current->parent; + } +} diff --git a/modules/theme-builder/conditions/child-of.php b/modules/theme-builder/conditions/child-of.php new file mode 100644 index 0000000..0cde175 --- /dev/null +++ b/modules/theme-builder/conditions/child-of.php @@ -0,0 +1,62 @@ + true, + 'public' => true, + ] ); + + $this->add_control( + 'parent_id', + [ + 'section' => 'settings', + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'dropdownCssClass' => 'elementor-conditions-select2-dropdown', + ], + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_POST, + 'query' => [ + 'post_type' => array_keys( $hierarchical_post_types ), + ], + ], + ] + ); + } +} diff --git a/modules/theme-builder/conditions/condition-base.php b/modules/theme-builder/conditions/condition-base.php new file mode 100644 index 0000000..1622422 --- /dev/null +++ b/modules/theme-builder/conditions/condition-base.php @@ -0,0 +1,68 @@ +get_name(); + } + + public static function get_type() { + throw new \Exception( 'Please overwrite the method.', Exceptions::INTERNAL_SERVER_ERROR ); + } + + public function check( $args ) { + return false; + } + + public function get_sub_conditions() { + return $this->sub_conditions; + } + + public function get_all_label() { + return $this->get_label(); + } + + protected function get_initial_config() { + $config = parent::get_initial_config(); + + $config['label'] = $this->get_label(); + $config['sub_conditions'] = $this->get_sub_conditions(); + $config['all_label'] = $this->get_all_label(); + + return $config; + } + + public function register_sub_conditions() {} + + /** + * @param self $condition + */ + public function register_sub_condition( $condition ) { + $conditions_manager = Module::instance()->get_conditions_manager(); + $conditions_manager->register_condition_instance( $condition ); + $this->sub_conditions[] = $condition->get_name(); + } + + public function __construct( array $data = [] ) { + parent::__construct( $data ); + + $this->register_sub_conditions(); + } +} diff --git a/modules/theme-builder/conditions/date.php b/modules/theme-builder/conditions/date.php new file mode 100644 index 0000000..8598590 --- /dev/null +++ b/modules/theme-builder/conditions/date.php @@ -0,0 +1,29 @@ +taxonomy = $data['object']; + } + + public function get_name() { + return 'in_' . $this->taxonomy->name . '_children'; + } + + public function get_label() { + /* translators: %s: Taxonomy label. */ + return sprintf( esc_html__( 'In Child %s', 'elementor-pro' ), $this->taxonomy->labels->name ); + } + + public function check( $args ) { + $id = (int) $args['id']; + if ( ! is_singular() || ! $id ) { + return false; + } + $child_terms = get_term_children( $id, $this->taxonomy->name ); + + return ! empty( $child_terms ) && has_term( $child_terms, $this->taxonomy->name ); + } +} diff --git a/modules/theme-builder/conditions/in-taxonomy.php b/modules/theme-builder/conditions/in-taxonomy.php new file mode 100644 index 0000000..54a50bb --- /dev/null +++ b/modules/theme-builder/conditions/in-taxonomy.php @@ -0,0 +1,65 @@ +taxonomy = $data['object']; + } + + public function get_name() { + return 'in_' . $this->taxonomy->name; + } + + public function get_label() { + /* translators: %s: Taxonomy label. */ + return sprintf( esc_html__( 'In %s', 'elementor-pro' ), $this->taxonomy->labels->singular_name ); + } + + public function check( $args ) { + return is_singular() && has_term( (int) $args['id'], $this->taxonomy->name ); + } + + protected function register_controls() { + $this->add_control( + 'taxonomy', + [ + 'section' => 'settings', + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'dropdownCssClass' => 'elementor-conditions-select2-dropdown', + ], + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_TAX, + 'display' => 'detailed', + 'by_field' => 'term_id', + 'query' => [ + 'taxonomy' => $this->taxonomy->name, + ], + ], + ] + ); + } +} diff --git a/modules/theme-builder/conditions/not-found404.php b/modules/theme-builder/conditions/not-found404.php new file mode 100644 index 0000000..085dd7c --- /dev/null +++ b/modules/theme-builder/conditions/not-found404.php @@ -0,0 +1,29 @@ +post_type = get_post_type_object( $data['post_type'] ); + $taxonomies = get_object_taxonomies( $data['post_type'], 'objects' ); + $this->post_taxonomies = wp_filter_object_list( $taxonomies, [ + 'public' => true, + 'show_in_nav_menus' => true, + ] ); + + parent::__construct(); + } + + public function get_name() { + return $this->post_type->name . '_archive'; + } + + public function get_label() { + /* translators: %s: Post type label. */ + return sprintf( esc_html__( '%s Archive', 'elementor-pro' ), $this->post_type->label ); + } + + public function get_all_label() { + /* translators: %s: Post type label. */ + return sprintf( esc_html__( '%s Archive', 'elementor-pro' ), $this->post_type->label ); + } + + public function register_sub_conditions() { + foreach ( $this->post_taxonomies as $slug => $object ) { + $condition = new Taxonomy( [ + 'object' => $object, + ] ); + + $this->register_sub_condition( $condition ); + + if ( ! $object->hierarchical ) { + continue; + } + + $sub_conditions = [ + 'Child_Of_Term', + 'Any_Child_Of_Term', + ]; + + foreach ( $sub_conditions as $class_name ) { + $full_class_name = __NAMESPACE__ . '\\' . $class_name; + $this->register_sub_condition( new $full_class_name( [ 'object' => $object ] ) ); + } + } + } + + public function check( $args ) { + return is_post_type_archive( $this->post_type->name ) || ( 'post' === $this->post_type->name && is_home() ); + } +} diff --git a/modules/theme-builder/conditions/post-type-by-author.php b/modules/theme-builder/conditions/post-type-by-author.php new file mode 100644 index 0000000..e9fc18d --- /dev/null +++ b/modules/theme-builder/conditions/post-type-by-author.php @@ -0,0 +1,56 @@ +post_type = $post_type; + } + + public function get_name() { + return $this->post_type->name . '_by_author'; + } + + public function get_label() { + /* translators: %s: Post type label. */ + return sprintf( esc_html__( '%s By Author', 'elementor-pro' ), $this->post_type->label ); + } + + public function check( $args = null ) { + return is_singular( $this->post_type->name ) && get_post_field( 'post_author' ) === $args['id']; + } + + protected function register_controls() { + $this->add_control( + 'author_id', + [ + 'section' => 'settings', + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'dropdownCssClass' => 'elementor-conditions-select2-dropdown', + ], + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_AUTHOR, + ], + ] + ); + } +} diff --git a/modules/theme-builder/conditions/post.php b/modules/theme-builder/conditions/post.php new file mode 100644 index 0000000..4f95b7c --- /dev/null +++ b/modules/theme-builder/conditions/post.php @@ -0,0 +1,95 @@ +post_type = get_post_type_object( $data['post_type'] ); + $taxonomies = get_object_taxonomies( $data['post_type'], 'objects' ); + $this->post_taxonomies = wp_filter_object_list( $taxonomies, [ + 'public' => true, + 'show_in_nav_menus' => true, + ] ); + + parent::__construct(); + } + + public function get_name() { + return $this->post_type->name; + } + + public function get_label() { + return $this->post_type->labels->singular_name; + } + + public function get_all_label() { + return $this->post_type->label; + } + + public function check( $args ) { + if ( isset( $args['id'] ) ) { + $id = (int) $args['id']; + if ( $id ) { + return is_singular() && get_queried_object_id() === $id; + } + } + + return is_singular( $this->post_type->name ); + } + + public function register_sub_conditions() { + foreach ( $this->post_taxonomies as $slug => $object ) { + $in_taxonomy = new In_Taxonomy( [ + 'object' => $object, + ] ); + $this->register_sub_condition( $in_taxonomy ); + + if ( $object->hierarchical ) { + $in_sub_term = new In_Sub_Term( [ + 'object' => $object, + ] ); + $this->register_sub_condition( $in_sub_term ); + } + } + + $by_author = new Post_Type_By_Author( $this->post_type ); + $this->register_sub_condition( $by_author ); + + } + + protected function register_controls() { + $this->add_control( + 'post_id', + [ + 'section' => 'settings', + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'dropdownCssClass' => 'elementor-conditions-select2-dropdown', + ], + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_POST, + 'query' => [ + 'post_type' => $this->get_name(), + ], + ], + ] + ); + } +} diff --git a/modules/theme-builder/conditions/search.php b/modules/theme-builder/conditions/search.php new file mode 100644 index 0000000..8559ddd --- /dev/null +++ b/modules/theme-builder/conditions/search.php @@ -0,0 +1,29 @@ +label; + + foreach ( $post_types as $post_type => $label ) { + $condition = new Post( [ + 'post_type' => $post_type, + ] ); + + $this->register_sub_condition( $condition ); + } + + $this->sub_conditions[] = 'child_of'; + + $this->sub_conditions[] = 'any_child_of'; + + $this->sub_conditions[] = 'by_author'; + + // Last condition. + $this->sub_conditions[] = 'not_found404'; + } + + public function check( $args ) { + return ( is_singular() && ! is_embed() ) || is_404(); + } +} diff --git a/modules/theme-builder/conditions/taxonomy.php b/modules/theme-builder/conditions/taxonomy.php new file mode 100644 index 0000000..ebc44de --- /dev/null +++ b/modules/theme-builder/conditions/taxonomy.php @@ -0,0 +1,70 @@ +taxonomy = $data['object']; + } + + public function get_name() { + return $this->taxonomy->name; + } + + public function get_label() { + return $this->taxonomy->label; + } + + public function check( $args ) { + $taxonomy = $this->get_name(); + $id = (int) $args['id']; + + if ( 'category' === $taxonomy ) { + return is_category( $id ); + } + + if ( 'post_tag' === $taxonomy ) { + return is_tag( $id ); + } + + return is_tax( $taxonomy, $id ); + } + + protected function register_controls() { + $this->add_control( + 'taxonomy', + [ + 'section' => 'settings', + 'type' => QueryModule::QUERY_CONTROL_ID, + 'options' => [ + '' => esc_html__( 'All', 'elementor-pro' ), + ], + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_TAX, + 'by_field' => 'term_id', + 'query' => [ + 'taxonomy' => $this->taxonomy->name, + ], + ], + ] + ); + } +} diff --git a/modules/theme-builder/documents/archive-single-base.php b/modules/theme-builder/documents/archive-single-base.php new file mode 100644 index 0000000..eb0f9ea --- /dev/null +++ b/modules/theme-builder/documents/archive-single-base.php @@ -0,0 +1,60 @@ +save_sub_type_condition(); + } + + private function save_sub_type_condition() { + $conditions_manager = ThemeBuilderModule::instance()->get_conditions_manager(); + + $sub_type = Utils::_unstable_get_super_global_value( $_REQUEST, self::REMOTE_CATEGORY_META_KEY ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended + + if ( ! empty( $sub_type ) && $conditions_manager->get_condition( $sub_type ) ) { + $this->update_meta( self::REMOTE_CATEGORY_META_KEY, $sub_type ); + + $conditions_manager->save_conditions( $this->post->ID, [ + [ + 'include', + $this->get_property( 'condition_type' ), + $sub_type, + ], + ] ); + } + } +} diff --git a/modules/theme-builder/documents/archive.php b/modules/theme-builder/documents/archive.php new file mode 100644 index 0000000..051b960 --- /dev/null +++ b/modules/theme-builder/documents/archive.php @@ -0,0 +1,105 @@ + esc_html__( 'What is an Archive Template?', 'elementor-pro' ), + 'content' => esc_html__( 'An archive template allows you to easily design the layout and style of archive pages - those pages that show a list of posts (e.g. a blog’s list of recent posts), which may be filtered by terms such as authors, categories, tags, search results, etc.', 'elementor-pro' ), + 'tip' => esc_html__( 'If you’d like a different style for a specific category, it’s easy to create a separate archive template whose condition is to only display when users are viewing that category’s list of posts.', 'elementor-pro' ), + 'docs' => 'https://go.elementor.com/app-theme-builder-archive', + 'video_url' => 'https://www.youtube.com/embed/wxElpEh9bfA', + ]; + } + + protected static function get_editor_panel_categories() { + $categories = [ + 'theme-elements-archive' => [ + 'title' => esc_html__( 'Archive', 'elementor-pro' ), + ], + ]; + + return $categories + parent::get_editor_panel_categories(); + } + + public static function get_preview_as_default() { + return 'archive/recent_posts'; + } + + public static function get_preview_as_options() { + $post_type_archives = []; + + $taxonomies = []; + + $post_types = Module::get_public_post_types(); + + foreach ( $post_types as $post_type => $label ) { + $post_type_object = get_post_type_object( $post_type ); + + if ( $post_type_object->has_archive ) { + /* translators: %s: Post type label. */ + $post_type_archives[ 'post_type_archive/' . $post_type ] = sprintf( esc_html__( '%s Archive', 'elementor-pro' ), $post_type_object->label ); + } + + $post_type_taxonomies = get_object_taxonomies( $post_type, 'objects' ); + + $post_type_taxonomies = wp_filter_object_list( $post_type_taxonomies, [ + 'public' => true, + 'show_in_nav_menus' => true, + ] ); + + foreach ( $post_type_taxonomies as $slug => $object ) { + /* translators: %s: Taxonomy label. */ + $taxonomies[ 'taxonomy/' . $slug ] = sprintf( esc_html__( '%s Archive', 'elementor-pro' ), $object->label ); + } + } + + $options = [ + 'archive/recent_posts' => esc_html__( 'Recent Posts', 'elementor-pro' ), + 'archive/date' => esc_html__( 'Date Archive', 'elementor-pro' ), + 'archive/author' => esc_html__( 'Author Archive', 'elementor-pro' ), + 'search' => esc_html__( 'Search Results', 'elementor-pro' ), + ]; + + $options += $taxonomies + $post_type_archives; + + return [ + 'archive' => [ + 'label' => esc_html__( 'Archive', 'elementor-pro' ), + 'options' => $options, + ], + ]; + } +} diff --git a/modules/theme-builder/documents/error-404.php b/modules/theme-builder/documents/error-404.php new file mode 100644 index 0000000..dbbdd27 --- /dev/null +++ b/modules/theme-builder/documents/error-404.php @@ -0,0 +1,53 @@ + esc_html__( 'What is a 404 Page Template?', 'elementor-pro' ), + 'content' => esc_html__( 'A 404 page template allows you to easily design the layout and style of the page that is displayed when a visitor arrives at a page that does not exist.', 'elementor-pro' ), + 'tip' => esc_html__( 'Keep your site\'s visitors happy when they get lost by displaying your recent posts, a search bar, or any information that might help the user find what they were looking for.', 'elementor-pro' ), + 'docs' => 'https://go.elementor.com/app-theme-builder-error-404', + 'video_url' => 'https://www.youtube.com/embed/ACCNp9tBMQg', + ]; + } + + public static function get_preview_as_options() { + return [ + 'page/404' => esc_html__( '404', 'elementor-pro' ), + ]; + } + + protected function get_remote_library_config() { + $config = parent::get_remote_library_config(); + + $config['category'] = '404 page'; + + return $config; + } +} diff --git a/modules/theme-builder/documents/footer.php b/modules/theme-builder/documents/footer.php new file mode 100644 index 0000000..f28c0ae --- /dev/null +++ b/modules/theme-builder/documents/footer.php @@ -0,0 +1,43 @@ + esc_html__( 'What is a Footer Template?', 'elementor-pro' ), + 'content' => esc_html__( 'The footer template allows you to easily design and edit custom WordPress footers without the limits of your theme’s footer design constraints', 'elementor-pro' ), + 'tip' => esc_html__( 'You can create multiple footers, and assign each to different areas of your site.', 'elementor-pro' ), + 'docs' => 'https://go.elementor.com/app-theme-builder-footer', + 'video_url' => 'https://www.youtube.com/embed/xa8DoR4tQrY', + ]; + } +} diff --git a/modules/theme-builder/documents/header-footer-base.php b/modules/theme-builder/documents/header-footer-base.php new file mode 100644 index 0000000..4f1be22 --- /dev/null +++ b/modules/theme-builder/documents/header-footer-base.php @@ -0,0 +1,48 @@ +get_main_id(); + } + + protected static function get_editor_panel_categories() { + // Move to top as active. + $categories = [ + 'theme-elements' => [ + 'title' => esc_html__( 'Site', 'elementor-pro' ), + 'active' => true, + ], + ]; + + return $categories + parent::get_editor_panel_categories(); + } + + protected function register_controls() { + parent::register_controls(); + + Post::register_style_controls( $this ); + + $this->update_control( + 'section_page_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + ] + ); + } + + protected function get_remote_library_config() { + $config = parent::get_remote_library_config(); + + $config['category'] = $this->get_name(); + + return $config; + } +} diff --git a/modules/theme-builder/documents/header.php b/modules/theme-builder/documents/header.php new file mode 100644 index 0000000..9d6022b --- /dev/null +++ b/modules/theme-builder/documents/header.php @@ -0,0 +1,44 @@ + esc_html__( 'What is a Header Template?', 'elementor-pro' ), + 'content' => esc_html__( 'The header template allows you to easily design and edit custom WordPress headers so you are no longer constrained by your theme’s header design limitations.', 'elementor-pro' ), + 'tip' => esc_html__( 'You can create multiple headers, and assign each to different areas of your site.', 'elementor-pro' ), + 'docs' => 'https://go.elementor.com/app-theme-builder-header', + 'video_url' => 'https://www.youtube.com/embed/HHy5RK6W-6I', + ]; + } +} diff --git a/modules/theme-builder/documents/search-results.php b/modules/theme-builder/documents/search-results.php new file mode 100644 index 0000000..784d217 --- /dev/null +++ b/modules/theme-builder/documents/search-results.php @@ -0,0 +1,68 @@ + esc_html__( 'What is a Search Results Template?', 'elementor-pro' ), + 'content' => esc_html__( 'You can easily control the layout and design of the Search Results page with the Search Results template, which is simply a special archive template just for displaying search results.', 'elementor-pro' ), + 'tip' => esc_html__( 'You can customize the message if there are no results for the search term.', 'elementor-pro' ), + 'docs' => 'https://go.elementor.com/app-theme-builder-search-results', + 'video_url' => 'https://www.youtube.com/embed/KKkIU_L5sDo', + ]; + } + + public static function get_preview_as_default() { + return 'search'; + } + + public static function get_preview_as_options() { + $options = [ + 'search' => esc_html__( 'Search Results', 'elementor-pro' ), + ]; + + return [ + 'archive' => [ + 'label' => esc_html__( 'Archive', 'elementor-pro' ), + 'options' => $options, + ], + ]; + } + + protected function get_remote_library_config() { + $config = parent::get_remote_library_config(); + + $config['category'] = 'archive'; + + return $config; + } +} diff --git a/modules/theme-builder/documents/section.php b/modules/theme-builder/documents/section.php new file mode 100644 index 0000000..c7deb2c --- /dev/null +++ b/modules/theme-builder/documents/section.php @@ -0,0 +1,113 @@ +get_locations_manager()->register_locations(); + + $locations = Module::instance()->get_locations_manager()->get_locations( [ + 'public' => true, + ] ); + + if ( empty( $locations ) ) { + return; + } + + $this->start_controls_section( + 'location_settings', + [ + 'label' => esc_html__( 'Location Settings', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_SETTINGS, + ] + ); + + $options = [ + '' => esc_html__( 'Select', 'elementor-pro' ), + ]; + + foreach ( $locations as $location => $settings ) { + $options[ $location ] = $settings['label']; + } + + $this->add_control( + 'location', + [ + 'label' => esc_html__( 'Location', 'elementor-pro' ), + 'label_block' => true, + 'type' => Controls_Manager::SELECT, + 'default' => $this->get_location(), + 'save_default' => true, + 'options' => $options, + ] + ); + + $this->add_control( + 'apply_location', + [ + 'type' => Controls_Manager::BUTTON, + 'label' => '', + 'text' => esc_html__( 'Apply', 'elementor-pro' ), + 'event' => 'elementorThemeBuilder:ApplyPreview', + ] + ); + + $this->end_controls_section(); + } + + public function get_export_data() { + $data = parent::get_export_data(); + + $data['location'] = $this->get_location(); + + return $data; + } + + public function save_settings( $settings ) { + if ( isset( $settings['location'] ) ) { + if ( empty( $settings['location'] ) ) { + $this->delete_main_meta( '_elementor_location' ); + } else { + $this->update_main_meta( '_elementor_location', $settings['location'] ); + unset( $settings['location'] ); + } + Module::instance()->get_conditions_manager()->get_cache()->regenerate(); + } + + parent::save_settings( $settings ); + } +} diff --git a/modules/theme-builder/documents/single-base.php b/modules/theme-builder/documents/single-base.php new file mode 100644 index 0000000..a675838 --- /dev/null +++ b/modules/theme-builder/documents/single-base.php @@ -0,0 +1,182 @@ + true, + ]; + + return $config; + } + + protected static function get_editor_panel_categories() { + $categories = [ + 'theme-elements-single' => [ + 'title' => esc_html__( 'Single', 'elementor-pro' ), + ], + ]; + + return $categories + parent::get_editor_panel_categories(); + } + + public function before_get_content() { + parent::before_get_content(); + + // For `loop_start` hook. + if ( have_posts() ) { + the_post(); + } + } + + public function after_get_content() { + wp_reset_postdata(); + + parent::after_get_content(); + } + + public function get_container_attributes() { + $attributes = parent::get_container_attributes(); + + if ( is_singular() /* Not 404 */ ) { + $post_classes = get_post_class( '', get_the_ID() ); + $attributes['class'] .= ' ' . implode( ' ', $post_classes ); + } + + return $attributes; + } + + public function print_content() { + $requested_post_id = get_the_ID(); + if ( $requested_post_id !== $this->post->ID ) { + $requested_document = Module::instance()->get_document( $requested_post_id ); + + /** + * if current requested document is theme-document & it's not a content type ( like header/footer/sidebar ) + * show a placeholder instead of content. + */ + if ( $requested_document && ! $requested_document instanceof Section && $requested_document->get_location() !== $this->get_location() ) { + echo '
    ' . esc_html__( 'Content Area', 'elementor-pro' ) . '
    '; + + return; + } + } + + parent::print_content(); + } + + protected function register_controls() { + parent::register_controls(); + + $post_type = $this->get_main_meta( self::REMOTE_CATEGORY_META_KEY ); + + $latest_posts = get_posts( [ + 'posts_per_page' => 1, + 'post_type' => $post_type, + ] ); + + if ( ! empty( $latest_posts ) ) { + $this->update_control( + 'preview_type', + [ + 'default' => 'single/' . $post_type, + ] + ); + + $this->update_control( + 'preview_id', + [ + 'default' => $latest_posts[0]->ID, + ] + ); + } + } + + public static function get_preview_as_options() { + $post_types = Module::get_public_post_types(); + + $post_types['attachment'] = get_post_type_object( 'attachment' )->label; + $post_types_options = []; + + foreach ( $post_types as $post_type => $label ) { + $post_types_options[ 'single/' . $post_type ] = get_post_type_object( $post_type )->labels->singular_name; + } + + return [ + 'single' => [ + 'label' => esc_html__( 'Single', 'elementor-pro' ), + 'options' => $post_types_options, + ], + 'page/404' => esc_html__( '404', 'elementor-pro' ), + ]; + } + + public function get_depended_widget() { + return Plugin::elementor()->widgets_manager->get_widget_types( 'theme-post-content' ); + } + + public function get_elements_data( $status = DB::STATUS_PUBLISH ) { + $data = parent::get_elements_data(); + + if ( Plugin::elementor()->preview->is_preview_mode() && self::get_property( 'location' ) === Module::instance()->get_locations_manager()->get_current_location() ) { + $has_the_content = false; + + $depended_widget = $this->get_depended_widget(); + + Plugin::elementor()->db->iterate_data( $data, function( $element ) use ( &$has_the_content, $depended_widget ) { + if ( isset( $element['widgetType'] ) && $depended_widget->get_name() === $element['widgetType'] ) { + $has_the_content = true; + } + } ); + + if ( ! $has_the_content ) { + add_action( 'wp_footer', [ $this, 'preview_error_handler' ] ); + } + } + + return $data; + } + + public function preview_error_handler() { + $depended_widget_title = $this->get_depended_widget()->get_title(); + + wp_localize_script( 'elementor-frontend', 'elementorPreviewErrorArgs', [ + /* translators: %s: Widget name. */ + 'headerMessage' => sprintf( esc_html__( 'The %s Widget was not found in your template.', 'elementor-pro' ), $depended_widget_title ), + /* translators: 1: Widget name, 2: Template name. */ + 'message' => sprintf( esc_html__( 'You must include the %1$s Widget in your template (%2$s), in order for Elementor to work on this page.', 'elementor-pro' ), $depended_widget_title, '' . static::get_title() . '' ), + 'strings' => [ + 'confirm' => esc_html__( 'Edit Template', 'elementor-pro' ), + ], + 'confirmURL' => $this->get_edit_url(), + ] ); + } +} diff --git a/modules/theme-builder/documents/single-page.php b/modules/theme-builder/documents/single-page.php new file mode 100644 index 0000000..5b72654 --- /dev/null +++ b/modules/theme-builder/documents/single-page.php @@ -0,0 +1,46 @@ + esc_html__( 'What is a Single Page Template?', 'elementor-pro' ), + 'content' => esc_html__( 'A single page template allows you to easily create the layout and style of pages, ensuring design consistency across all the pages of your site.', 'elementor-pro' ), + 'tip' => esc_html__( 'You can create multiple single page templates, and assign each to different areas of your site.', 'elementor-pro' ), + 'docs' => 'https://go.elementor.com/app-theme-builder-page', + 'video_url' => 'https://www.youtube.com/embed/_y5eZ60lVoY', + ]; + } + + protected function get_remote_library_config() { + $config = parent::get_remote_library_config(); + + $config['category'] = 'single page'; + + return $config; + } +} diff --git a/modules/theme-builder/documents/single-post.php b/modules/theme-builder/documents/single-post.php new file mode 100644 index 0000000..7f239a5 --- /dev/null +++ b/modules/theme-builder/documents/single-post.php @@ -0,0 +1,43 @@ + esc_html__( 'What is a Single Post Template?', 'elementor-pro' ), + 'content' => esc_html__( 'A single post template allows you to easily design the layout and style of posts, ensuring a design consistency throughout all your blog posts, for example.', 'elementor-pro' ), + 'tip' => esc_html__( 'You can create multiple single post templates, and assign each to a different category.', 'elementor-pro' ), + 'docs' => 'https://go.elementor.com/app-theme-builder-post', + 'video_url' => 'https://www.youtube.com/embed/8Fk-Edu7DL0', + ]; + } + + protected function get_remote_library_config() { + $config = parent::get_remote_library_config(); + + $config['category'] = 'single post'; + + return $config; + } +} diff --git a/modules/theme-builder/documents/single.php b/modules/theme-builder/documents/single.php new file mode 100644 index 0000000..e4ae60a --- /dev/null +++ b/modules/theme-builder/documents/single.php @@ -0,0 +1,46 @@ +get_meta( self::REMOTE_CATEGORY_META_KEY ); + + if ( $category ) { + if ( 'not_found404' === $category ) { + $category = '404 page'; + } else { + $category = 'single ' . $category; + } + + $config['category'] = $category; + } else { + $config['category'] = 'single post'; + } + + return $config; + } + + protected static function get_site_editor_thumbnail_url() { + return ELEMENTOR_ASSETS_URL . 'images/app/site-editor/single-post.svg'; + } +} diff --git a/modules/theme-builder/documents/theme-document.php b/modules/theme-builder/documents/theme-document.php new file mode 100644 index 0000000..9c02bf8 --- /dev/null +++ b/modules/theme-builder/documents/theme-document.php @@ -0,0 +1,682 @@ + static::get_type(), + 'icon' => static::get_site_editor_icon(), + 'title' => static::get_title(), + 'page_title' => static::get_title(), + 'page_layout' => static::get_site_editor_layout(), + + // Todo: Remove. Core plugin should use `urls.route`. + 'url' => static::get_site_editor_route(), + + 'urls' => [ + 'route' => static::get_site_editor_route(), + 'create' => static::get_create_url(), + 'thumbnail' => static::get_site_editor_thumbnail_url(), + ], + 'tooltip_data' => static::get_site_editor_tooltip_data(), + 'show_instances' => true, + ]; + } + + public static function get_editor_panel_config() { + $panel_config = parent::get_editor_panel_config(); + $document_config = static::get_properties(); + + if ( true === $document_config['support_site_editor'] ) { + $panel_config['messages']['publish_notification'] = esc_html__( 'Congrats! Your Site Part is Live', 'elementor-pro' ); + } + + return $panel_config; + } + + protected function get_have_a_look_url() { + $document_config = static::get_properties(); + + if ( true === $document_config['support_site_editor'] ) { + return ''; + } + + return parent::get_have_a_look_url(); + } + + public static function get_create_url() { + $base_create_url = Plugin::elementor()->documents->get_create_new_post_url( Source_Local::CPT ); + + return add_query_arg( [ 'template_type' => static::get_type() ], $base_create_url ); + } + + protected static function get_site_editor_tooltip_data() { + return [ + 'title' => '', + 'content' => '', + 'tip' => '', + 'video_url' => '', + ]; + } + + public function get_name() { + return static::get_type(); + } + + public static function get_lock_behavior_v2() { + return new Feature_Lock( [ + 'type' => static::get_type(), + ] ); + } + + public function get_location_label() { + $location = $this->get_location(); + $locations_settings = Module::instance()->get_locations_manager()->get_location( $location ); + $label = ''; + $is_section_doc_type = 'section' === $this->get_name(); + + if ( $location ) { + if ( $is_section_doc_type ) { + $label .= isset( $locations_settings['label'] ) ? $locations_settings['label'] : $location; + } + } + + $supported = true; + + if ( $is_section_doc_type ) { + if ( $location && ! $locations_settings ) { + $supported = false; + } + } elseif ( ! $location || ! $locations_settings ) { + $supported = false; + } + + if ( ! $supported ) { + $label .= ' (' . esc_html__( 'Unsupported', 'elementor-pro' ) . ')'; + } + + return $label; + } + + public function before_get_content() { + $preview_manager = Module::instance()->get_preview_manager(); + $preview_manager->switch_to_preview_query(); + } + + public function after_get_content() { + $preview_manager = Module::instance()->get_preview_manager(); + $preview_manager->restore_current_query(); + } + + public function get_content( $with_css = false ) { + $this->before_get_content(); + + $content = parent::get_content( $with_css ); + + $this->after_get_content(); + + return $content; + } + + public function print_content() { + $plugin = Plugin::elementor(); + + if ( $plugin->preview->is_preview_mode( $this->get_main_id() ) ) { + // PHPCS - the method builder_wrapper is safe. + echo $plugin->preview->builder_wrapper( '' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } else { + // PHPCS - the method get_content is safe. + echo $this->get_content(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + } + + public static function get_preview_as_default() { + return ''; + } + + public static function get_preview_as_options() { + return []; + } + + public function get_container_attributes() { + $attributes = parent::get_container_attributes(); + + $location = Module::instance()->get_locations_manager()->get_current_location(); + + if ( $location ) { + $attributes['class'] .= ' elementor-location-' . $location; + } + + return $attributes; + } + + /** + * @static + * @since 2.0.0 + * @access public + * + * @return string + */ + public function get_edit_url() { + $url = parent::get_edit_url(); + + if ( isset( $_GET['action'] ) && 'elementor_new_post' === $_GET['action'] ) { + $url .= '#library'; + } + + return $url; + + } + + public function get_export_summary() { + $summary = parent::get_export_summary(); + + $summary['location'] = $this->get_location(); + + $theme_builder = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); + + $conditions = $theme_builder->get_conditions_manager()->get_document_conditions( $this ); + + foreach ( $conditions as $condition ) { + if ( 'include' === $condition['type'] && ! $condition['sub_id'] ) { + $summary['conditions'][] = $condition; + + break; + } + } + + return $summary; + } + + public function import( array $data ) { + parent::import( $data ); + + /** @var Module $theme_builder */ + $theme_builder = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); + + $conditions = isset( $data['import_settings']['conditions'] ) ? $data['import_settings']['conditions'] : []; + + if ( ! empty( $conditions ) ) { + $condition = $conditions[0]; + + $condition = rtrim( implode( '/', $condition ), '/' ); + + $conflicts = $theme_builder->get_conditions_manager()->get_conditions_conflicts_by_location( $condition, $this->get_location() ); + + if ( $conflicts ) { + /** @var Import_Export_Module $import_export_module */ + $import_export_module = Plugin::elementor()->app->get_component( 'import-export' ); + + $override_conditions = $import_export_module->import->get_settings( 'overrideConditions' ); + + if ( ! $override_conditions || ! in_array( $data['id'], $override_conditions, true ) ) { + return; + } + + foreach ( $conflicts as $template ) { + /** @var Theme_Document $template_document */ + $template_document = Plugin::elementor()->documents->get( $template['template_id'] ); + + $template_conditions = $theme_builder->get_conditions_manager()->get_document_conditions( $template_document ); + + foreach ( $template_conditions as $index => $template_condition ) { + if ( in_array( $template_condition, $conditions, true ) ) { + unset( $template_conditions[ $index ] ); + } + } + + $theme_builder->get_conditions_manager()->save_conditions( $template_document->get_main_id(), $template_conditions ); + } + } + } + + $theme_builder->get_conditions_manager()->save_conditions( $this->get_main_id(), $conditions ); + } + + protected function register_controls() { + parent::register_controls(); + + $this->start_controls_section( + 'preview_settings', + [ + 'label' => esc_html__( 'Preview Settings', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_SETTINGS, + ] + ); + + $this->add_control( + 'preview_type', + [ + 'label' => esc_html__( 'Preview Dynamic Content as', 'elementor-pro' ), + 'label_block' => true, + 'type' => Controls_Manager::SELECT, + 'default' => $this::get_preview_as_default(), + 'groups' => $this::get_preview_as_options(), + 'export' => false, + ] + ); + + $this->add_control( + 'preview_id', + [ + 'type' => QueryModule::QUERY_CONTROL_ID, + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_JS, + ], + 'export' => false, + 'condition' => [ + 'preview_type!' => [ + '', + 'search', + ], + ], + ] + ); + + $this->add_control( + 'preview_search_term', + [ + 'label' => esc_html__( 'Search Term', 'elementor-pro' ), + 'export' => false, + 'condition' => [ + 'preview_type' => 'search', + ], + ] + ); + + $this->add_control( + 'apply_preview', + [ + 'type' => Controls_Manager::BUTTON, + 'label' => esc_html__( 'Apply & Preview', 'elementor-pro' ), + 'label_block' => true, + 'show_label' => false, + 'text' => esc_html__( 'Apply & Preview', 'elementor-pro' ), + 'event' => 'elementorThemeBuilder:ApplyPreview', + ] + ); + + $this->end_controls_section(); + + $this->inject_html_tag_control(); + } + + /** + * @since 2.9.0 + * + * If the implementing document uses optional wrapper HTML tags, this method injects the control to choose the tag + */ + private function inject_html_tag_control() { + $wrapper_tags = $this->get_wrapper_tags(); + + // Only proceed if the implementing document has optional wrapper HTML tags to replace 'div' + if ( ! $wrapper_tags ) { + return; + } + + // Add 'div' to the beginning of the list of wrapper tags + array_unshift( $wrapper_tags, 'div' ); + + /** + * Inject the control that sets the HTML tag for the header/footer wrapper element + */ + $this->start_injection( [ + 'of' => 'post_status', + 'fallback' => [ + 'of' => 'post_title', + ], + ] ); + + $this->add_control( + 'content_wrapper_html_tag', + [ + 'label' => esc_html__( 'HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'div', + 'options' => array_combine( $wrapper_tags, $wrapper_tags ), + ] + ); + + $this->end_injection(); + } + + /** + * @param null $elements_data + * @since 2.9.0 + * @access public + * + * Overwrite method from document.php to check for user-selected tags to use as the document wrapper element + */ + public function print_elements_with_wrapper( $elements_data = null ) { + // Check if the implementing document has optional wrapper tags + $has_wrapper_tags = $this->get_wrapper_tags(); + $settings = $this->get_settings_for_display(); + $wrapper_tag = 'div'; + + // Only proceed if the inheriting document has optional wrapper HTML tags to replace 'div' + if ( $has_wrapper_tags ) { + $wrapper_tag = Utils::validate_html_tag( $settings['content_wrapper_html_tag'] ); + } + + if ( ! $elements_data ) { + $elements_data = $this->get_elements_data(); + } + + ?> + < get_container_attributes() ); ?>> + print_elements( $elements_data ); ?> + > + get_preview_manager(); + + $preview_manager->switch_to_preview_query(); + + $editor_data = parent::get_elements_raw_data( $data, $with_html_content ); + + $preview_manager->restore_current_query(); + + return $editor_data; + } + + public function render_element( $data ) { + $preview_manager = Module::instance()->get_preview_manager(); + + $preview_manager->switch_to_preview_query(); + + $render_html = parent::render_element( $data ); + + $preview_manager->restore_current_query(); + + return $render_html; + } + + public function get_wp_preview_url() { + // Ajax request from editor. + // PHPCS - the method is safe - just retrieving a value. + if ( ! empty( $_POST['initial_document_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing + return parent::get_wp_preview_url(); + } + + $preview_id = (int) $this->get_settings( 'preview_id' ); + $post_id = $this->get_main_id(); + + list( $preview_category, $preview_object_type ) = array_pad( explode( '/', $this->get_settings( 'preview_type' ) ), 2, '' ); + + $home_url = trailingslashit( home_url() ); + + switch ( $preview_category ) { + case 'archive': + switch ( $preview_object_type ) { + case 'author': + if ( empty( $preview_id ) ) { + $preview_id = get_current_user_id(); + } + $preview_url = get_author_posts_url( $preview_id ); + break; + case 'date': + $preview_url = add_query_arg( 'year', gmdate( 'Y' ), $home_url ); + break; + } + break; + case 'search': + $preview_url = add_query_arg( 's', $this->get_settings( 'preview_search_term' ), $home_url ); + break; + case 'taxonomy': + $term = get_term( $preview_id ); + + if ( $term && ! is_wp_error( $term ) ) { + $preview_url = get_term_link( $preview_id ); + } + + break; + case 'page': + switch ( $preview_object_type ) { + case 'home': + $preview_url = get_post_type_archive_link( 'post' ); + break; + case 'front': + $preview_url = $home_url; + break; + case '404': + $preview_url = add_query_arg( 'p', '-1', $home_url ); + break; + } + break; + case 'post_type_archive': + $post_type = $preview_object_type; + if ( post_type_exists( $post_type ) ) { + $preview_url = get_post_type_archive_link( $post_type ); + } + break; + case 'single': + $post = get_post( $preview_id ); + if ( $post ) { + $preview_url = get_permalink( $post ); + } + break; + } // End switch(). + + if ( empty( $preview_url ) ) { + $preview_url = $this->get_permalink(); + } + + $query_args = [ + 'preview' => true, + 'preview_nonce' => wp_create_nonce( 'post_preview_' . $post_id ), + 'theme_template_id' => $post_id, + ]; + + $preview_url = set_url_scheme( add_query_arg( $query_args, $preview_url ) ); + + /** + * Document "WordPress preview" URL. + * + * Filters the WordPress preview URL. + * + * @since 2.0.0 + * + * @param string $preview_url Document preview URL. + * @param Theme_Document $this An instance of the theme document. + */ + $preview_url = apply_filters( 'elementor/document/wp_preview_url', $preview_url, $this ); + + return $preview_url; + } + + public function get_preview_as_query_args() { + $preview_id = (int) $this->get_settings( 'preview_id' ); + + list( $preview_category, $preview_object_type ) = array_pad( explode( '/', $this->get_settings( 'preview_type' ) ), 2, '' ); + + switch ( $preview_category ) { + case 'archive': + switch ( $preview_object_type ) { + case 'author': + if ( empty( $preview_id ) ) { + $preview_id = get_current_user_id(); + } + + $query_args = [ + 'author' => $preview_id, + ]; + break; + case 'date': + $query_args = [ + 'year' => gmdate( 'Y' ), + ]; + break; + case 'recent_posts': + $query_args = [ + 'post_type' => 'post', + ]; + break; + } + break; + case 'search': + $query_args = [ + 's' => $this->get_settings( 'preview_search_term' ), + ]; + break; + case 'taxonomy': + case 'post_taxonomy': + case 'product_taxonomy': + $term = $this->get_taxonomy_term( $preview_id, $preview_object_type ); + + if ( $term && ! is_wp_error( $term ) ) { + $query_args = [ + 'tax_query' => [ + [ + 'taxonomy' => $term->taxonomy, + 'terms' => [ $term->term_id ], + 'field' => 'id', + ], + ], + ]; + } + break; + case 'page': + switch ( $preview_object_type ) { + case 'home': + $query_args = []; + break; + case 'front': + $query_args = [ + 'p' => get_option( 'page_on_front' ), + 'post_type' => 'page', + ]; + break; + case '404': + $query_args = [ + 'p' => -1, + ]; + break; + } + break; + case 'post_type_archive': + $post_type = $preview_object_type; + if ( post_type_exists( $post_type ) ) { + $query_args = [ + 'post_type' => $post_type, + ]; + } + break; + case 'single': + $query_args = [ + 'post_type' => $preview_object_type, + 'p' => $preview_id, + ]; + break; + } // End switch(). + + if ( empty( $query_args ) ) { + $query_args = [ + 'p' => $this->get_main_id(), + 'post_type' => $this->get_main_post()->post_type, + ]; + } + + return $query_args; + } + + public function after_preview_switch_to_query() { + global $wp_query; + if ( 'archive/recent_posts' === $this->get_settings( 'preview_type' ) ) { + $wp_query->is_archive = true; + } + } + + public function get_location() { + $value = self::get_property( 'location' ); + if ( ! $value ) { + $value = $this->get_main_meta( self::LOCATION_META_KEY ); + } + + return $value; + } + + public function get_initial_config() { + $config = parent::get_initial_config(); + + $config['support_site_editor'] = static::get_property( 'support_site_editor' ); + + return $config; + } + + /** + * @param $preview_id + * @param $preview_object_type + * @return \WP_Error|\WP_Term|null + */ + private function get_taxonomy_term( $preview_id, $preview_object_type ) { + if ( ! empty( $preview_id ) ) { + return get_term( $preview_id ); + } + + $terms = get_terms( [ + 'taxonomy' => $preview_object_type, + ] ); + + return reset( $terms ); + } +} diff --git a/modules/theme-builder/documents/theme-page-document.php b/modules/theme-builder/documents/theme-page-document.php new file mode 100644 index 0000000..3e11bb9 --- /dev/null +++ b/modules/theme-builder/documents/theme-page-document.php @@ -0,0 +1,131 @@ +get_main_id(); + } + + public static function get_properties() { + $properties = parent::get_properties(); + + $properties['support_wp_page_templates'] = true; + + return $properties; + } + + protected function register_controls() { + parent::register_controls(); + + $this->start_injection( [ + 'of' => 'post_status', + 'fallback' => [ + 'of' => 'post_title', + ], + ] ); + + $this->add_control( + 'page_template', + [ + 'label' => esc_html__( 'Page Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Default', 'elementor-pro' ), + PageTemplatesModule::TEMPLATE_CANVAS => esc_html__( 'Elementor Canvas', 'elementor-pro' ), + PageTemplatesModule::TEMPLATE_HEADER_FOOTER => esc_html__( 'Elementor Full Width', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'page_template_default_description', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'Default Page Template from your theme', 'elementor-pro' ), + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'page_template' => 'default', + ], + ] + ); + + $this->add_control( + 'page_template_canvas_description', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'No header, no footer, just Elementor', 'elementor-pro' ), + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'page_template' => PageTemplatesModule::TEMPLATE_CANVAS, + ], + ] + ); + + $this->add_control( + 'page_template_header_footer_description', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'This template includes the header, full-width content and footer', 'elementor-pro' ), + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'page_template' => PageTemplatesModule::TEMPLATE_HEADER_FOOTER, + ], + ] + ); + + $this->end_injection(); + + Post::register_style_controls( $this ); + } + + /** + * Add body classes. + * + * Add the body classes for the `style` controls selector. + * + * @param $body_classes + * + * @return array + */ + public function filter_body_classes( $body_classes ) { + // Indicator for edit/preview an `archive` document, so it's a `single` elementor_library post - but need a behavior like an archive. + $is_archive_template = 'archive' === Source_Local::get_template_type( get_the_ID() ); + + $add_body_class = false; + + if ( $this instanceof Archive && ( is_archive() || is_search() || is_home() || $is_archive_template ) ) { + $add_body_class = true; + } elseif ( $this instanceof Single_Base && ( is_singular() || is_404() ) && ! $is_archive_template ) { + $add_body_class = true; + } + + if ( $add_body_class ) { + $body_classes[] = 'elementor-page-' . $this->get_main_id(); + } + + return $body_classes; + } + + public function __construct( array $data = [] ) { + if ( $data ) { + add_filter( 'body_class', [ $this, 'filter_body_classes' ] ); + } + + parent::__construct( $data ); + } +} diff --git a/modules/theme-builder/documents/theme-section-document.php b/modules/theme-builder/documents/theme-section-document.php new file mode 100644 index 0000000..901482c --- /dev/null +++ b/modules/theme-builder/documents/theme-section-document.php @@ -0,0 +1,43 @@ + esc_html__( 'Select...', 'elementor-pro' ), + ], + Archive::get_preview_as_options(), + Single::get_preview_as_options() + ); + } + + protected function get_remote_library_config() { + $config = parent::get_remote_library_config(); + + $config['category'] = ''; + + return $config; + } +} diff --git a/modules/theme-builder/module.php b/modules/theme-builder/module.php new file mode 100644 index 0000000..fcfef4c --- /dev/null +++ b/modules/theme-builder/module.php @@ -0,0 +1,504 @@ +preview->is_preview_mode() || is_preview(); + } + + public static function get_public_post_types( $args = [] ) { + $post_types = Utils::get_public_post_types( $args ); + + // Product form WooCommerce are handled separately. + if ( class_exists( 'woocommerce' ) ) { + unset( $post_types['product'] ); + } + + return $post_types; + } + + public function get_name() { + return 'theme-builder'; + } + + public function get_widgets() { + $widgets = [ + 'Site_Logo', + 'Site_Title', + 'Page_Title', + 'Post_Title', + 'Post_Excerpt', + 'Post_Content', + 'Post_Featured_Image', + 'Archive_Title', + ]; + + if ( class_exists( '\ElementorPro\Modules\Posts\Widgets\Posts' ) ) { + $widgets[] = 'Archive_Posts'; + } + + return $widgets; + } + + /** + * @return Classes\Conditions_Manager + */ + public function get_conditions_manager() { + return $this->get_component( 'conditions' ); + } + + /** + * @return Classes\Locations_Manager + */ + public function get_locations_manager() { + return $this->get_component( 'locations' ); + } + + /** + * @return Classes\Preview_Manager + */ + public function get_preview_manager() { + return $this->get_component( 'preview' ); + } + + /** + * @return Classes\Templates_Types_Manager + */ + public function get_types_manager() { + return $this->get_component( 'templates_types' ); + } + + /** + * @param $post_id + * + * @return Theme_Document + */ + public function get_document( $post_id ) { + $document = null; + + try { + $document = Plugin::elementor()->documents->get( $post_id ); + } catch ( \Exception $e ) { + // Do nothing. + unset( $e ); + } + + if ( ! empty( $document ) && ! $document instanceof Theme_Document ) { + $document = null; + } + + return $document; + } + + /** + * @deprecated 3.1.0 + */ + public function localize_settings() { + Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); + + return []; + } + + public function document_config( $config, $post_id ) { + $document = $this->get_document( $post_id ); + + if ( ! $document ) { + return $config; + } + + $types_manager = $this->get_types_manager(); + $conditions_manager = $this->get_conditions_manager(); + $template_type = $this->get_template_type( $post_id ); + + $config = array_replace_recursive( $config, [ + 'theme_builder' => [ + 'types' => $types_manager->get_types_config(), + 'conditions' => $conditions_manager->get_conditions_config(), + 'template_conditions' => ( new Classes\Template_Conditions() )->get_config(), + 'is_theme_template' => $this->is_theme_template( $post_id ), + 'settings' => [ + 'template_type' => $template_type, + 'location' => $document->get_location(), + 'conditions' => $conditions_manager->get_document_conditions( $document ), + ], + ], + ] ); + + return $config; + } + + public function register_controls( Controls_Manager $controls_manager ) { + $controls_manager->register( new Classes\Conditions_Repeater() ); + $controls_manager->register( new Classes\Control_Media_Preview() ); + } + + public function create_new_dialog_types( $types ) { + /** + * @var Theme_Document[] $document_types + */ + foreach ( $types as $type => $label ) { + $document_type = Plugin::elementor()->documents->get_document_type( $type ); + $instance = new $document_type(); + + if ( $instance instanceof Theme_Document && 'section' !== $type ) { + $types[ $type ] .= $instance->get_location_label(); + } + + if ( Single::class === $document_type ) { + unset( $types[ $type ] ); + } + } + + return $types; + } + + public function print_location_field() { + $locations = $this->get_locations_manager()->get_locations( [ + 'public' => true, + ] ); + + if ( empty( $locations ) ) { + return; + } + ?> +
    + +
    + +
    +
    + false, + ] ); + + if ( empty( $post_types ) ) { + return; + } + ?> +
    + +
    + +
    +
    + id, [ 'elementor_library', 'edit-elementor_library' ] ) ) { + // For column type (Supported/Unsupported) & for `print_location_field`. + $this->get_locations_manager()->register_locations(); + } + } + + /** + * An hack to hide the app menu on before render without remove the app page from system. + * + * @param $menu + * + * @return mixed + */ + public function hide_admin_app_submenu( $menu ) { + remove_submenu_page( Source_Local::ADMIN_MENU_SLUG, App::PAGE_ID ); + + return $menu; + } + + public function admin_columns_content( $column_name, $post_id ) { + if ( 'elementor_library_type' === $column_name ) { + /** @var Document $document */ + $document = Plugin::elementor()->documents->get( $post_id ); + + if ( $document instanceof Theme_Document ) { + $location_label = $document->get_location_label(); + + if ( $location_label ) { + echo ' - ' . esc_html( $location_label ); + } + } + } + } + + public function get_template_type( $post_id ) { + return Source_local::get_template_type( $post_id ); + } + + public function is_theme_template( $post_id ) { + $document = Plugin::elementor()->documents->get( $post_id ); + + return $document instanceof Theme_Document; + } + + public function on_elementor_editor_init() { + Plugin::elementor()->common->add_template( __DIR__ . '/views/panel-template.php' ); + } + + public function add_finder_items( array $categories ) { + $categories['create']['items']['theme-template'] = [ + 'title' => esc_html__( 'Add New Theme Template', 'elementor-pro' ), + 'icon' => 'plus-circle-o', + 'url' => $this->get_admin_templates_url() . '#add_new', + 'keywords' => [ 'template', 'theme', 'new', 'create' ], + ]; + + return $categories; + } + + /** + * Add New item to admin menu. + * + * @since 3.6.0 + * @access private + */ + private function register_admin_menu( MainMenu $menu ) { + $menu->add_submenu( [ + 'menu_title' => esc_html__( 'Theme Builder', 'elementor-pro' ), + 'menu_slug' => Plugin::elementor()->app->get_settings( 'menu_url' ), + 'index' => 30, + ] ); + } + + /** + * Add New item to admin menu. + * + * Fired by `admin_menu` action. + * + * @since 3.6.0 + * @access private + */ + private function register_admin_menu_legacy( Admin_Menu_Manager $admin_menu ) { + $admin_menu->register( $this->get_admin_templates_url( true ), new Theme_Builder_Menu_Item() ); + } + + public function print_new_theme_builder_promotion( $views ) { + /** @var Source_Local $source */ + $source = Plugin::elementor()->templates_manager->get_source( 'local' ); + + $current_tab_group = $source->get_current_tab_group(); + + if ( self::ADMIN_LIBRARY_TAB_GROUP === $current_tab_group ) { + /** + * @var Admin_Notices $admin_notices + */ + $admin_notices = Plugin::elementor()->admin->get_component( 'admin-notices' ); + + $admin_notices->print_admin_notice( [ + 'title' => esc_html__( 'Meet the New Theme Builder: More Intuitive and Visual Than Ever', 'elementor-pro' ), + 'description' => esc_html__( 'With the new Theme Builder you can visually manage every part of your site intuitively, making the task of designing a complete website that much easier', 'elementor-pro' ), + 'button' => [ + 'text' => esc_html__( 'Try it Now', 'elementor-pro' ), + 'class' => 'elementor-button e-accent', + 'url' => Plugin::elementor()->app->get_settings( 'menu_url' ), + ], + ] ); + } + + return $views; + } + + private function get_admin_templates_url( $relative = false ) { + $base_url = Source_Local::ADMIN_MENU_SLUG; + + if ( ! $relative ) { + $base_url = admin_url( $base_url ); + } + + return add_query_arg( 'tabs_group', self::ADMIN_LIBRARY_TAB_GROUP, $base_url ); + } + + /** + * Get the conflicts between the active templates' conditions and new templates. + * + * @since 3.8.0 + * + * @param array $templates + * @return array + */ + public function get_conditions_conflicts( array $templates ) : array { + $conflicts = []; + + foreach ( $templates as $template_id => $template ) { + if ( empty( $template['conditions'] ) ) { + continue; + } + + foreach ( $template['conditions'] as $condition ) { + $condition = rtrim( implode( '/', $condition ), '/' ); + $condition_conflicts = $this->get_conditions_manager()->get_conditions_conflicts_by_location( $condition, $template['location'] ); + + if ( $condition_conflicts ) { + $conflicts[ $template_id ] = $condition_conflicts; + } + } + } + + return $conflicts; + } + + /** + * TODO: BC - remove in 3.11.0|4.1.0 + * Add conflicts to import result. + * + * @since 3.7.0 + * + * @param array $result + * @return array + */ + private function add_conflicts_to_import_result( array $result ) { + $manifest_data = $result['manifest']; + + if ( empty( $manifest_data['templates'] ) ) { + return $result; + } + + $result['conflicts'] = $this->get_conditions_conflicts( $manifest_data['templates'] ); + + return $result; + } + + /** + * Add attributes to the document wrapper element. + * + * @param array $attributes - The document's wrapper element attributes. + * @param Document $document + * + * @return array + */ + public function add_document_attributes( array $attributes, Document $document ): array { + $attributes['data-elementor-post-type'] = $document->get_post()->post_type; + + return $attributes; + } + + public function __construct() { + parent::__construct(); + + require __DIR__ . '/api.php'; + + $this->add_component( 'theme_support', new Classes\Theme_Support() ); + $this->add_component( 'conditions', new Classes\Conditions_Manager() ); + $this->add_component( 'templates_types', new Classes\Templates_Types_Manager() ); + $this->add_component( 'preview', new Classes\Preview_Manager() ); + $this->add_component( 'locations', new Classes\Locations_Manager() ); + + add_action( 'elementor/controls/register', [ $this, 'register_controls' ] ); + + // Editor + add_action( 'elementor/editor/init', [ $this, 'on_elementor_editor_init' ] ); + add_filter( 'elementor/document/config', [ $this, 'document_config' ], 10, 2 ); + add_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_document_attributes' ], 10, 2 ); + + // Admin + add_action( 'admin_head', [ $this, 'admin_head' ] ); + add_filter( 'add_menu_classes', [ $this, 'hide_admin_app_submenu' ], 9 /* Before core submenu fixes */ ); + add_action( 'manage_' . Source_Local::CPT . '_posts_custom_column', [ $this, 'admin_columns_content' ], 10, 2 ); + add_action( 'elementor/template-library/create_new_dialog_fields', [ $this, 'print_location_field' ] ); + add_action( 'elementor/template-library/create_new_dialog_fields', [ $this, 'print_post_type_field' ] ); + + if ( Plugin::elementor()->experiments->is_feature_active( 'admin_menu_rearrangement' ) ) { + add_action( 'elementor/admin/menu_registered/elementor', function( MainMenu $menu ) { + $this->register_admin_menu( $menu ); + } ); + } else { + add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu ) { + $this->register_admin_menu_legacy( $admin_menu ); + }, static::ADMIN_MENU_PRIORITY /* After "Popups" */ ); + + // TODO: BC - Remove after `Admin_Menu_Manager` will be the standard. + add_action( 'admin_menu', function () { + if ( did_action( 'elementor/admin/menu/register' ) ) { + return; + } + + add_submenu_page( + Source_Local::ADMIN_MENU_SLUG, + '', + esc_html__( 'Theme Builder', 'elementor-pro' ), + 'publish_posts', + $this->get_admin_templates_url( true ) + ); + }, 22 /* After core promotion menu */ ); + } + + add_filter( 'elementor/template-library/create_new_dialog_types', [ $this, 'create_new_dialog_types' ] ); + add_filter( 'views_edit-' . Source_Local::CPT, [ $this, 'print_new_theme_builder_promotion' ], 9 ); + + // Moved into the IE module \ElementorPro\Core\App\Modules\ImportExport\Module::add_actions + // TODO: remove in 3.10.0 + add_filter( 'elementor/import/stage_1/result', function ( array $result ) { + return $this->add_conflicts_to_import_result( $result ); + }); + + // Common + add_filter( 'elementor/finder/categories', [ $this, 'add_finder_items' ] ); + } +} diff --git a/modules/theme-builder/skins/post-comments-skin-classic.php b/modules/theme-builder/skins/post-comments-skin-classic.php new file mode 100644 index 0000000..856f467 --- /dev/null +++ b/modules/theme-builder/skins/post-comments-skin-classic.php @@ -0,0 +1,492 @@ +start_controls_section( + 'section_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'default' => '[field id="comment-count"]', + 'label_block' => true, + 'dynamic' => [ + 'types' => [ + 'text', + ], + 'apply_on' => 'value', + 'allow_free_text' => true, + 'allow_multiple' => true, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'show_gravatar', + [ + 'label' => esc_html__( 'Gravatar', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style', + [ + 'label' => esc_html__( 'Comments', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'row_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 10, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-comment' => 'margin-bottom: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'row_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .elementor-comment' => 'background-color: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'row_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-comment' => 'border-color: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'row_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'placeholder' => '1', + 'selectors' => [ + '{{WRAPPER}} .elementor-comment' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'row_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-comment' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_meta_style', + [ + 'label' => esc_html__( 'Meta', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'meta_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 0, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-comment .comment-meta' => 'padding-bottom: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'meta_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-comment .comment-meta' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'meta_typography', + 'selector' => '{{WRAPPER}} .elementor-comment .comment-meta', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_content_style', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'content_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-comment .comment-content' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'content__typography', + 'selector' => '{{WRAPPER}} .elementor-comment .comment-content', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_reply_button_style', + [ + 'label' => esc_html__( 'Reply Button', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->start_controls_tabs( 'tabs_reply_button_style' ); + + $this->start_controls_tab( + 'tab_reply_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'reply_button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-comment .comment-reply-link' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'reply_button_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}} .elementor-comment .comment-reply-link', + ] + ); + + $this->add_control( + 'reply_button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_ACCENT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-comment .comment-reply-link' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), [ + 'name' => 'reply_button_border', + 'label' => esc_html__( 'Border', 'elementor-pro' ), + 'placeholder' => '1px', + 'default' => '1px', + 'selector' => '{{WRAPPER}} .elementor-comment .comment-reply-link', + ] + ); + + $this->add_control( + 'reply_button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-comment .comment-reply-link' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'reply_button_text_padding', + [ + 'label' => esc_html__( 'Text Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-comment .comment-reply-link' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_reply_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_hover_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-comment .comment-reply-link:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'reply_button_background_hover_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-comment .comment-reply-link:hover' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'reply_button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-comment .comment-reply-link:hover' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + $this->get_control_id( 'reply_button_border_border!' ) => '', + ], + ] + ); + + $this->add_control( + 'reply_button_hover_animation', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + } + + public function render() { + + Module::instance()->get_preview_manager()->switch_to_preview_query(); + + // Hack to remove template comment form + $comments_template_callback = function() { + return __DIR__ . '/../views/comments-template.php'; + }; + + add_filter( 'comments_template', $comments_template_callback ); + + // The `comments_template` doesn't has an API to pass current widget instance, so make it global + $GLOBALS['post_comment_skin_classic'] = $this; + + comments_template(); + + remove_filter( 'comments_template', $comments_template_callback ); + + unset( $GLOBALS['post_comment_skin_classic'] ); + + Module::instance()->get_preview_manager()->restore_current_query(); + } + + public function comment_callback( $comment, $args, $depth ) { + $tag = ( 'div' === $args['style'] ) ? 'div' : 'li'; + $class = 'elementor-comment'; + if ( ! empty( $args['has_children'] ) ) { + $class .= ' parent'; + } + ?> + < id="comment-" > +
    +
    +
    + + %s', get_comment_author_link( $comment ) ), + '', + '' + ); + ?> +
    + + + + comment_approved ) : ?> +

    + +
    + +
    + +
    + + 'div-comment', + 'depth' => $depth, + 'max_depth' => $args['max_depth'], + 'before' => '
    ', + 'after' => '
    ', + ] ) ); + ?> +
    + > + parent->query_posts(); + + $wp_query = $this->parent->get_query(); + + if ( ! $wp_query->found_posts ) { + $this->render_loop_header(); + + $should_escape = true; + + /** + * Should escape 'nothing found' message. + * + * Filters the ability for escaping HTML tags on archive pages that return no + * result. + * + * By default Elementor removes HTML tags from the 'nothing found' message added + * by the user, for security reasons. This hook allows developers to change this + * behaviour. By setting this to `false`, Elementor will increase flexibility + * and allow the user to add HTML tags to the message. + * + * @param bool $should_escape Whether to escape 'nothing found' message. + */ + $should_escape = apply_filters( 'elementor_pro/theme_builder/archive/escape_nothing_found_message', $should_escape ); + + $message = $this->parent->get_settings_for_display( 'nothing_found_message' ); + if ( $should_escape ) { + $message = esc_html( $message ); + } + + ?> +
    + +
    + render_loop_footer(); + + return; + } + + parent::render(); + } +} diff --git a/modules/theme-builder/skins/posts-archive-skin-cards.php b/modules/theme-builder/skins/posts-archive-skin-cards.php new file mode 100644 index 0000000..d466f63 --- /dev/null +++ b/modules/theme-builder/skins/posts-archive-skin-cards.php @@ -0,0 +1,34 @@ +register_core_location( 'header' ); + $manager->register_core_location( 'footer' ); + } + + public function metabox_capability( $capability ) { + if ( Source_Local::CPT === get_post_type() ) { + $capability = 'do_not_allow'; + } + + return $capability; + } + + public function do_header() { + $did_location = Module::instance()->get_locations_manager()->do_location( 'header' ); + if ( $did_location ) { + remove_action( 'generate_header', 'generate_construct_header' ); + remove_action( 'generate_after_header', 'generate_add_navigation_after_header', 5 ); + } + } + + public function do_footer() { + $did_location = Module::instance()->get_locations_manager()->do_location( 'footer' ); + if ( $did_location ) { + remove_action( 'generate_footer', 'generate_construct_footer' ); + remove_action( 'generate_footer', 'generate_construct_footer_widgets', 5 ); + } + } + + public function body_classes( $classes ) { + if ( in_array( 'elementor-template-full-width', $classes ) ) { + $classes[] = 'full-width-content'; + } + + return $classes; + } + + public function __construct() { + add_action( 'elementor/theme/register_locations', [ $this, 'register_locations' ] ); + add_filter( 'generate_metabox_capability', [ $this, 'metabox_capability' ] ); + + add_action( 'generate_header', [ $this, 'do_header' ], 0 ); + add_action( 'generate_footer', [ $this, 'do_footer' ], 0 ); + + add_filter( 'body_class', [ $this, 'body_classes' ], 11 ); + } +} diff --git a/modules/theme-builder/theme-support/safe-mode-theme-support.php b/modules/theme-builder/theme-support/safe-mode-theme-support.php new file mode 100644 index 0000000..1b3ffae --- /dev/null +++ b/modules/theme-builder/theme-support/safe-mode-theme-support.php @@ -0,0 +1,34 @@ +register_core_location( 'header' ); + $manager->register_core_location( 'footer' ); + } + + public function do_header() { + elementor_theme_do_location( 'header' ); + } + + public function do_footer() { + elementor_theme_do_location( 'footer' ); + } + + public function __construct() { + add_action( 'elementor/theme/register_locations', [ $this, 'register_locations' ] ); + + add_action( 'elementor/page_templates/canvas/before_content', [ $this, 'do_header' ], 0 ); + add_action( 'elementor/page_templates/canvas/after_content', [ $this, 'do_footer' ], 0 ); + } +} diff --git a/modules/theme-builder/views/comments-template.php b/modules/theme-builder/views/comments-template.php new file mode 100644 index 0000000..1e75c6f --- /dev/null +++ b/modules/theme-builder/views/comments-template.php @@ -0,0 +1,53 @@ + +

    + + + +

    + get_instance_value( 'title' ) ); ?> +

    + + + +
      + [ $skin, 'comment_callback' ], + ] ); + ?> +
    + + + + + +

    get_instance_value( 'title' ) ); ?>

    + + + +

    + + + + + + + + + + diff --git a/modules/theme-builder/views/theme-support-footer.php b/modules/theme-builder/views/theme-support-footer.php new file mode 100644 index 0000000..39607e8 --- /dev/null +++ b/modules/theme-builder/views/theme-support-footer.php @@ -0,0 +1,14 @@ +get_locations_manager(); +$location_manager->do_location( 'footer' ); ?> + + + + + diff --git a/modules/theme-builder/views/theme-support-header.php b/modules/theme-builder/views/theme-support-header.php new file mode 100644 index 0000000..0e6708f --- /dev/null +++ b/modules/theme-builder/views/theme-support-header.php @@ -0,0 +1,33 @@ +get_locations_manager(); +?> + +> + + + + + + <?php + // PHPCS - already escaped by WordPress. + echo wp_get_document_title(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> + + + + +> +do_location( 'header' ); +?> diff --git a/modules/theme-builder/widgets/archive-posts.php b/modules/theme-builder/widgets/archive-posts.php new file mode 100644 index 0000000..14aac8f --- /dev/null +++ b/modules/theme-builder/widgets/archive-posts.php @@ -0,0 +1,151 @@ +add_skin( new Skins\Posts_Archive_Skin_Classic( $this ) ); + $this->add_skin( new Skins\Posts_Archive_Skin_Cards( $this ) ); + $this->add_skin( new Skins\Posts_Archive_Skin_Full_Content( $this ) ); + } + + protected function register_controls() { + parent::register_controls(); + + $this->register_pagination_section_controls(); + + $this->register_advanced_section_controls(); + + $this->update_control( + 'pagination_type', + [ + 'default' => 'numbers', + ] + ); + } + + public function register_advanced_section_controls() { + $this->start_controls_section( + 'section_advanced', + [ + 'label' => esc_html__( 'Advanced', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'nothing_found_message', + [ + 'label' => esc_html__( 'Nothing Found Message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => esc_html__( 'It seems we can\'t find what you\'re looking for.', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_nothing_found_style', + [ + 'tab' => Controls_Manager::TAB_STYLE, + 'label' => esc_html__( 'Nothing Found Message', 'elementor-pro' ), + 'condition' => [ + 'nothing_found_message!' => '', + ], + ] + ); + + $this->add_control( + 'nothing_found_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-posts-nothing-found' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'nothing_found_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-posts-nothing-found', + ] + ); + + $this->end_controls_section(); + } + + public function query_posts() { + global $wp_query; + + $query_vars = $wp_query->query_vars; + + /** + * Posts archive query vars. + * + * Filters the post query variables when the theme loads the posts archive page. + * + * @since 2.0.0 + * + * @param array $query_vars The query variables for the `WP_Query`. + */ + $query_vars = apply_filters( 'elementor/theme/posts_archive/query_posts/query_vars', $query_vars ); + + if ( $query_vars !== $wp_query->query_vars ) { + $this->query = new \WP_Query( $query_vars ); // SQL_CALC_FOUND_ROWS is used. + } else { + $this->query = $wp_query; + } + + Query_Control::add_to_avoid_list( wp_list_pluck( $this->query->posts, 'ID' ) ); + } +} diff --git a/modules/theme-builder/widgets/archive-title.php b/modules/theme-builder/widgets/archive-title.php new file mode 100644 index 0000000..4038ae3 --- /dev/null +++ b/modules/theme-builder/widgets/archive-title.php @@ -0,0 +1,43 @@ + 'heading', + 'is_core_dependency' => true, + ], + ]; + } +} diff --git a/modules/theme-builder/widgets/page-title.php b/modules/theme-builder/widgets/page-title.php new file mode 100644 index 0000000..5e04cf6 --- /dev/null +++ b/modules/theme-builder/widgets/page-title.php @@ -0,0 +1,43 @@ + 'heading', + 'is_core_dependency' => true, + ], + ]; + } + + public function get_title() { + return esc_html__( 'Page Title', 'elementor-pro' ); + } + + public function get_icon() { + return 'eicon-archive-title'; + } + + public function get_categories() { + return [ 'theme-elements' ]; + } + + public function get_keywords() { + return [ 'title', 'heading', 'page' ]; + } +} diff --git a/modules/theme-builder/widgets/post-content.php b/modules/theme-builder/widgets/post-content.php new file mode 100644 index 0000000..5475d52 --- /dev/null +++ b/modules/theme-builder/widgets/post-content.php @@ -0,0 +1,115 @@ +start_controls_section( + 'section_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => 'text-align: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}}' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + // Post CSS should not be printed here because it overrides the already existing post CSS. + $this->render_post_content( false, false ); + } + + public function render_plain_content() {} +} diff --git a/modules/theme-builder/widgets/post-excerpt.php b/modules/theme-builder/widgets/post-excerpt.php new file mode 100644 index 0000000..4972fc9 --- /dev/null +++ b/modules/theme-builder/widgets/post-excerpt.php @@ -0,0 +1,128 @@ +start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'excerpt', + [ + 'type' => Controls_Manager::TEXT, + 'label_block' => true, + 'dynamic' => [ + 'active' => true, + 'default' => Plugin::elementor()->dynamic_tags->tag_data_to_tag_text( null, 'post-excerpt' ), + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-widget-container' => 'text-align: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'title_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-widget-container' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-widget-container', + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + $this->print_unescaped_setting( 'excerpt' ); + } +} diff --git a/modules/theme-builder/widgets/post-featured-image.php b/modules/theme-builder/widgets/post-featured-image.php new file mode 100644 index 0000000..eb187d5 --- /dev/null +++ b/modules/theme-builder/widgets/post-featured-image.php @@ -0,0 +1,65 @@ + 'image', + 'is_core_dependency' => true, + ], + ]; + } + + protected function register_controls() { + parent::register_controls(); + + $this->update_control( + 'image', + [ + 'dynamic' => [ + 'default' => Plugin::elementor()->dynamic_tags->tag_data_to_tag_text( null, 'post-featured-image' ), + ], + ], + [ + 'recursive' => true, + ] + ); + } + + protected function get_html_wrapper_class() { + return parent::get_html_wrapper_class() . ' elementor-widget-' . parent::get_name(); + } +} diff --git a/modules/theme-builder/widgets/post-title.php b/modules/theme-builder/widgets/post-title.php new file mode 100644 index 0000000..4d04311 --- /dev/null +++ b/modules/theme-builder/widgets/post-title.php @@ -0,0 +1,51 @@ + [ + 'default' => 'entry-title', + ], + ]; + } + + public function get_inline_css_depends() { + return [ + [ + 'name' => 'heading', + 'is_core_dependency' => true, + ], + ]; + } +} diff --git a/modules/theme-builder/widgets/site-logo.php b/modules/theme-builder/widgets/site-logo.php new file mode 100644 index 0000000..7ad71aa --- /dev/null +++ b/modules/theme-builder/widgets/site-logo.php @@ -0,0 +1,221 @@ + 'image', + 'is_core_dependency' => true, + ], + ]; + } + + protected function register_controls() { + parent::register_controls(); + + $this->update_control( + 'section_image', + [ + 'label' => esc_html__( 'Site Logo', 'elementor-pro' ), + ] + ); + + $this->update_control( + 'image', + [ + 'label' => esc_html__( 'Site Logo', 'elementor-pro' ), + 'type' => Control_Media_Preview::CONTROL_TYPE, + 'src' => $this->get_site_logo(), + 'dynamic' => [ + 'default' => Plugin::elementor()->dynamic_tags->tag_data_to_tag_text( null, 'site-logo' ), + ], + ], + [ + 'recursive' => true, + ] + ); + + $this->update_control( + 'image_size', + [ + 'separator' => 'before', + 'default' => 'full', + ] + ); + + $this->update_control( + 'link_to', + [ + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'site_url' => esc_html__( 'Site URL', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom URL', 'elementor-pro' ), + 'file' => esc_html__( 'Media File', 'elementor-pro' ), + ], + 'default' => 'site_url', + ], + [ + 'recursive' => true, + ] + ); + + $this->update_control( + 'caption_source', + [ + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'attachment' => esc_html__( 'Attachment Caption', 'elementor-pro' ), + ], + ] + ); + + $this->remove_control( 'caption' ); + + $this->add_control( + 'change_logo_cta', + [ + 'type' => Controls_Manager::BUTTON, + 'label_block' => true, + 'show_label' => false, + 'button_type' => 'default elementor-button-center', + 'text' => esc_html__( 'Change Site Logo', 'elementor-pro' ), + 'event' => 'elementorProSiteLogo:change', + ], + [ + 'position' => [ + 'of' => 'image', + 'type' => 'control', + 'at' => 'after', + ], + ] + ); + } + + /** + * TODO: Remove this method when Elementor Core 3.11.0 is required. + * Duplicate of render() method from Elementor\Widget_Image class, so it will use the get_link_url() method. + * + * @return void + */ + protected function render() { + $settings = $this->get_settings_for_display(); + + if ( empty( $settings['image']['url'] ) ) { + return; + } + + $has_caption = $this->has_caption( $settings ); + + $link = $this->get_link_url( $settings ); + + if ( $link ) { + $this->add_link_attributes( 'link', $link ); + + if ( Plugin::elementor()->editor->is_edit_mode() ) { + $this->add_render_attribute( 'link', 'class', 'elementor-clickable' ); + } + + if ( 'file' === $settings['link_to'] ) { + $this->add_lightbox_data_attributes( 'link', $settings['image']['id'], $settings['open_lightbox'] ); + } + } ?> + +
    + + + print_render_attribute_string( 'link' ); ?>> + + + + + + +
    get_caption( $settings ) ); + ?>
    + + +
    + + Plugin::elementor()->dynamic_tags->get_tag_data_content( null, 'site-url' ) ?? '' ]; + + default: + return [ 'url' => $settings['image']['url'] ]; + } + } + + // TODO: Remove this method when removing the render() method. + private function has_caption( $settings ) { + return ( ! empty( $settings['caption_source'] ) && 'none' !== $settings['caption_source'] ); + } + + // TODO: Remove this method when removing the render() method. + private function get_caption( $settings ) { + $caption = ''; + + if ( ! empty( $settings['caption_source'] ) && 'attachment' === $settings['caption_source'] ) { + $caption = wp_get_attachment_caption( $settings['image']['id'] ); + } + + return $caption; + } + + // Get the site logo from the dynamic tag + private function get_site_logo(): string { + $site_logo = Plugin::elementor()->dynamic_tags->get_tag_data_content( null, 'site-logo' ); + return $site_logo['url'] ?? Utils::get_placeholder_image_src(); + } +} diff --git a/modules/theme-builder/widgets/site-title.php b/modules/theme-builder/widgets/site-title.php new file mode 100644 index 0000000..c8c0043 --- /dev/null +++ b/modules/theme-builder/widgets/site-title.php @@ -0,0 +1,99 @@ + 'heading', + 'is_core_dependency' => true, + ], + ]; + } + + protected function register_controls() { + parent::register_controls(); + $this->update_control( + 'title', + [ + 'dynamic' => [ + 'default' => Plugin::elementor()->dynamic_tags->tag_data_to_tag_text( null, 'site-title' ), + ], + ], + [ + 'recursive' => true, + ] + ); + + $this->update_control( + 'link', + [ + 'dynamic' => [ + 'default' => Plugin::elementor()->dynamic_tags->tag_data_to_tag_text( null, 'site-url' ), + ], + ], + [ + 'recursive' => true, + ] + ); + + $this->add_control( + 'site_identity_notice', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'To edit the title of your site, go to %1$sSite Identity%2$s.', 'elementor-pro' ), + '', + '' + ), + ], + [ + 'position' => [ + 'of' => 'title', + 'type' => 'control', + 'at' => 'before', + ], + ] + ); + } + + protected function get_html_wrapper_class() { + return parent::get_html_wrapper_class() . ' elementor-widget-' . parent::get_name(); + } +} diff --git a/modules/theme-builder/widgets/title-widget-base.php b/modules/theme-builder/widgets/title-widget-base.php new file mode 100644 index 0000000..e98432c --- /dev/null +++ b/modules/theme-builder/widgets/title-widget-base.php @@ -0,0 +1,62 @@ +documents->get( get_the_ID() ); + if ( $current_doc && 'yes' === $current_doc->get_settings( 'hide_title' ) ) { + return false; + } + + return true; + } + + protected function register_controls() { + parent::register_controls(); + + $dynamic_tag_name = $this->get_dynamic_tag_name(); + + $this->update_control( + 'title', + [ + 'dynamic' => [ + 'default' => ProPlugin::elementor()->dynamic_tags->tag_data_to_tag_text( null, $dynamic_tag_name ), + ], + ], + [ + 'recursive' => true, + ] + ); + + $this->update_control( + 'header_size', + [ + 'default' => 'h1', + ] + ); + } + + protected function get_html_wrapper_class() { + return parent::get_html_wrapper_class() . ' elementor-page-title elementor-widget-' . parent::get_name(); + } + + public function render() { + if ( $this->should_show_page_title() ) { + return parent::render(); + } + } +} diff --git a/modules/theme-elements/module.php b/modules/theme-elements/module.php new file mode 100644 index 0000000..10b4e50 --- /dev/null +++ b/modules/theme-elements/module.php @@ -0,0 +1,39 @@ +is_yoast_seo_active() ) { + $widgets[] = 'Breadcrumbs'; + } + + return $widgets; + } + + public function is_yoast_seo_active() { + return function_exists( 'yoast_breadcrumb' ); + } +} diff --git a/modules/theme-elements/widgets/author-box.php b/modules/theme-elements/widgets/author-box.php new file mode 100644 index 0000000..8b05827 --- /dev/null +++ b/modules/theme-elements/widgets/author-box.php @@ -0,0 +1,1590 @@ +start_controls_section( + 'section_author_info', + [ + 'label' => esc_html__( 'Author Info', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'source', + [ + 'label' => esc_html__( 'Source', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'current', + 'options' => [ + 'current' => esc_html__( 'Current Author', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'show_avatar', + [ + 'label' => esc_html__( 'Profile Picture', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'prefix_class' => 'elementor-author-box--avatar-', + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + 'separator' => 'before', + 'condition' => [ + 'source' => 'current', + ], + 'render_type' => 'template', + ] + ); + + // Used by the WordPress `get_avatar_url()` function to set the image size. + $this->add_control( + 'avatar_size', + [ + 'label' => esc_html__( 'Picture Size', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 300, + 'condition' => [ + 'source' => 'current', + 'show_avatar' => 'yes', + ], + ] + ); + + $this->add_control( + 'author_avatar', + [ + 'label' => esc_html__( 'Profile Picture', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'default' => [ + 'url' => Utils::get_placeholder_image_src(), + ], + 'condition' => [ + 'source' => 'custom', + ], + 'separator' => 'before', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'show_name', + [ + 'label' => esc_html__( 'Display Name', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'prefix_class' => 'elementor-author-box--name-', + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + 'condition' => [ + 'source' => 'current', + ], + 'render_type' => 'template', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'author_name', + [ + 'label' => esc_html__( 'Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'John Doe', 'elementor-pro' ), + 'condition' => [ + 'source' => 'custom', + ], + 'separator' => 'before', + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'author_name_tag', + [ + 'label' => esc_html__( 'HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + ], + 'default' => 'h4', + ] + ); + + $this->add_control( + 'link_to', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'website' => esc_html__( 'Website', 'elementor-pro' ), + 'posts_archive' => esc_html__( 'Posts Archive', 'elementor-pro' ), + ], + 'condition' => [ + 'source' => 'current', + ], + 'description' => esc_html__( 'Link for the Author Name and Image', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'show_biography', + [ + 'label' => esc_html__( 'Biography', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'prefix_class' => 'elementor-author-box--biography-', + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + 'condition' => [ + 'source' => 'current', + ], + 'render_type' => 'template', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'show_link', + [ + 'label' => esc_html__( 'Archive Button', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'prefix_class' => 'elementor-author-box--link-', + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'no', + 'condition' => [ + 'source' => 'current', + ], + 'render_type' => 'template', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'author_website', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'condition' => [ + 'source' => 'custom', + ], + 'description' => esc_html__( 'Link for the Author Name and Image', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'author_bio', + [ + 'label' => esc_html__( 'Biography', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), + 'rows' => 3, + 'condition' => [ + 'source' => 'custom', + ], + 'separator' => 'before', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'posts_url', + [ + 'label' => esc_html__( 'Archive Button', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'source' => 'custom', + ], + ] + ); + + $this->add_control( + 'link_text', + [ + 'label' => esc_html__( 'Archive Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'All Posts', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'above' => [ + 'title' => esc_html__( 'Above', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'separator' => 'before', + 'prefix_class' => 'elementor-author-box--layout-image-', + ] + ); + + $this->add_control( + 'alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'prefix_class' => 'elementor-author-box--align-', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_image_style', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_avatar', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_avatar[url]', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_control( + 'image_vertical_align', + [ + 'label' => esc_html__( 'Vertical Align', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'middle' => [ + 'title' => esc_html__( 'Middle', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + ], + 'prefix_class' => 'elementor-author-box--image-valign-', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_avatar', + 'operator' => '===', + 'value' => 'yes', + ], + [ + 'name' => 'layout', + 'operator' => '!==', + 'value' => 'above', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_avatar[url]', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'layout', + 'operator' => '!==', + 'value' => 'above', + ], + ], + ], + ], + ], + ] + ); + + $this->add_responsive_control( + 'image_size', + [ + 'label' => esc_html__( 'Image Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__avatar img' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}}', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_avatar', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_avatar[url]', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_responsive_control( + 'image_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + 'body.rtl {{WRAPPER}}.elementor-author-box--layout-image-left .elementor-author-box__avatar, + body:not(.rtl) {{WRAPPER}}:not(.elementor-author-box--layout-image-above) .elementor-author-box__avatar' => 'margin-right: {{SIZE}}{{UNIT}}; margin-left: 0;', + + 'body:not(.rtl) {{WRAPPER}}.elementor-author-box--layout-image-right .elementor-author-box__avatar, + body.rtl {{WRAPPER}}:not(.elementor-author-box--layout-image-above) .elementor-author-box__avatar' => 'margin-left: {{SIZE}}{{UNIT}}; margin-right:0;', + + '{{WRAPPER}}.elementor-author-box--layout-image-above .elementor-author-box__avatar' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_avatar', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_avatar[url]', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_control( + 'image_border', + [ + 'label' => esc_html__( 'Border', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__avatar img' => 'border-style: solid', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_avatar', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_avatar[url]', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_control( + 'image_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#000', + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__avatar img' => 'border-color: {{VALUE}}', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_avatar', + 'operator' => '===', + 'value' => 'yes', + ], + [ + 'name' => 'image_border', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_avatar[url]', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'image_border', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + ], + ], + ] + ); + + $this->add_responsive_control( + 'image_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__avatar img' => 'border-width: {{SIZE}}{{UNIT}}', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_avatar', + 'operator' => '===', + 'value' => 'yes', + ], + [ + 'name' => 'image_border', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_avatar[url]', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'image_border', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + ], + ], + ] + ); + + $this->add_control( + 'image_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__avatar img' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_avatar', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_avatar[url]', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'input_box_shadow', + 'selector' => '{{WRAPPER}} .elementor-author-box__avatar img', + 'fields_options' => [ + 'box_shadow_type' => [ + 'separator' => 'default', + ], + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_avatar', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_avatar[url]', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_text_style', + [ + 'label' => esc_html__( 'Author', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'heading_name_style', + [ + 'label' => esc_html__( 'Name', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_name', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_name', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_control( + 'name_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__name' => 'color: {{VALUE}}', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_name', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_name', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'name_typography', + 'selector' => '{{WRAPPER}} .elementor-author-box__name', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_name', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_name', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_responsive_control( + 'name_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__name' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_name', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_name', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_control( + 'heading_bio_style', + [ + 'label' => esc_html__( 'Biography', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_biography', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_bio', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_control( + 'bio_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__bio' => 'color: {{VALUE}}', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_biography', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_bio', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'bio_typography', + 'selector' => '{{WRAPPER}} .elementor-author-box__bio', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_biography', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_bio', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->add_responsive_control( + 'bio_gap', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__bio' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'current', + ], + [ + 'name' => 'show_biography', + 'operator' => '===', + 'value' => 'yes', + ], + ], + ], + [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'source', + 'operator' => '===', + 'value' => 'custom', + ], + [ + 'name' => 'author_bio', + 'operator' => '!==', + 'value' => '', + ], + ], + ], + ], + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_button', + [ + 'label' => 'Button', + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_button_style' ); + + $this->start_controls_tab( + 'tab_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__button' => 'color: {{VALUE}}; border-color: {{VALUE}}', + ], + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__button' => 'background-color: {{VALUE}}', + ], + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'button_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}} .elementor-author-box__button', + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__button:hover' => 'border-color: {{VALUE}}; color: {{VALUE}};', + ], + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_background_hover_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__button:hover' => 'background-color: {{VALUE}};', + ], + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__button' => 'transition-duration: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_hover_animation', + [ + 'label' => esc_html__( 'Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'button_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__button' => 'border-width: {{SIZE}}{{UNIT}};', + ], + 'separator' => 'before', + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__button' => 'border-radius: {{SIZE}}{{UNIT}};', + ], + 'separator' => 'after', + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->add_control( + 'button_text_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-author-box__button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'before', + 'condition' => [ + 'link_text!' => '', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + $author = []; + $link_tag = 'div'; + $link_url = ''; + $link_target = ''; + $author_name_tag = Utils::validate_html_tag( $settings['author_name_tag'] ); + + $custom_src = ( 'custom' === $settings['source'] ); + + if ( 'current' === $settings['source'] ) { + + $avatar_args['size'] = $settings['avatar_size']; + + $user_id = get_the_author_meta( 'ID' ); + $author['avatar'] = get_avatar_url( $user_id, $avatar_args ); + $author['display_name'] = get_the_author_meta( 'display_name' ); + $author['website'] = get_the_author_meta( 'user_url' ); + $author['bio'] = get_the_author_meta( 'description' ); + $author['posts_url'] = get_author_posts_url( $user_id ); + + } elseif ( $custom_src ) { + + if ( ! empty( $settings['author_avatar']['url'] ) ) { + $avatar_src = $settings['author_avatar']['url']; + + if ( $settings['author_avatar']['id'] ) { + $attachment_image_src = wp_get_attachment_image_src( $settings['author_avatar']['id'], 'medium' ); + + if ( ! empty( $attachment_image_src[0] ) ) { + $avatar_src = $attachment_image_src[0]; + } + } + + $author['avatar'] = $avatar_src; + } + + $author['display_name'] = $settings['author_name']; + $author['website'] = $settings['author_website']['url']; + $author['bio'] = wpautop( $settings['author_bio'] ); + $author['posts_url'] = $settings['posts_url']['url']; + } + + $print_avatar = ( ( ! $custom_src && 'yes' === $settings['show_avatar'] ) || ( $custom_src && ! empty( $author['avatar'] ) ) ); + $print_name = ( ( ! $custom_src && 'yes' === $settings['show_name'] ) || ( $custom_src && ! empty( $author['display_name'] ) ) ); + $print_bio = ( ( ! $custom_src && 'yes' === $settings['show_biography'] ) || ( $custom_src && ! empty( $author['bio'] ) ) ); + $print_link = ( ( ! $custom_src && 'yes' === $settings['show_link'] ) && ! empty( $settings['link_text'] ) || ( $custom_src && ! empty( $author['posts_url'] ) && ! empty( $settings['link_text'] ) ) ); + + if ( ! empty( $settings['link_to'] ) || $custom_src ) { + if ( ( $custom_src || 'website' === $settings['link_to'] ) && ! empty( $author['website'] ) ) { + $link_tag = 'a'; + $link_url = $author['website']; + + if ( $custom_src ) { + $link_target = $settings['author_website']['is_external'] ? '_blank' : ''; + } else { + $link_target = '_blank'; + } + } elseif ( 'posts_archive' === $settings['link_to'] && ! empty( $author['posts_url'] ) ) { + $link_tag = 'a'; + $link_url = $author['posts_url']; + } + + if ( ! empty( $link_url ) ) { + $this->add_render_attribute( 'author_link', 'href', esc_url( $link_url ) ); + + if ( ! empty( $link_target ) ) { + $this->add_render_attribute( 'author_link', 'target', $link_target ); + } + } + } + + $this->add_render_attribute( + 'button', + 'class', [ + 'elementor-author-box__button', + 'elementor-button', + 'elementor-size-xs', + ] + ); + + if ( $print_link ) { + $this->add_render_attribute( 'button', 'href', esc_url( $author['posts_url'] ) ); + } + + if ( $print_link && ! empty( $settings['button_hover_animation'] ) ) { + $this->add_render_attribute( + 'button', + 'class', + 'elementor-animation-' . $settings['button_hover_animation'] + ); + } + + if ( $print_avatar ) { + $this->add_render_attribute( + 'avatar', + [ + 'src' => esc_url( $author['avatar'] ), + 'alt' => ( ! empty( $author['display_name'] ) ) + ? sprintf( + /* translators: %s: Author display name. */ + esc_attr__( 'Picture of %s', 'elementor-pro' ), + $author['display_name'] + ) + : esc_html__( 'Author picture', 'elementor-pro' ), + 'loading' => 'lazy', + ] + ); + } + + ?> +
    + + < print_render_attribute_string( 'author_link' ); ?> class="elementor-author-box__avatar"> + print_render_attribute_string( 'avatar' ); ?>> + > + + +
    + + < print_render_attribute_string( 'author_link' ); ?>> + < class="elementor-author-box__name"> + + > + > + + + +
    + +
    + + + + print_render_attribute_string( 'button' ); ?>> + print_unescaped_setting( 'link_text' ); ?> + + +
    +
    + start_controls_section( + 'section_breadcrumbs_content', + [ + 'label' => esc_html__( 'Breadcrumbs', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'prefix_class' => 'elementor%s-align-', + ] + ); + + $this->add_control( + 'html_tag', + [ + 'label' => esc_html__( 'HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Default', 'elementor-pro' ), + 'p' => 'p', + 'div' => 'div', + 'nav' => 'nav', + 'span' => 'span', + ], + 'default' => '', + ] + ); + + $this->add_control( + 'html_description', + [ + 'raw' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'Additional settings are available in the Yoast SEO %1$sBreadcrumbs Panel%2$s', 'elementor-pro' ), + sprintf( '', admin_url( 'admin.php?page=wpseo_titles#top#breadcrumbs' ) ), + '' + ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style', + [ + 'label' => esc_html__( 'Breadcrumbs', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'typography', + 'selector' => '{{WRAPPER}}', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}}' => 'color: {{VALUE}};', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_breadcrumbs_style' ); + + $this->start_controls_tab( + 'tab_color_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'link_color', + [ + 'label' => esc_html__( 'Link Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} a' => 'color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_color_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'link_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} a:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + } + + private function get_html_tag() { + $html_tag = $this->get_settings( 'html_tag' ); + + if ( empty( $html_tag ) ) { + $html_tag = 'p'; + } + + return Utils::validate_html_tag( $html_tag ); + } + + protected function render() { + if ( class_exists( '\WPSEO_Breadcrumbs' ) ) { + $html_tag = $this->get_html_tag(); + WPSEO_Breadcrumbs::breadcrumb( '<' . $html_tag . ' id="breadcrumbs">', '' ); + } + + } + + public function get_group_name() { + return 'theme-elements'; + } +} diff --git a/modules/theme-elements/widgets/post-comments.php b/modules/theme-elements/widgets/post-comments.php new file mode 100644 index 0000000..df101f8 --- /dev/null +++ b/modules/theme-elements/widgets/post-comments.php @@ -0,0 +1,125 @@ +start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'Comments', 'elementor-pro' ), + ] + ); + + $this->add_control( + '_skin', + [ + 'type' => Controls_Manager::HIDDEN, + ] + ); + + $this->add_control( + 'skin_temp', + [ + 'label' => esc_html__( 'Skin', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Theme Comments', 'elementor-pro' ), + ], + 'description' => esc_html__( 'The Theme Comments skin uses the currently active theme comments design and layout to display the comment form and comments.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'source_type', + [ + 'label' => esc_html__( 'Source', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + Module::SOURCE_TYPE_CURRENT_POST => esc_html__( 'Current Post', 'elementor-pro' ), + Module::SOURCE_TYPE_CUSTOM => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => Module::SOURCE_TYPE_CURRENT_POST, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'source_custom', + [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_POST, + ], + 'condition' => [ + 'source_type' => Module::SOURCE_TYPE_CUSTOM, + ], + ] + ); + + $this->end_controls_section(); + } + + public function render() { + $settings = $this->get_settings(); + + if ( Module::SOURCE_TYPE_CUSTOM === $settings['source_type'] ) { + $post_id = (int) $settings['source_custom']; + Plugin::elementor()->db->switch_to_post( $post_id ); + } + + if ( ! comments_open() && ( Plugin::elementor()->preview->is_preview_mode() || Plugin::elementor()->editor->is_edit_mode() ) ) : + ?> + + db->restore_current_post(); + } + } + + public function get_group_name() { + return 'theme-elements'; + } +} diff --git a/modules/theme-elements/widgets/post-info.php b/modules/theme-elements/widgets/post-info.php new file mode 100644 index 0000000..40e35c2 --- /dev/null +++ b/modules/theme-elements/widgets/post-info.php @@ -0,0 +1,1108 @@ + 'icon-list', + 'is_core_dependency' => true, + ], + ]; + } + + protected function register_controls() { + $this->start_controls_section( + 'section_icon', + [ + 'label' => esc_html__( 'Meta Data', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'view', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'inline', + 'options' => [ + 'traditional' => [ + 'title' => esc_html__( 'Default', 'elementor-pro' ), + 'icon' => 'eicon-editor-list-ul', + ], + 'inline' => [ + 'title' => esc_html__( 'Inline', 'elementor-pro' ), + 'icon' => 'eicon-ellipsis-h', + ], + ], + 'render_type' => 'template', + 'classes' => 'elementor-control-start-end', + ] + ); + + $repeater = new Repeater(); + + $repeater->add_control( + 'type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'date', + 'options' => [ + 'author' => esc_html__( 'Author', 'elementor-pro' ), + 'date' => esc_html__( 'Date', 'elementor-pro' ), + 'time' => esc_html__( 'Time', 'elementor-pro' ), + 'comments' => esc_html__( 'Comments', 'elementor-pro' ), + 'terms' => esc_html__( 'Terms', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + ] + ); + + $repeater->add_control( + 'date_format', + [ + 'label' => esc_html__( 'Date Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'default', + 'options' => [ + 'default' => 'Default', + '0' => _x( 'March 6, 2018 (F j, Y)', 'Date Format', 'elementor-pro' ), + '1' => '2018-03-06 (Y-m-d)', + '2' => '03/06/2018 (m/d/Y)', + '3' => '06/03/2018 (d/m/Y)', + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'condition' => [ + 'type' => 'date', + ], + ] + ); + + $repeater->add_control( + 'custom_date_format', + [ + 'label' => esc_html__( 'Custom Date Format', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => 'F j, Y', + 'condition' => [ + 'type' => 'date', + 'date_format' => 'custom', + ], + 'description' => sprintf( + /* translators: %s: Allowed data letters (see: http://php.net/manual/en/function.date.php). */ + __( 'Use the letters: %s', 'elementor-pro' ), + 'l D d j S F m M n Y y' + ), + 'ai' => [ + 'active' => false, + ], + ] + ); + + $repeater->add_control( + 'time_format', + [ + 'label' => esc_html__( 'Time Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'default', + 'options' => [ + 'default' => 'Default', + '0' => '3:31 pm (g:i a)', + '1' => '3:31 PM (g:i A)', + '2' => '15:31 (H:i)', + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'condition' => [ + 'type' => 'time', + ], + ] + ); + $repeater->add_control( + 'custom_time_format', + [ + 'label' => esc_html__( 'Custom Time Format', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => 'g:i a', + 'placeholder' => 'g:i a', + 'condition' => [ + 'type' => 'time', + 'time_format' => 'custom', + ], + 'description' => sprintf( + /* translators: %s: Allowed time letters (see: http://php.net/manual/en/function.time.php). */ + __( 'Use the letters: %s', 'elementor-pro' ), + 'g G H i a A' + ), + 'ai' => [ + 'active' => false, + ], + ] + ); + + $repeater->add_control( + 'taxonomy', + [ + 'label' => esc_html__( 'Taxonomy', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'label_block' => true, + 'default' => [], + 'options' => $this->get_taxonomies(), + 'condition' => [ + 'type' => 'terms', + ], + ] + ); + + $repeater->add_control( + 'text_prefix', + [ + 'label' => esc_html__( 'Before', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'condition' => [ + 'type!' => 'custom', + ], + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $repeater->add_control( + 'show_avatar', + [ + 'label' => esc_html__( 'Avatar', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'type' => 'author', + ], + ] + ); + + $repeater->add_responsive_control( + 'avatar_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} {{CURRENT_ITEM}} .elementor-icon-list-icon' => 'width: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'show_avatar' => 'yes', + ], + ] + ); + + $repeater->add_control( + 'comments_custom_strings', + [ + 'label' => esc_html__( 'Custom Format', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => false, + 'condition' => [ + 'type' => 'comments', + ], + ] + ); + + $repeater->add_control( + 'string_no_comments', + [ + 'label' => esc_html__( 'No Comments', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'No Comments', 'elementor-pro' ), + 'condition' => [ + 'comments_custom_strings' => 'yes', + 'type' => 'comments', + ], + ] + ); + + $repeater->add_control( + 'string_one_comment', + [ + 'label' => esc_html__( 'One Comment', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'One Comment', 'elementor-pro' ), + 'condition' => [ + 'comments_custom_strings' => 'yes', + 'type' => 'comments', + ], + ] + ); + + $repeater->add_control( + 'string_comments', + [ + 'label' => esc_html__( 'Comments', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( '%s Comments', 'elementor-pro' ), + 'condition' => [ + 'comments_custom_strings' => 'yes', + 'type' => 'comments', + ], + ] + ); + + $repeater->add_control( + 'custom_text', + [ + 'label' => esc_html__( 'Custom', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'label_block' => true, + 'condition' => [ + 'type' => 'custom', + ], + ] + ); + + $repeater->add_control( + 'link', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'condition' => [ + 'type!' => 'time', + ], + ] + ); + + $repeater->add_control( + 'custom_url', + [ + 'label' => esc_html__( 'Custom URL', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'type' => 'custom', + ], + ] + ); + + $repeater->add_control( + 'show_icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'default' => esc_html__( 'Default', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'default', + 'condition' => [ + 'show_avatar!' => 'yes', + ], + ] + ); + + $repeater->add_control( + 'selected_icon', + [ + 'label' => esc_html__( 'Choose Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'condition' => [ + 'show_icon' => 'custom', + 'show_avatar!' => 'yes', + ], + ] + ); + + $this->add_control( + 'icon_list', + [ + 'label' => '', + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'default' => [ + [ + 'type' => 'author', + 'selected_icon' => [ + 'value' => 'far fa-user-circle', + 'library' => 'fa-regular', + ], + ], + [ + 'type' => 'date', + 'selected_icon' => [ + 'value' => 'fas fa-calendar', + 'library' => 'fa-solid', + ], + ], + [ + 'type' => 'time', + 'selected_icon' => [ + 'value' => 'far fa-clock', + 'library' => 'fa-regular', + ], + ], + [ + 'type' => 'comments', + 'selected_icon' => [ + 'value' => 'far fa-comment-dots', + 'library' => 'fa-regular', + ], + ], + ], + 'title_field' => '{{{ elementor.helpers.renderIcon( this, selected_icon, {}, "i", "panel" ) || \'\' }}} {{{ type }}}', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_icon_list', + [ + 'label' => esc_html__( 'List', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'space_between', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-list-items:not(.elementor-inline-items) .elementor-icon-list-item:not(:last-child)' => 'padding-bottom: calc({{SIZE}}{{UNIT}}/2)', + '{{WRAPPER}} .elementor-icon-list-items:not(.elementor-inline-items) .elementor-icon-list-item:not(:first-child)' => 'margin-top: calc({{SIZE}}{{UNIT}}/2)', + '{{WRAPPER}} .elementor-icon-list-items.elementor-inline-items .elementor-icon-list-item' => 'margin-right: calc({{SIZE}}{{UNIT}}/2); margin-left: calc({{SIZE}}{{UNIT}}/2)', + '{{WRAPPER}} .elementor-icon-list-items.elementor-inline-items' => 'margin-right: calc(-{{SIZE}}{{UNIT}}/2); margin-left: calc(-{{SIZE}}{{UNIT}}/2)', + 'body.rtl {{WRAPPER}} .elementor-icon-list-items.elementor-inline-items .elementor-icon-list-item:after' => 'left: calc(-{{SIZE}}{{UNIT}}/2)', + 'body:not(.rtl) {{WRAPPER}} .elementor-icon-list-items.elementor-inline-items .elementor-icon-list-item:after' => 'right: calc(-{{SIZE}}{{UNIT}}/2)', + ], + ] + ); + + $this->add_responsive_control( + 'icon_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-h-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'prefix_class' => 'elementor%s-align-', + ] + ); + + $this->add_control( + 'divider', + [ + 'label' => esc_html__( 'Divider', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-list-item:not(:last-child):after' => 'content: ""', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'divider_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'solid' => esc_html__( 'Solid', 'elementor-pro' ), + 'double' => esc_html__( 'Double', 'elementor-pro' ), + 'dotted' => esc_html__( 'Dotted', 'elementor-pro' ), + 'dashed' => esc_html__( 'Dashed', 'elementor-pro' ), + ], + 'default' => 'solid', + 'condition' => [ + 'divider' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-list-items:not(.elementor-inline-items) .elementor-icon-list-item:not(:last-child):after' => 'border-top-style: {{VALUE}};', + '{{WRAPPER}} .elementor-icon-list-items.elementor-inline-items .elementor-icon-list-item:not(:last-child):after' => 'border-left-style: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'divider_weight', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 1, + ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'condition' => [ + 'divider' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-list-items:not(.elementor-inline-items) .elementor-icon-list-item:not(:last-child):after' => 'border-top-width: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .elementor-inline-items .elementor-icon-list-item:not(:last-child):after' => 'border-left-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'divider_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + '%' => [ + 'min' => 1, + 'max' => 100, + ], + ], + 'condition' => [ + 'divider' => 'yes', + 'view!' => 'inline', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-list-item:not(:last-child):after' => 'width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'divider_height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + '%' => [ + 'min' => 1, + 'max' => 100, + ], + ], + 'condition' => [ + 'divider' => 'yes', + 'view' => 'inline', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-list-item:not(:last-child):after' => 'height: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ddd', + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'condition' => [ + 'divider' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-list-item:not(:last-child):after' => 'border-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_icon_style', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'icon_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-list-icon i' => 'color: {{VALUE}};', + '{{WRAPPER}} .elementor-icon-list-icon svg' => 'fill: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + ] + ); + + $this->add_responsive_control( + 'icon_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 14, + ], + 'range' => [ + 'px' => [ + 'min' => 6, + ], + 'em' => [ + 'max' => 0.6, + ], + 'rem' => [ + 'max' => 0.6, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-list-icon' => 'width: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .elementor-icon-list-icon i' => 'font-size: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .elementor-icon-list-icon svg' => '--e-icon-list-icon-size: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_text_style', + [ + 'label' => esc_html__( 'Text', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'text_indent', + [ + 'label' => esc_html__( 'Indent', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .elementor-icon-list-text' => 'padding-left: {{SIZE}}{{UNIT}}', + 'body.rtl {{WRAPPER}} .elementor-icon-list-text' => 'padding-right: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-icon-list-text, {{WRAPPER}} .elementor-icon-list-text a' => 'color: {{VALUE}}', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'icon_typography', + 'selector' => '{{WRAPPER}} .elementor-icon-list-item', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->end_controls_section(); + } + + protected function get_taxonomies() { + $taxonomies = get_taxonomies( [ + 'show_in_nav_menus' => true, + ], 'objects' ); + + $options = [ + '' => esc_html__( 'Choose', 'elementor-pro' ), + ]; + + foreach ( $taxonomies as $taxonomy ) { + $options[ $taxonomy->name ] = $taxonomy->label; + } + + return $options; + } + + protected function get_meta_data( $repeater_item ) { + $item_data = []; + + switch ( $repeater_item['type'] ) { + case 'author': + $item_data['text'] = get_the_author_meta( 'display_name' ); + $item_data['icon'] = 'fa fa-user-circle-o'; // Default icon. + $item_data['selected_icon'] = [ + 'value' => 'far fa-user-circle', + 'library' => 'fa-regular', + ]; // Default icons. + $item_data['itemprop'] = 'author'; + + if ( 'yes' === $repeater_item['link'] ) { + $item_data['url'] = [ + 'url' => get_author_posts_url( get_the_author_meta( 'ID' ) ), + ]; + } + + if ( 'yes' === $repeater_item['show_avatar'] ) { + $item_data['image'] = get_avatar_url( get_the_author_meta( 'ID' ), 96 ); + } + + break; + + case 'date': + $custom_date_format = empty( $repeater_item['custom_date_format'] ) ? 'F j, Y' : $repeater_item['custom_date_format']; + + $format_options = [ + 'default' => 'F j, Y', + '0' => 'F j, Y', + '1' => 'Y-m-d', + '2' => 'm/d/Y', + '3' => 'd/m/Y', + 'custom' => $custom_date_format, + ]; + + $item_data['text'] = get_the_time( $format_options[ $repeater_item['date_format'] ] ); + $item_data['icon'] = 'fa fa-calendar'; // Default icon + $item_data['selected_icon'] = [ + 'value' => 'fas fa-calendar', + 'library' => 'fa-solid', + ]; // Default icons. + $item_data['itemprop'] = 'datePublished'; + + if ( 'yes' === $repeater_item['link'] ) { + $item_data['url'] = [ + 'url' => get_day_link( get_post_time( 'Y' ), get_post_time( 'm' ), get_post_time( 'j' ) ), + ]; + } + break; + + case 'time': + $custom_time_format = empty( $repeater_item['custom_time_format'] ) ? 'g:i a' : $repeater_item['custom_time_format']; + + $format_options = [ + 'default' => 'g:i a', + '0' => 'g:i a', + '1' => 'g:i A', + '2' => 'H:i', + 'custom' => $custom_time_format, + ]; + $item_data['text'] = get_the_time( $format_options[ $repeater_item['time_format'] ] ); + $item_data['icon'] = 'fa fa-clock-o'; // Default icon + $item_data['selected_icon'] = [ + 'value' => 'far fa-clock', + 'library' => 'fa-regular', + ]; // Default icons. + break; + + case 'comments': + if ( comments_open() ) { + $default_strings = [ + 'string_no_comments' => esc_html__( 'No Comments', 'elementor-pro' ), + 'string_one_comment' => esc_html__( 'One Comment', 'elementor-pro' ), + 'string_comments' => esc_html__( '%s Comments', 'elementor-pro' ), + ]; + + if ( 'yes' === $repeater_item['comments_custom_strings'] ) { + if ( ! empty( $repeater_item['string_no_comments'] ) ) { + $default_strings['string_no_comments'] = $repeater_item['string_no_comments']; + } + + if ( ! empty( $repeater_item['string_one_comment'] ) ) { + $default_strings['string_one_comment'] = $repeater_item['string_one_comment']; + } + + if ( ! empty( $repeater_item['string_comments'] ) ) { + $default_strings['string_comments'] = $repeater_item['string_comments']; + } + } + + $num_comments = (int) get_comments_number(); // get_comments_number returns only a numeric value + + if ( 0 === $num_comments ) { + $item_data['text'] = $default_strings['string_no_comments']; + } else { + $item_data['text'] = sprintf( _n( $default_strings['string_one_comment'], $default_strings['string_comments'], $num_comments, 'elementor-pro' ), $num_comments ); + } + + if ( 'yes' === $repeater_item['link'] ) { + $item_data['url'] = [ + 'url' => get_comments_link(), + ]; + } + $item_data['icon'] = 'fa fa-commenting-o'; // Default icon + $item_data['selected_icon'] = [ + 'value' => 'far fa-comment-dots', + 'library' => 'fa-regular', + ]; // Default icons. + $item_data['itemprop'] = 'commentCount'; + } + break; + + case 'terms': + $item_data['icon'] = 'fa fa-tags'; // Default icon + $item_data['selected_icon'] = [ + 'value' => 'fas fa-tags', + 'library' => 'fa-solid', + ]; // Default icons. + $item_data['itemprop'] = 'about'; + + $taxonomy = $repeater_item['taxonomy']; + $terms = wp_get_post_terms( get_the_ID(), $taxonomy ); + foreach ( $terms as $term ) { + $item_data['terms_list'][ $term->term_id ]['text'] = $term->name; + if ( 'yes' === $repeater_item['link'] ) { + $item_data['terms_list'][ $term->term_id ]['url'] = get_term_link( $term ); + } + } + break; + + case 'custom': + $item_data['text'] = $repeater_item['custom_text']; + $item_data['icon'] = 'fa fa-info-circle'; // Default icon. + $item_data['selected_icon'] = [ + 'value' => 'far fa-tags', + 'library' => 'fa-regular', + ]; // Default icons. + + if ( 'yes' === $repeater_item['link'] && ! empty( $repeater_item['custom_url'] ) ) { + $item_data['url'] = $repeater_item['custom_url']; + } + + break; + } + + $item_data['type'] = $repeater_item['type']; + + if ( ! empty( $repeater_item['text_prefix'] ) ) { + $item_data['text_prefix'] = esc_html( $repeater_item['text_prefix'] ); + } + + return $item_data; + } + + protected function render_item( $repeater_item ) { + $item_data = $this->get_meta_data( $repeater_item ); + $repeater_index = $repeater_item['_id']; + + if ( empty( $item_data['text'] ) && empty( $item_data['terms_list'] ) ) { + return; + } + + $has_link = false; + $link_key = 'link_' . $repeater_index; + $item_key = 'item_' . $repeater_index; + + $this->add_render_attribute( $item_key, 'class', + [ + 'elementor-icon-list-item', + 'elementor-repeater-item-' . $repeater_item['_id'], + ] + ); + + $active_settings = $this->get_active_settings(); + + if ( 'inline' === $active_settings['view'] ) { + $this->add_render_attribute( $item_key, 'class', 'elementor-inline-item' ); + } + + if ( ! empty( $item_data['url']['url'] ) ) { + $has_link = true; + + $this->add_link_attributes( $link_key, $item_data['url'] ); + } + + if ( ! empty( $item_data['itemprop'] ) ) { + $this->add_render_attribute( $item_key, 'itemprop', $item_data['itemprop'] ); + } + + ?> +
  • print_render_attribute_string( $item_key ); ?>> + + print_render_attribute_string( $link_key ); ?>> + + render_item_icon_or_image( $item_data, $repeater_item, $repeater_index ); ?> + render_item_text( $item_data, $repeater_index ); ?> + + + +
  • + + + add_render_attribute( + $image_data, + [ + 'class' => 'elementor-avatar', + 'src' => $item_data['image'], + 'alt' => sprintf( + /* translators: %s: Author name. */ + esc_attr__( 'Picture of %s', 'elementor-pro' ), + $item_data['text'] + ), + 'loading' => 'lazy', + ] + ); + ?> + print_render_attribute_string( $image_data ); ?>> + + 'true' ] ); + else : ?> + + + + + get_repeater_setting_key( 'text', 'icon_list', $repeater_index ); + + $this->add_render_attribute( $repeater_setting_key, 'class', [ 'elementor-icon-list-text', 'elementor-post-info__item', 'elementor-post-info__item--type-' . $item_data['type'] ] ); + if ( ! empty( $item['terms_list'] ) ) { + $this->add_render_attribute( $repeater_setting_key, 'class', 'elementor-terms-list' ); + } + + ?> + print_render_attribute_string( $repeater_setting_key ); ?>> + + + + + + + %s', $item_data['text'] ) + : $item_data['text']; + + echo wp_kses( $content, [ + 'a' => [ + 'href' => [], + 'title' => [], + 'rel' => [], + ], + 'time' => [], + ] ); + ?> + + + get_settings_for_display(); + + ob_start(); + if ( ! empty( $settings['icon_list'] ) ) { + foreach ( $settings['icon_list'] as $repeater_item ) { + $this->render_item( $repeater_item ); + } + } + $items_html = ob_get_clean(); + + if ( empty( $items_html ) ) { + return; + } + + if ( 'inline' === $settings['view'] ) { + $this->add_render_attribute( 'icon_list', 'class', 'elementor-inline-items' ); + } + + $this->add_render_attribute( 'icon_list', 'class', [ 'elementor-icon-list-items', 'elementor-post-info' ] ); + ?> +
      print_render_attribute_string( 'icon_list' ); ?>> + + +
    + start_controls_section( + 'section_post_navigation_content', + [ + 'label' => esc_html__( 'Post Navigation', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'show_label', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'prev_label', + [ + 'label' => esc_html__( 'Previous Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Previous', 'elementor-pro' ), + 'condition' => [ + 'show_label' => 'yes', + ], + ] + ); + + $this->add_control( + 'next_label', + [ + 'label' => esc_html__( 'Next Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Next', 'elementor-pro' ), + 'condition' => [ + 'show_label' => 'yes', + ], + 'ai' => [ + 'active' => false, + ], + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'show_arrow', + [ + 'label' => esc_html__( 'Arrows', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'arrow', + [ + 'label' => esc_html__( 'Arrows Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'fa fa-angle-left' => esc_html__( 'Angle', 'elementor-pro' ), + 'fa fa-angle-double-left' => esc_html__( 'Double Angle', 'elementor-pro' ), + 'fa fa-chevron-left' => esc_html__( 'Chevron', 'elementor-pro' ), + 'fa fa-chevron-circle-left' => esc_html__( 'Chevron Circle', 'elementor-pro' ), + 'fa fa-caret-left' => esc_html__( 'Caret', 'elementor-pro' ), + 'fa fa-arrow-left' => esc_html__( 'Arrow', 'elementor-pro' ), + 'fa fa-long-arrow-left' => esc_html__( 'Long Arrow', 'elementor-pro' ), + 'fa fa-arrow-circle-left' => esc_html__( 'Arrow Circle', 'elementor-pro' ), + 'fa fa-arrow-circle-o-left' => esc_html__( 'Arrow Circle Negative', 'elementor-pro' ), + ], + 'default' => 'fa fa-angle-left', + 'condition' => [ + 'show_arrow' => 'yes', + ], + ] + ); + + $this->add_control( + 'show_title', + [ + 'label' => esc_html__( 'Post Title', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'show_borders', + [ + 'label' => esc_html__( 'Borders', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + 'prefix_class' => 'elementor-post-navigation-borders-', + ] + ); + + // Filter out post type without taxonomies + $post_type_options = []; + $post_type_taxonomies = []; + foreach ( Utils::get_public_post_types() as $post_type => $post_type_label ) { + $taxonomies = Utils::get_taxonomies( [ 'object_type' => $post_type ], false ); + if ( empty( $taxonomies ) ) { + continue; + } + + $post_type_options[ $post_type ] = $post_type_label; + $post_type_taxonomies[ $post_type ] = []; + foreach ( $taxonomies as $taxonomy ) { + $post_type_taxonomies[ $post_type ][ $taxonomy->name ] = $taxonomy->label; + } + } + + $this->add_control( + 'in_same_term', + [ + 'label' => esc_html__( 'In same Term', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'options' => $post_type_options, + 'default' => '', + 'multiple' => true, + 'label_block' => true, + 'description' => esc_html__( 'Indicates whether next post must be within the same taxonomy term as the current post, this lets you set a taxonomy per each post type', 'elementor-pro' ), + ] + ); + + foreach ( $post_type_options as $post_type => $post_type_label ) { + $this->add_control( + $post_type . '_taxonomy', + [ + 'label' => $post_type_label . ' ' . esc_html__( 'Taxonomy', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $post_type_taxonomies[ $post_type ], + 'default' => '', + 'condition' => [ + 'in_same_term' => $post_type, + ], + ] + ); + } + + $this->end_controls_section(); + + $this->start_controls_section( + 'label_style', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'show_label' => 'yes', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_label_style' ); + + $this->start_controls_tab( + 'label_color_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'label_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}} span.post-navigation__prev--label' => 'color: {{VALUE}};', + '{{WRAPPER}} span.post-navigation__next--label' => 'color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'label_color_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'label_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} span.post-navigation__prev--label:hover' => 'color: {{VALUE}};', + '{{WRAPPER}} span.post-navigation__next--label:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'label_hover_color_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} span.post-navigation__prev--label' => 'transition-duration: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} span.post-navigation__next--label' => 'transition-duration: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'label_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + 'selector' => '{{WRAPPER}} span.post-navigation__prev--label, {{WRAPPER}} span.post-navigation__next--label', + 'exclude' => [ 'line_height' ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'title_style', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'show_title' => 'yes', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_post_navigation_style' ); + + $this->start_controls_tab( + 'tab_color_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}} span.post-navigation__prev--title, {{WRAPPER}} span.post-navigation__next--title' => 'color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_color_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} span.post-navigation__prev--title:hover, {{WRAPPER}} span.post-navigation__next--title:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'hover_color_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} span.post-navigation__prev--title' => 'transition-duration: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} span.post-navigation__next--title' => 'transition-duration: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'title_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_SECONDARY, + ], + 'selector' => '{{WRAPPER}} span.post-navigation__prev--title, {{WRAPPER}} span.post-navigation__next--title', + 'exclude' => [ 'line_height' ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'arrow_style', + [ + 'label' => esc_html__( 'Arrow', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'show_arrow' => 'yes', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_post_navigation_arrow_style' ); + + $this->start_controls_tab( + 'arrow_color_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'arrow_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .post-navigation__arrow-wrapper' => 'color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'arrow_color_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'arrow_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .post-navigation__arrow-wrapper:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'arrow_hover_color_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} .post-navigation__arrow-wrapper' => 'transition-duration: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_responsive_control( + 'arrow_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 300, + ], + 'em' => [ + 'max' => 30, + ], + 'rem' => [ + 'max' => 30, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .post-navigation__arrow-wrapper' => 'font-size: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'arrow_padding', + [ + 'label' => esc_html__( 'Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .post-navigation__arrow-prev' => 'padding-right: {{SIZE}}{{UNIT}};', + 'body:not(.rtl) {{WRAPPER}} .post-navigation__arrow-next' => 'padding-left: {{SIZE}}{{UNIT}};', + 'body.rtl {{WRAPPER}} .post-navigation__arrow-prev' => 'padding-left: {{SIZE}}{{UNIT}};', + 'body.rtl {{WRAPPER}} .post-navigation__arrow-next' => 'padding-right: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'borders_section_style', + [ + 'label' => esc_html__( 'Borders', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'show_borders!' => '', + ], + ] + ); + + $this->add_control( + 'sep_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + //'default' => '#D4D4D4', + 'selectors' => [ + '{{WRAPPER}} .elementor-post-navigation__separator' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .elementor-post-navigation' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'borders_width', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post-navigation__separator' => 'width: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .elementor-post-navigation' => 'border-top-width: {{SIZE}}{{UNIT}}; border-bottom-width: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .elementor-post-navigation__next.elementor-post-navigation__link' => 'width: calc(50% - ({{SIZE}}{{UNIT}} / 2))', + '{{WRAPPER}} .elementor-post-navigation__prev.elementor-post-navigation__link' => 'width: calc(50% - ({{SIZE}}{{UNIT}} / 2))', + ], + ] + ); + + $this->add_control( + 'borders_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-post-navigation' => 'padding: {{SIZE}}{{UNIT}} 0;', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + $prev_label = ''; + $next_label = ''; + $prev_arrow = ''; + $next_arrow = ''; + + if ( 'yes' === $settings['show_label'] ) { + $prev_label = '' . $settings['prev_label'] . ''; + $next_label = '' . $settings['next_label'] . ''; + } + + if ( 'yes' === $settings['show_arrow'] ) { + if ( is_rtl() ) { + $prev_icon_class = str_replace( 'left', 'right', $settings['arrow'] ); + $next_icon_class = $settings['arrow']; + } else { + $prev_icon_class = $settings['arrow']; + $next_icon_class = str_replace( 'left', 'right', $settings['arrow'] ); + } + + $prev_arrow = '' . esc_html__( 'Prev', 'elementor-pro' ) . ''; + $next_arrow = '' . esc_html__( 'Next', 'elementor-pro' ) . ''; + } + + $prev_title = ''; + $next_title = ''; + + if ( 'yes' === $settings['show_title'] ) { + $prev_title = '%title'; + $next_title = '%title'; + } + + $in_same_term = false; + $taxonomy = 'category'; + $post_type = get_post_type( get_queried_object_id() ); + + if ( ! empty( $settings['in_same_term'] ) && is_array( $settings['in_same_term'] ) && in_array( $post_type, $settings['in_same_term'] ) ) { + if ( isset( $settings[ $post_type . '_taxonomy' ] ) ) { + $in_same_term = true; + $taxonomy = $settings[ $post_type . '_taxonomy' ]; + } + } + ?> +
    +
    + ' . $prev_label . $prev_title . '', $in_same_term, '', $taxonomy ); ?> +
    + +
    +
    +
    + +
    + ' . $next_label . $next_title . '' . $next_arrow, $in_same_term, '', $taxonomy ); ?> +
    +
    + start_controls_section( + 'search_content', + [ + 'label' => esc_html__( 'Search Form', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'skin', + [ + 'label' => esc_html__( 'Skin', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'classic', + 'options' => [ + 'classic' => esc_html__( 'Classic', 'elementor-pro' ), + 'minimal' => esc_html__( 'Minimal', 'elementor-pro' ), + 'full_screen' => esc_html__( 'Full Screen', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-search-form--skin-', + 'render_type' => 'template', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'placeholder', + [ + 'label' => esc_html__( 'Placeholder', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'separator' => 'before', + 'default' => esc_html__( 'Search', 'elementor-pro' ) . '...', + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'heading_button_content', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'skin' => 'classic', + ], + ] + ); + + $this->add_control( + 'button_type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'icon', + 'options' => [ + 'icon' => esc_html__( 'Icon', 'elementor-pro' ), + 'text' => esc_html__( 'Text', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-search-form--button-type-', + 'render_type' => 'template', + 'condition' => [ + 'skin' => 'classic', + ], + ] + ); + + $this->add_control( + 'button_text', + [ + 'label' => esc_html__( 'Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Search', 'elementor-pro' ), + 'condition' => [ + 'button_type' => 'text', + 'skin' => 'classic', + ], + ] + ); + + $this->add_control( + 'icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'search', + 'options' => [ + 'search' => [ + 'title' => esc_html__( 'Search', 'elementor-pro' ), + 'icon' => 'eicon-search', + ], + 'arrow' => [ + 'title' => esc_html__( 'Arrow', 'elementor-pro' ), + 'icon' => 'eicon-arrow-right', + ], + ], + 'render_type' => 'template', + 'prefix_class' => 'elementor-search-form--icon-', + 'condition' => [ + 'button_type' => 'icon', + 'skin' => 'classic', + ], + ] + ); + + $this->add_control( + 'size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 50, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__container' => 'min-height: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .elementor-search-form__submit' => 'min-width: {{SIZE}}{{UNIT}}', + 'body:not(.rtl) {{WRAPPER}} .elementor-search-form__icon' => 'padding-left: calc({{SIZE}}{{UNIT}} / 3)', + 'body.rtl {{WRAPPER}} .elementor-search-form__icon' => 'padding-right: calc({{SIZE}}{{UNIT}} / 3)', + '{{WRAPPER}} .elementor-search-form__input, {{WRAPPER}}.elementor-search-form--button-type-text .elementor-search-form__submit' => 'padding-left: calc({{SIZE}}{{UNIT}} / 3); padding-right: calc({{SIZE}}{{UNIT}} / 3)', + ], + 'condition' => [ + 'skin!' => 'full_screen', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'toggle_button_content', + [ + 'label' => esc_html__( 'Toggle', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'skin' => 'full_screen', + ], + ] + ); + + $this->add_control( + 'toggle_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'center', + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-h-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form' => 'text-align: {{VALUE}}', + ], + 'condition' => [ + 'skin' => 'full_screen', + ], + ] + ); + + $this->add_control( + 'toggle_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 33, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__toggle' => '--e-search-form-toggle-size: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'skin' => 'full_screen', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_input_style', + [ + 'label' => esc_html__( 'Input', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'icon_size_minimal', + [ + 'label' => esc_html__( 'Icon Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__icon' => '--e-search-form-icon-size-minimal: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'skin' => 'minimal', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'overlay_background_color', + [ + 'label' => esc_html__( 'Overlay Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-search-form--skin-full_screen .elementor-search-form__container' => 'background-color: {{VALUE}}', + ], + 'condition' => [ + 'skin' => 'full_screen', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'input_typography', + 'selector' => '{{WRAPPER}} input[type="search"].elementor-search-form__input', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->start_controls_tabs( 'tabs_input_colors' ); + + $this->start_controls_tab( + 'tab_input_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'input_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__input, + {{WRAPPER}} .elementor-search-form__icon, + {{WRAPPER}} .elementor-lightbox .dialog-lightbox-close-button, + {{WRAPPER}} .elementor-lightbox .dialog-lightbox-close-button:hover, + {{WRAPPER}}.elementor-search-form--skin-full_screen input[type="search"].elementor-search-form__input' => 'color: {{VALUE}}; fill: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'input_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}:not(.elementor-search-form--skin-full_screen) .elementor-search-form__container' => 'background-color: {{VALUE}}', + '{{WRAPPER}}.elementor-search-form--skin-full_screen input[type="search"].elementor-search-form__input' => 'background-color: {{VALUE}}', + ], + 'condition' => [ + 'skin!' => 'full_screen', + ], + ] + ); + + $this->add_control( + 'input_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}:not(.elementor-search-form--skin-full_screen) .elementor-search-form__container' => 'border-color: {{VALUE}}', + '{{WRAPPER}}.elementor-search-form--skin-full_screen input[type="search"].elementor-search-form__input' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'input_box_shadow', + 'selector' => '{{WRAPPER}} .elementor-search-form__container', + 'fields_options' => [ + 'box_shadow_type' => [ + 'separator' => 'default', + ], + ], + 'condition' => [ + 'skin!' => 'full_screen', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_input_focus', + [ + 'label' => esc_html__( 'Focus', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'input_text_color_focus', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}:not(.elementor-search-form--skin-full_screen) .elementor-search-form--focus .elementor-search-form__input, + {{WRAPPER}} .elementor-search-form--focus .elementor-search-form__icon, + {{WRAPPER}} .elementor-lightbox .dialog-lightbox-close-button:hover, + {{WRAPPER}}.elementor-search-form--skin-full_screen input[type="search"].elementor-search-form__input:focus' => 'color: {{VALUE}}; fill: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'input_background_color_focus', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}:not(.elementor-search-form--skin-full_screen) .elementor-search-form--focus .elementor-search-form__container' => 'background-color: {{VALUE}}', + '{{WRAPPER}}.elementor-search-form--skin-full_screen input[type="search"].elementor-search-form__input:focus' => 'background-color: {{VALUE}}', + ], + 'condition' => [ + 'skin!' => 'full_screen', + ], + ] + ); + + $this->add_control( + 'input_border_color_focus', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}:not(.elementor-search-form--skin-full_screen) .elementor-search-form--focus .elementor-search-form__container' => 'border-color: {{VALUE}}', + '{{WRAPPER}}.elementor-search-form--skin-full_screen input[type="search"].elementor-search-form__input:focus' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'input_box_shadow_focus', + 'selector' => '{{WRAPPER}} .elementor-search-form--focus .elementor-search-form__container', + 'fields_options' => [ + 'box_shadow_type' => [ + 'separator' => 'default', + ], + ], + 'condition' => [ + 'skin!' => 'full_screen', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'button_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}:not(.elementor-search-form--skin-full_screen) .elementor-search-form__container' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + '{{WRAPPER}}.elementor-search-form--skin-full_screen input[type="search"].elementor-search-form__input' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'default' => [ + 'size' => 3, + ], + 'selectors' => [ + '{{WRAPPER}}:not(.elementor-search-form--skin-full_screen) .elementor-search-form__container' => 'border-radius: {{SIZE}}{{UNIT}}', + '{{WRAPPER}}.elementor-search-form--skin-full_screen input[type="search"].elementor-search-form__input' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_button_style', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'skin' => 'classic', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'button_typography', + 'selector' => '{{WRAPPER}} .elementor-search-form__submit', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'condition' => [ + 'button_type' => 'text', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_button_colors' ); + + $this->start_controls_tab( + 'tab_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__submit' => '--e-search-form-submit-text-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_SECONDARY, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__submit' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_text_color_hover', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__submit:hover' => '--e-search-form-submit-text-color: {{VALUE}}', + '{{WRAPPER}} .elementor-search-form__submit:focus' => '--e-search-form-submit-text-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'button_background_color_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__submit:hover' => 'background-color: {{VALUE}}', + '{{WRAPPER}} .elementor-search-form__submit:focus' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_responsive_control( + 'icon_size', + [ + 'label' => esc_html__( 'Icon Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__submit' => '--e-search-form-submit-icon-size: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'button_type' => 'icon', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'button_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 10, + 'step' => 0.1, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__submit' => 'min-width: calc( {{SIZE}} * {{size.SIZE}}{{size.UNIT}} )', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_toggle_style', + [ + 'label' => esc_html__( 'Toggle', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'skin' => 'full_screen', + ], + ] + ); + + $this->start_controls_tabs( 'tabs_toggle_color' ); + + $this->start_controls_tab( + 'tab_toggle_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'toggle_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__toggle' => '--e-search-form-toggle-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'toggle_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__toggle' => '--e-search-form-toggle-background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_toggle_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'toggle_color_hover', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__toggle:hover' => '--e-search-form-toggle-color: {{VALUE}}', + '{{WRAPPER}} .elementor-search-form__toggle:focus' => '--e-search-form-toggle-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'toggle_background_color_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__toggle:hover' => '--e-search-form-toggle-background-color: {{VALUE}}', + '{{WRAPPER}} .elementor-search-form__toggle:focus' => '--e-search-form-toggle-background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'toggle_icon_size', + [ + 'label' => esc_html__( 'Icon Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__toggle' => '--e-search-form-toggle-icon-size: calc({{SIZE}}em / 100)', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'toggle_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__toggle' => '--e-search-form-toggle-border-width: {{SIZE}}{{UNIT}}', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'toggle_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-search-form__toggle' => '--e-search-form-toggle-border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + $this->add_render_attribute( + 'form', + [ + 'class' => 'elementor-search-form', + 'action' => home_url(), + 'method' => 'get', + ] + ); + + $this->add_render_attribute( + 'container', + [ + 'class' => 'elementor-search-form__container', + ] + ); + + $this->add_render_attribute( + 'label', + [ + 'class' => 'elementor-screen-only', + 'for' => 'elementor-search-form-' . $this->get_id(), + ] + ); + + $this->add_render_attribute( + 'input', + [ + 'id' => 'elementor-search-form-' . $this->get_id(), + 'placeholder' => $settings['placeholder'], + 'class' => 'elementor-search-form__input', + 'type' => 'search', + 'name' => 's', + 'value' => get_search_query(), + ] + ); + + // Set the selected icon. + $icon_class = 'search'; + + if ( 'icon' === $settings['button_type'] && 'arrow' === $settings['icon'] ) { + $icon_class = is_rtl() ? 'arrow-left' : 'arrow-right'; + } + + $this->add_render_attribute( 'icon', 'class', 'fa fa-' . $icon_class ); + + $icon = [ + 'value' => 'fas fa-' . $icon_class, + 'library' => 'fa-solid', + ]; + ?> + +
    print_render_attribute_string( 'form' ); ?>> + + +
    + render_search_icon( $icon, [ 'aria-hidden' => 'true' ] ); ?> + +
    + +
    print_render_attribute_string( 'container' ); ?>> + + + +
    + render_search_icon( $icon, [ 'aria-hidden' => 'true' ] ); ?> + +
    + + + print_render_attribute_string( 'input' ); ?>> + + + + + + + +
    + 'eicons', + 'value' => 'eicon-close', + ], [ 'aria-hidden' => 'true' ] ); + ?> + +
    + +
    +
    +
    + + <# + view.addRenderAttribute( + 'form', + { + 'class': 'elementor-search-form', + 'action': '', + 'method': 'get', + } + ); + + view.addRenderAttribute( + 'container', + { + 'class': 'elementor-search-form__container', + } + ); + + view.addRenderAttribute( + 'label', + { + 'class': 'elementor-screen-only', + 'for': 'elementor-search-form-get_id() ); ?>', + } + ); + + view.addRenderAttribute( + 'input', + { + 'id': 'elementor-search-form-get_id() ); ?>', + 'placeholder': settings.placeholder, + 'class': 'elementor-search-form__input', + 'type': 'search', + 'name': 's', + } + ); + + var iconClass = 'fa fas fa-search'; + + if ( 'arrow' === settings.icon ) { + if ( elementorCommon.config.isRTL ) { + iconClass = 'fa fas fa-arrow-left'; + } else { + iconClass = 'fa fas fa-arrow-right'; + } + } + #> + +
    + <# if ( 'full_screen' === settings.skin ) { #> +
    + + +
    + <# } #> +
    + + + <# if ( 'minimal' === settings.skin ) { #> +
    + + +
    + <# } #> + + + + <# if ( 'classic' === settings.skin ) { #> + + <# } #> +
    +
    +
    + experiments->is_feature_active( 'e_font_icon_svg' ) ) { + $icon_html = Icons_Manager::render_font_icon( $icon, $attributes ); + + Utils::print_unescaped_internal_string( sprintf( '
    %s
    ', $icon_html ) ); + } else { + $migration_allowed = Icons_Manager::is_migration_allowed(); + + if ( ! $migration_allowed || ! Icons_Manager::render_icon( $icon, [ 'aria-hidden' => 'true' ] ) ) { + Utils::print_unescaped_internal_string( sprintf( '', esc_attr( $this->get_render_attribute_string( 'icon' ) ) ) ); + } + } + } + + public function get_group_name() { + return 'theme-elements'; + } +} diff --git a/modules/theme-elements/widgets/sitemap.php b/modules/theme-elements/widgets/sitemap.php new file mode 100644 index 0000000..d2e34d4 --- /dev/null +++ b/modules/theme-elements/widgets/sitemap.php @@ -0,0 +1,736 @@ +start_controls_section( + 'sitemap_section', + [ + 'label' => esc_html__( 'Sitemap', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->register_post_type_controls(); + + $this->add_control( + 'sitemap_layout_divider', + [ + 'type' => Controls_Manager::DIVIDER, + ] + ); + + $this->register_layout_controls(); + + $this->end_controls_section(); + + $this->register_additional_settings_section(); + } + + private function register_additional_settings_section() { + $this->start_controls_section( + 'sitemap_query_section', + [ + 'label' => esc_html__( 'Additional Options', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_control( + 'sitemap_exclude', + [ + 'label' => esc_html__( 'Exclude', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'options' => [ + 'current_post' => esc_html__( 'Current Post', 'elementor-pro' ), + 'manual_selection' => esc_html__( 'Manual Selection', 'elementor-pro' ), + ], + 'label_block' => true, + ] + ); + + $this->add_control( + 'sitemap_exclude_ids', + [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => Query_Module::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'multiple' => true, + 'autocomplete' => [ + 'object' => Query_Module::QUERY_OBJECT_POST, + ], + 'condition' => [ + 'sitemap_exclude' => 'manual_selection', + ], + ] + ); + + $this->add_control( + 'sitemap_password_protected', + [ + 'label' => esc_html__( 'Protected Posts', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + ] + ); + + $this->end_controls_section(); + } + + private function register_layout_controls() { + $this->add_responsive_control( + 'sitemap_columns', + [ + 'label' => esc_html__( 'Columns', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '4', + 'tablet_default' => '2', + 'mobile_default' => '1', + 'options' => [ + '1' => '1', + '2' => '2', + '3' => '3', + '4' => '4', + '5' => '5', + '6' => '6', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-sitemap-section' => 'flex-basis: calc( 1 / {{VALUE}} * 100% );', + ], + ] + ); + + $this->add_control( + 'sitemap_title_tag', + [ + 'label' => esc_html__( 'Title HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + 'p' => 'p', + ], + 'default' => 'h2', + ] + ); + + $this->add_control( + 'sitemap_add_nofollow', + [ + 'label' => esc_html__( 'Add nofollow', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + ] + ); + } + + private function register_post_type_controls() { + $supported_taxonomies = []; + + $public_types = Pro_Utils::get_public_post_types(); + + foreach ( $public_types as $type => $title ) { + $taxonomies = get_object_taxonomies( $type, 'objects' ); + foreach ( $taxonomies as $key => $tax ) { + if ( ! in_array( $tax->name, $supported_taxonomies ) ) { + $label = $tax->label . ' (' . $tax->name . ')'; + $supported_taxonomies[ $tax->name ] = $label; + } + } + } + + $repeater = new Repeater(); + + $repeater->add_control( + 'sitemap_type_selector', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'post_type', + 'options' => [ + 'post_type' => esc_html__( 'Post Type', 'elementor-pro' ), + 'taxonomy' => esc_html__( 'Taxonomy', 'elementor-pro' ), + ], + ] + ); + + $repeater->add_control( + 'sitemap_source_post_type', + [ + 'label' => esc_html__( 'Source', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'page', + 'options' => $public_types, + 'condition' => [ + 'sitemap_type_selector' => 'post_type', + ], + ] + ); + + $repeater->add_control( + 'sitemap_source_taxonomy', + [ + 'label' => esc_html__( 'Source', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'category', + 'options' => $supported_taxonomies, + 'condition' => [ + 'sitemap_type_selector' => 'taxonomy', + ], + ] + ); + + $repeater->add_control( + 'sitemap_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'sitemap_orderby_post_type', + [ + 'label' => esc_html__( 'Order By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'post_date', + 'options' => [ + 'post_date' => esc_html__( 'Date', 'elementor-pro' ), + 'post_title' => esc_html__( 'Title', 'elementor-pro' ), + 'menu_order' => esc_html__( 'Menu Order', 'elementor-pro' ), + 'rand' => esc_html__( 'Random', 'elementor-pro' ), + ], + 'condition' => [ + 'sitemap_type_selector' => 'post_type', + ], + ] + ); + + $repeater->add_control( + 'sitemap_orderby_taxonomy', + [ + 'label' => esc_html__( 'Order By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'name', + 'options' => [ + 'id' => esc_html__( 'ID', 'elementor-pro' ), + 'name' => esc_html__( 'Name', 'elementor-pro' ), + ], + 'condition' => [ + 'sitemap_type_selector' => 'taxonomy', + ], + ] + ); + + $repeater->add_control( + 'sitemap_order', + [ + 'label' => esc_html__( 'Order', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'desc', + 'options' => [ + 'asc' => esc_html__( 'ASC', 'elementor-pro' ), + 'desc' => esc_html__( 'DESC', 'elementor-pro' ), + ], + ] + ); + + $repeater->add_control( + 'sitemap_hide_empty', + [ + 'label' => esc_html__( 'Hide Empty', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'condition' => [ + 'sitemap_type_selector' => 'taxonomy', + ], + ] + ); + + $repeater->add_control( + 'sitemap_hierarchical', + [ + 'label' => esc_html__( 'Hierarchical View', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'no', + ] + ); + + $repeater->add_control( + 'sitemap_depth', + [ + 'label' => esc_html__( 'Depth', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '0', + 'options' => [ + '0' => esc_html__( 'All', 'elementor-pro' ), + '1' => 1, + '2' => 2, + '3' => 3, + '4' => 4, + '5' => 5, + '6' => 6, + ], + 'condition' => [ + 'sitemap_hierarchical' => 'yes', + ], + ] + ); + + $this->add_control( + 'sitemap_items', + [ + 'label' => '', + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'default' => [ + [ + 'sitemap_type_selector' => 'post_type', + 'sitemap_title' => esc_html__( 'Pages', 'elementor-pro' ), + 'sitemap_source_post_type' => 'page', + ], + [ + 'sitemap_type_selector' => 'taxonomy', + 'sitemap_title' => esc_html__( 'Categories', 'elementor-pro' ), + 'sitemap_source_taxonomy' => 'category', + ], + ], + 'title_field' => '{{{ sitemap_title }}}', + ] + ); + } + + private function register_style_tab() { + $this->start_controls_section( + 'section_sitemap_style', + [ + 'label' => esc_html__( 'List', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'sitemap_list_indent', + [ + 'label' => esc_html__( 'Indent', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .elementor-sitemap-section ul' => 'margin-left: {{SIZE}}{{UNIT}};', + 'body.rtl {{WRAPPER}} .elementor-sitemap-section ul' => 'margin-right: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'sitemap_section_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-sitemap-section' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'sitemap_title_style', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sitemap_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-sitemap-title' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sitemap_title_typography', + 'selector' => '{{WRAPPER}} .elementor-sitemap-title', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + ] + ); + + $this->add_control( + 'sitemap_list_style', + [ + 'label' => esc_html__( 'List Item', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sitemap_list_item_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-sitemap-item, {{WRAPPER}} span.elementor-sitemap-list, {{WRAPPER}} .elementor-sitemap-item a' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sitemap_list_item_typography', + 'selector' => '{{WRAPPER}} .elementor-sitemap-item, {{WRAPPER}} span.elementor-sitemap-list, {{WRAPPER}} .elementor-sitemap-item a', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_control( + 'sitemap_bullet_style', + [ + 'label' => esc_html__( 'Bullet', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sitemap_bullet_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .elementor-sitemap-item' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_control( + 'sitemap_list_item_bullet_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'disc', + 'options' => [ + 'disc' => [ + 'title' => esc_html__( 'Disc', 'elementor-pro' ), + 'icon' => 'eicon-circle', + ], + 'circle' => [ + 'title' => esc_html__( 'Circle', 'elementor-pro' ), + 'icon' => 'eicon-circle-o', + ], + 'square' => [ + 'title' => esc_html__( 'Square', 'elementor-pro' ), + 'icon' => 'eicon-square', + ], + 'none' => [ + 'title' => esc_html__( 'None', 'elementor-pro' ), + 'icon' => 'eicon-ban', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-sitemap-list, {{WRAPPER}} .elementor-sitemap-list .children' => 'list-style-type: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function register_controls() { + $this->register_sitemap_tab(); + $this->register_style_tab(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + if ( empty( $settings['sitemap_items'] ) ) { + return; + } + + $title_tag = $settings['sitemap_title_tag']; + + $posts_query = [ + 'post__not_in' => $settings['sitemap_exclude_ids'], + 'has_password' => 'yes' === $settings['sitemap_password_protected'] ? null : false, + ]; + + $this->add_render_attribute( [ + 'category_link' => [ + 'class' => 'elementor-sitemap-category-title', + ], + 'wrapper' => [ + 'class' => 'elementor-sitemap-wrap', + ], + ] ); + + if ( 'yes' === $settings['sitemap_add_nofollow'] ) { + $this->add_render_attribute( 'a', 'rel', 'nofollow' ); + } + + echo '
    print_render_attribute_string( 'wrapper' ); + echo '>'; + foreach ( $settings['sitemap_items'] as $sitemap_item ) { + // PHPCS - `render_sitemap_item` is safe. + echo $this->render_sitemap_item( $sitemap_item, $title_tag, $posts_query ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + echo '
    '; + } + + private function get_list_title( $current_title, $item_type, $is_taxonomy ) { + if ( '' !== $current_title ) { + return $current_title; + } + + if ( $is_taxonomy ) { + $obj = get_taxonomy( $item_type ); + if ( false === $obj ) { + return ''; + } + return $obj->label; + } + + $obj = get_post_type_object( $item_type ); + if ( null === $obj ) { + return ''; + } + if ( '' === $obj->labels->name ) { + return $obj->labels->singular_name; + } + + return $obj->labels->name; + } + + private function render_sitemap_item( $sitemap_item, $title_tag, $query_args ) { + $hierarchical = 'yes' === $sitemap_item['sitemap_hierarchical']; + $max_depth = $sitemap_item['sitemap_depth']; + $query_args['orderby'] = $sitemap_item['sitemap_orderby_post_type']; + $query_args['order'] = $sitemap_item['sitemap_order']; + $is_taxonomy = 'taxonomy' === $sitemap_item['sitemap_type_selector']; + $item_type = $is_taxonomy ? $sitemap_item['sitemap_source_taxonomy'] : $sitemap_item['sitemap_source_post_type']; + $title = $this->get_list_title( $sitemap_item['sitemap_title'], $item_type, $is_taxonomy ); + + $this->add_render_attribute( [ + 'section' . $item_type => [ + 'class' => [ + 'elementor-sitemap-section', + ], + ], + 'list' . $item_type => [ + 'class' => [ + 'elementor-sitemap-list', + 'elementor-sitemap-' . $item_type . '-list', + ], + ], + $title_tag . $item_type => [ + 'class' => [ + 'elementor-sitemap-title', + 'elementor-sitemap-' . $item_type . '-title', + ], + ], + 'item' . $item_type => [ + 'class' => [ + 'elementor-sitemap-item', + 'elementor-sitemap-item-' . $item_type, + ], + ], + ] ); + + $items_html = ''; + + if ( $is_taxonomy ) { + $items_html .= $this->sitemap_html_taxonomies( $item_type, $hierarchical, $max_depth, $sitemap_item, $query_args ); + } else { + $items_html .= $this->sitemap_html_post_types( $item_type, $hierarchical, $max_depth, $query_args ); + } + + $title = empty( $title ) ? '' : sprintf( '<%s %s>%s', Utils::validate_html_tag( $title_tag ), $this->get_render_attribute_string( $title_tag . $item_type ), $title ); + + $html = sprintf( '
    %s', $this->get_render_attribute_string( 'section' . $item_type ), $title ); + if ( empty( $items_html ) ) { + $html .= sprintf( '%s', $this->get_render_attribute_string( 'list' . $item_type ), esc_html__( 'None', 'elementor-pro' ) ); + } else { + $html .= sprintf( '
      %s
    ', $this->get_render_attribute_string( 'list' . $item_type ), $items_html ); + } + $html .= '
    '; + + return $html; + } + + private function sitemap_html_taxonomies( $taxonomy, $hierarchical, $max_depth, $item_settings, $query_args ) { + $query_args['hide_empty'] = 'yes' === $item_settings['sitemap_hide_empty']; + $query_args['show_option_none'] = ''; + $query_args['taxonomy'] = $taxonomy; + $query_args['title_li'] = ''; + $query_args['echo'] = false; + $query_args['depth'] = $max_depth; + $query_args['hierarchical'] = $hierarchical; + $query_args['orderby'] = $item_settings['sitemap_orderby_taxonomy']; + + $taxonomy_list = wp_list_categories( $query_args ); + $taxonomy_list = $this->add_sitemap_item_classes( 'item' . $taxonomy, $taxonomy_list ); + + return $taxonomy_list; + } + + /** + * @param string $post_type + * @param array $query_args + * + * @return \WP_Query + */ + private function query_by_post_type( $post_type, $query_args ) { + $args = [ + 'posts_per_page' => -1, + 'update_post_meta_cache' => false, + 'post_type' => $post_type, + 'filter' => 'ids', + 'post_status' => 'publish', + ]; + + $args = array_merge( $query_args, $args ); + + $query = new \WP_Query( $args ); + + return $query; + } + + /** + * @param string $post_type + * @param bool $hierarchical + * @param int $depth + * @param array $query_args + * + * @return string + */ + private function sitemap_html_post_types( $post_type, $hierarchical, $depth, $query_args ) { + $html = ''; + + $query_result = $this->query_by_post_type( $post_type, $query_args ); + + if ( empty( $query_result ) ) { + return ''; + } + + if ( $query_result->have_posts() ) { + if ( ! $hierarchical ) { + $depth = -1; + } + $walker = new \Walker_Page(); + $walker->tree_type = $post_type; + $walker_str = $walker->walk( $query_result->posts, $depth ); + $html .= $this->add_sitemap_item_classes( 'item' . $post_type, $walker_str ); + } + + return $html; + } + + private function add_sitemap_item_classes( $element, $str ) { + $element_str = $this->get_render_attribute_string( $element ); + /** remove trailing " */ + $element_str = substr_replace( $element_str, ' ', -1, 1 ); + $source = [ + 'class="', + ]; + $replace = [ + $element_str, + ]; + + if ( 'yes' === $this->get_settings_for_display( 'sitemap_add_nofollow' ) ) { + $source[] = 'href='; + $replace[] = 'rel="nofollow" href='; + } + + return str_replace( $source, $replace, $str ); + } + + public function get_group_name() { + return 'theme-elements'; + } +} + + diff --git a/modules/tiers/admin-menu-items/base-promotion-item.php b/modules/tiers/admin-menu-items/base-promotion-item.php new file mode 100644 index 0000000..fa88eda --- /dev/null +++ b/modules/tiers/admin-menu-items/base-promotion-item.php @@ -0,0 +1,67 @@ + $this->get_promotion_title(), + 'description' => $this->get_promotion_description(), + 'image' => $this->get_image_url(), + 'upgrade_text' => $this->get_cta_text(), + 'upgrade_url' => $this->get_cta_url(), + ]; + ?> +
    +
    + + +

    + +

    + + + + +
    +
    + get_content_lines() ) ) { + ?> +
      + get_content_lines() as $item ) { ?> +
    • + +
    + get_promotion_data(); + ?> +
    +
    +

    + + + + + + + + +
    +

    +
    + + +
    + + +
    + $this->get_promotion_title(), + 'cta_url' => $this->get_cta_url(), + 'cta_text' => $this->get_cta_text(), + 'video_url' => $this->get_video_url(), + 'lines' => $this->get_lines(), + 'side_note' => $this->get_side_note(), + ]; + } +} diff --git a/modules/tiers/module.php b/modules/tiers/module.php new file mode 100644 index 0000000..fc06d55 --- /dev/null +++ b/modules/tiers/module.php @@ -0,0 +1,53 @@ + + + 'Custom Fonts', + 'custom_icons' => 'Custom Icons', + ]; + } + + public function get_custom_fonts() : array { + return [ + 'value' => count( Module::get_fonts_usage() ), + ]; + } + + public function get_custom_icons() : array { + return [ + 'value' => count( Module::get_icons_usage() ), + ]; + } +} diff --git a/modules/usage/integrations-reporter.php b/modules/usage/integrations-reporter.php new file mode 100644 index 0000000..4f5d30c --- /dev/null +++ b/modules/usage/integrations-reporter.php @@ -0,0 +1,49 @@ + '', + ]; + } + + public function get_integrations() : array { + $usage_integrations_text = ''; + + $integrations = Module::instance()->get_integrations_usage(); + + foreach ( array_keys( $integrations ) as $integration ) { + $usage_integrations_text .= '' . $integration . '' . esc_html__( 'Active', 'elementor-pro' ) . ''; + } + + return [ + 'value' => $usage_integrations_text, + ]; + } + + public function get_raw_integrations() : array { + $usage_integrations = PHP_EOL; + + $integrations = Module::instance()->get_integrations_usage(); + + foreach ( array_keys( $integrations ) as $integration ) { + $usage_integrations .= "\t" . $integration . ': ' . esc_html__( 'Active', 'elementor-pro' ) . PHP_EOL; + } + + return [ + 'value' => $usage_integrations, + ]; + } +} diff --git a/modules/usage/module.php b/modules/usage/module.php new file mode 100644 index 0000000..ef11ae8 --- /dev/null +++ b/modules/usage/module.php @@ -0,0 +1,190 @@ +settings->get_tabs(); + $integrations = $settings_tab['integrations']['sections']; + + foreach ( $integrations as $integration_name => $integration_data ) { + $integration_options = []; + $integration_fields_count = count( $integration_data['fields'] ); + + foreach ( $integration_data['fields'] as $field_name => $field_data ) { + $integration_options [] = get_option( 'elementor_' . $field_name ); + } + /** + * array_filter will clear all empty array values. + * if all the values filled then the count should be the same. + */ + if ( count( array_filter( $integration_options ) ) === $integration_fields_count ) { + $usage[ $integration_name ] = true; + } + } + + return $usage; + } + + /** + * Get fonts usage. + * + * Retrieve the number of Elementor fonts variants saved. + * + * @access public + * @static + * + * @return array The number of Elementor fonts variants. + */ + public static function get_fonts_usage() { + $usage = []; + $query = new \WP_Query( [ + 'posts_per_page' => -1, + 'post_type' => 'elementor_font', + ] ); + + $post_index = 0; + foreach ( $query->get_posts() as $post ) { + $elementor_font_files = get_post_meta( $post->ID, Custom_Fonts::FONT_META_KEY ); + + if ( ! empty( $elementor_font_files ) ) { + foreach ( $elementor_font_files as $elementor_font_index => $elementor_font_file ) { + $current = & $usage[ $post_index ]; + + foreach ( $elementor_font_file as $elementor_font_variant_index => $elementor_font_variant ) { + $current_variant = & $current[ 'variant_' . $elementor_font_variant_index ]; + + foreach ( [ 'weight', 'style' ] as $font_prop ) { + $current_variant[ $font_prop ] = $elementor_font_variant[ 'font_' . $font_prop ]; + } + + $current_variant['types'] = []; + foreach ( [ 'woff', 'woff2', 'ttf', 'svg', 'eot' ] as $font_ext ) { + if ( isset( $elementor_font_variant[ $font_ext ] ) && strlen( $elementor_font_variant[ $font_ext ]['url'] ) ) { + $current_variant['types'][] = $font_ext; + } + } + } + } + + $post_index++; + } + } + + return $usage; + } + + /** + * Get icons usage. + * + * Retrieve the number of Elementor icons saved. + * + * @access public + * @static + * + * @return array The number of Elementor icons. + */ + public static function get_icons_usage() { + $usage = []; + $query = new \WP_Query( [ + 'posts_per_page' => -1, + 'post_type' => 'elementor_icons', + ] ); + + $index = 0; + foreach ( $query->get_posts() as $post ) { + $elementor_custom_icon_set_config = get_post_meta( $post->ID, Custom_Icons::META_KEY ); + + if ( isset( $elementor_custom_icon_set_config[0] ) ) { + $elementor_custom_icon_set_config = json_decode( $elementor_custom_icon_set_config[0] ); + + $usage[ $index ] = (int) $elementor_custom_icon_set_config->count; + + $index++; + } + } + + return $usage; + } + + /** + * Add's tracking data. + * + * Called on elementor/tracker/send_tracking_data_params. + * + * @param array $params + * + * @return array + */ + public function add_tracking_data( $params ) { + unset( $params['is_first_time'] ); + + $params['install_time_pro'] = Plugin::instance()->license_admin->get_installed_time(); + + $params['usages']['integrations'] = $this->get_integrations_usage(); + $params['usages']['icons'] = $this->get_icons_usage(); + $params['usages']['fonts'] = $this->get_fonts_usage(); + + return $params; + } + + public function register_system_info_reporters() { + System_Info::add_report( 'features', [ + 'file_name' => __DIR__ . '/features-reporter.php', + 'class_name' => __NAMESPACE__ . '\Features_Reporter', + ] ); + + System_Info::add_report( 'integrations', [ + 'file_name' => __DIR__ . '/integrations-reporter.php', + 'class_name' => __NAMESPACE__ . '\Integrations_Reporter', + ] ); + } + + /** + * Usage module constructor. + * + * Initializing Elementor usage module. + * + * @access public + */ + public function __construct() { + add_filter( 'elementor/tracker/send_tracking_data_params', [ $this, 'add_tracking_data' ] ); + + add_action( 'admin_init', [ $this, 'register_system_info_reporters' ], 60 ); + } +} diff --git a/modules/video-playlist/module.php b/modules/video-playlist/module.php new file mode 100644 index 0000000..1accdb0 --- /dev/null +++ b/modules/video-playlist/module.php @@ -0,0 +1,23 @@ +start_controls_section( + 'section_playlist', + [ + 'label' => esc_html__( 'Playlist', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'tabs_direction', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::HIDDEN, + 'default' => 'vertical', + 'options' => [ + 'horizontal' => esc_html__( 'Horizontal', 'elementor-pro' ), + 'vertical' => esc_html__( 'Vertical', 'elementor-pro' ), + ], + 'prefix_class' => 'e-tabs-view-', + ] + ); + + $this->add_control( + 'playlist_title', + [ + 'label' => esc_html__( 'Playlist Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Playlist', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Playlist', 'elementor-pro' ), + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'playlist_title_tag', + [ + 'label' => esc_html__( 'HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + ], + 'default' => 'h2', + 'condition' => [ + 'playlist_title!' => '', + ], + ] + ); + + $repeater = new Repeater(); + + $repeater->add_control( + 'type', + [ + 'label' => esc_html__( 'Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'youtube', + 'options' => [ + 'youtube' => esc_html__( 'YouTube', 'elementor-pro' ), + 'vimeo' => esc_html__( 'Vimeo', 'elementor-pro' ), + 'hosted' => esc_html__( 'Self Hosted', 'elementor-pro' ), + 'section' => esc_html__( 'Section', 'elementor-pro' ), + ], + ] + ); + + $repeater->add_control( + 'youtube_url', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'placeholder' => esc_html__( 'Paste URL', 'elementor-pro' ) . ' (YouTube)', + 'label_block' => true, + 'condition' => [ + 'type' => 'youtube', + ], + ] + ); + + $repeater->add_control( + 'vimeo_url', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + 'categories' => [ + TagsModule::POST_META_CATEGORY, + TagsModule::URL_CATEGORY, + ], + ], + 'ai' => [ + 'active' => false, + ], + 'placeholder' => esc_html__( 'Enter your URL', 'elementor-pro' ) . ' (Vimeo)', + 'default' => 'https://vimeo.com/235215203', + 'label_block' => true, + 'condition' => [ + 'type' => 'vimeo', + ], + ] + ); + + $repeater->add_control( + 'vimeo_fetch_data', + [ + 'type' => Controls_Manager::BUTTON, + 'label_block' => true, + 'text' => esc_html__( 'Get Video Data', 'elementor-pro' ), + 'event' => 'elementorPlaylistWidget:fetchVideoData', + 'condition' => [ + 'type' => [ 'youtube', 'vimeo' ], + ], + ] + ); + + $repeater->add_control( + 'is_external_url', + [ + 'label' => esc_html__( 'External URL', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'condition' => [ + 'type' => 'hosted', + ], + ] + ); + + $repeater->add_control( + 'hosted_url', + [ + 'label' => esc_html__( 'Choose File', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'dynamic' => [ + 'active' => true, + 'categories' => [ + TagsModule::MEDIA_CATEGORY, + ], + ], + 'media_types' => [ 'video' ], + 'condition' => [ + 'type' => 'hosted', + 'is_external_url' => '', + ], + ] + ); + + $repeater->add_control( + 'external_url', + [ + 'label' => esc_html__( 'URL', 'elementor-pro' ), + 'type' => Controls_Manager::URL, + 'autocomplete' => false, + 'options' => false, + 'label_block' => true, + 'show_label' => false, + 'dynamic' => [ + 'active' => true, + 'categories' => [ + TagsModule::POST_META_CATEGORY, + TagsModule::URL_CATEGORY, + ], + ], + 'media_types' => [ 'video' ], + 'placeholder' => esc_html__( 'Enter your URL', 'elementor-pro' ), + 'condition' => [ + 'type' => 'hosted', + 'is_external_url' => 'yes', + ], + ] + ); + + $repeater->add_control( + 'title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'default' => esc_html__( 'Title', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Add Your Text Here', 'elementor-pro' ), + 'label_block' => true, + ] + ); + + $repeater->add_control( + 'section_html_tag', + [ + 'label' => esc_html__( 'Title HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + ], + 'default' => 'h3', + 'condition' => [ + 'type' => 'section', + ], + ] + ); + + $repeater->add_control( + 'video_html_tag', + [ + 'label' => esc_html__( 'Title HTML Tag', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'h1' => 'H1', + 'h2' => 'H2', + 'h3' => 'H3', + 'h4' => 'H4', + 'h5' => 'H5', + 'h6' => 'H6', + 'div' => 'div', + 'span' => 'span', + ], + 'default' => 'h4', + 'condition' => [ + 'type!' => 'section', + ], + ] + ); + + $repeater->add_control( + 'duration', + [ + 'label' => esc_html__( 'Duration', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => '1:05', + 'default' => '', + 'ai' => [ + 'active' => false, + ], + 'condition' => [ + 'type!' => 'section', + ], + ] + ); + + $repeater->add_control( + 'thumbnail', + [ + 'label' => esc_html__( 'Thumbnail', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'dynamic' => [ + 'active' => true, + ], + 'default' => [ + 'url' => Utils::get_placeholder_image_src(), + ], + 'condition' => [ + 'type!' => 'section', + ], + ] + ); + + $repeater->add_control( + 'inner_tab_is_content_visible', + [ + 'label' => esc_html__( 'Contents Tabs ', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'return_value' => 'show', + 'default' => '', + 'condition' => [ + 'type' => [ 'youtube', 'hosted', 'vimeo' ], + ], + ] + ); + + $repeater->start_controls_tabs( 'video_tabs' ); + + $repeater->start_controls_tab( + 'inner_tab_1', + [ + 'label' => esc_html__( 'Tab #1', 'elementor-pro' ), + 'condition' => [ + 'type' => [ 'youtube', 'hosted', 'vimeo' ], + 'inner_tab_is_content_visible' => 'show', + ], + ] + ); + + $repeater->add_control( + 'inner_tab_content_1', + [ + 'label' => '', + 'type' => Controls_Manager::WYSIWYG, + 'default' => '

    ' . esc_html__( 'Add some content for each one of your videos, like a description, transcript or external links.To add, remove or edit tab names, go to Tabs.', 'elementor-pro' ) . '

    ', + 'condition' => [ + 'type' => [ 'youtube', 'hosted', 'vimeo' ], + 'inner_tab_is_content_visible' => 'show', + ], + ] + ); + + $repeater->end_controls_tab(); + + $repeater->start_controls_tab( + 'inner_tab_2', + [ + 'label' => esc_html__( 'Tab #2', 'elementor-pro' ), + 'condition' => [ + 'type' => [ 'youtube', 'hosted', 'vimeo' ], + 'inner_tab_is_content_visible' => 'show', + ], + ] + ); + + $repeater->add_control( + 'inner_tab_content_2', + [ + 'label' => '', + 'type' => Controls_Manager::WYSIWYG, + 'condition' => [ + 'type' => [ 'youtube', 'hosted', 'vimeo' ], + 'inner_tab_is_content_visible' => 'show', + ], + ] + ); + + $repeater->end_controls_tab(); + + $repeater->end_controls_tabs(); + + $this->add_control( + 'tabs', + [ + 'label' => esc_html__( 'Playlist Items', 'elementor-pro' ), + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'default' => [ + [ + 'title' => esc_html__( 'Sample Video', 'elementor-pro' ), + 'youtube_url' => 'https://www.youtube.com/watch?v=XHOmBV4js_E', + 'duration' => '0:16', + 'thumbnail' => [ 'url' => 'https://img.youtube.com/vi/XHOmBV4js_E/maxresdefault.jpg' ], + ], + [ + 'title' => esc_html__( 'Sample Video', 'elementor-pro' ), + 'youtube_url' => 'https://www.youtube.com/watch?v=XHOmBV4js_E', + 'duration' => '0:16', + 'thumbnail' => [ 'url' => 'https://img.youtube.com/vi/XHOmBV4js_E/maxresdefault.jpg' ], + ], + [ + 'title' => esc_html__( 'Sample Video', 'elementor-pro' ), + 'youtube_url' => 'https://www.youtube.com/watch?v=XHOmBV4js_E', + 'duration' => '0:16', + 'thumbnail' => [ 'url' => 'https://img.youtube.com/vi/XHOmBV4js_E/maxresdefault.jpg' ], + ], + ], + 'frontend_available' => true, + 'title_field' => '{{{ title }}}', + 'separator' => 'before', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_inner_tab', + [ + 'label' => esc_html__( 'Tabs', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'inner_tab_title_1', + [ + 'label' => esc_html__( 'Tab 1 Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Tab #1', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'placeholder' => esc_html__( 'Name', 'elementor-pro' ), + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'inner_tab_title_2', + [ + 'label' => esc_html__( 'Tab 2 Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Tab #2', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'placeholder' => esc_html__( 'Name', 'elementor-pro' ), + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'inner_tab_is_content_collapsible', + [ + 'label' => esc_html__( 'Collapsible', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'return_value' => 'collapsible', + 'default' => '', + 'separator' => 'before', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'inner_tab_label_show_more', + [ + 'label' => esc_html__( 'Read More Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'placeholder' => esc_html__( 'Show More', 'elementor-pro' ), + 'default' => esc_html__( 'Show More', 'elementor-pro' ), + 'condition' => [ + 'inner_tab_is_content_collapsible' => 'collapsible', + ], + ] + ); + + $this->add_control( + 'inner_tab_label_show_less', + [ + 'label' => esc_html__( 'Read Less Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'placeholder' => esc_html__( 'Show Less', 'elementor-pro' ), + 'default' => esc_html__( 'Show Less', 'elementor-pro' ), + 'condition' => [ + 'inner_tab_is_content_collapsible' => 'collapsible', + ], + ] + ); + + $this->add_responsive_control( + 'inner_tab_collapsible_height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'default' => [ + 'size' => 54, + ], + 'range' => [ + 'px' => [ + 'min' => 50, + 'max' => 500, + ], + 'em' => [ + 'min' => 5, + 'max' => 50, + ], + 'rem' => [ + 'min' => 5, + 'max' => 50, + ], + ], + 'render_type' => 'template', + 'selectors' => [ + '{{WRAPPER}} .collapsible .e-inner-tab-text' => 'height: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'inner_tab_is_content_collapsible' => 'collapsible', + ], + 'frontend_available' => true, + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_image_overlay', + [ + 'label' => esc_html__( 'Image Overlay', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'show_image_overlay', + [ + 'label' => esc_html__( 'Image Overlay', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'image_overlay', + [ + 'label' => esc_html__( 'Choose Image', 'elementor-pro' ), + 'type' => Controls_Manager::MEDIA, + 'default' => [ + 'url' => Utils::get_placeholder_image_src(), + ], + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'show_image_overlay' => 'yes', + ], + ] + ); + + $this->add_group_control( + Group_Control_Image_Size::get_type(), + [ + 'name' => 'image_overlay', + 'default' => 'full', + 'condition' => [ + 'show_image_overlay' => 'yes', + ], + ] + ); + + $this->add_control( + 'show_play_icon', + [ + 'label' => esc_html__( 'Play Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'default' => [ + 'value' => 'far fa-play-circle', + 'library' => 'fa-regular', + ], + 'label_block' => false, + 'skin' => 'inline', + 'condition' => [ + 'show_image_overlay' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_additional_options', + [ + 'label' => esc_html__( 'Additional Options', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'heading_autoplay', + [ + 'label' => esc_html__( 'Autoplay', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'autoplay_on_load', + [ + 'label' => esc_html__( 'On Load', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'autoplay_next', + [ + 'label' => esc_html__( 'Next Up', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'show_watched_indication', + [ + 'label' => esc_html__( 'Indicate Watched', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'separator' => 'before', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'show_video_count', + [ + 'label' => esc_html__( 'Video Count', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + ] + ); + + $this->add_control( + 'show_duration', + [ + 'label' => esc_html__( 'Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'show_thumbnail', + [ + 'label' => esc_html__( 'Thumbnails', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'play_icon', + [ + 'label' => esc_html__( 'Play Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'default' => [ + 'value' => 'fas fa-play-circle', + 'library' => 'fa-solid', + ], + 'label_block' => false, + 'skin' => 'inline', + ] + ); + + $this->add_control( + 'watched_icon', + [ + 'label' => esc_html__( 'Watched Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon', + 'default' => [ + 'value' => 'fas fa-check-circle', + 'library' => 'fa-solid', + ], + 'label_block' => false, + 'skin' => 'inline', + ] + ); + + $this->add_control( + 'lazy_load', + [ + 'label' => esc_html__( 'Lazy Load', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'separator' => 'before', + 'frontend_available' => true, + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'tabs_alignment', + [ + 'label' => esc_html__( 'Video Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'default' => 'end', + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => "eicon-h-align-$start", + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => "eicon-h-align-$end", + ], + ], + 'prefix_class' => 'elementor-layout-', + ] + ); + + $this->add_responsive_control( + 'layout_height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 200, + 'max' => 1200, + ], + 'em' => [ + 'min' => 20, + 'max' => 120, + ], + 'rem' => [ + 'min' => 20, + 'max' => 120, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .e-tabs .e-tabs-main-area' => 'height: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_top_bar', + [ + 'label' => esc_html__( 'Top Bar', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'heading_playlist_name', + [ + 'label' => esc_html__( 'Playlist Name', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'playlist_name_background', + [ + 'label' => esc_html__( 'Background', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-header' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'playlist_name_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .e-tabs-header .e-tabs-title' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'playlist_name_typography', + 'selector' => '{{WRAPPER}} .e-tabs-header .e-tabs-title', + ] + ); + + $this->add_control( + 'heading_videos_amount', + [ + 'label' => esc_html__( 'Video Count', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'videos_amount_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .e-tabs-header .e-tabs-videos-count' => 'color: {{VALUE}};', + '{{WRAPPER}} .e-tabs-header .e-tabs-header-right-side i' => 'color: {{VALUE}};', + '{{WRAPPER}} .e-tabs-header .e-tabs-header-right-side svg' => 'fill: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'videos_amount_typography', + 'selector' => '{{WRAPPER}} .e-tabs-header .e-tabs-videos-count', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_videos', + [ + 'label' => esc_html__( 'Videos', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->start_controls_tabs( 'playlist_tabs' ); + + $this->start_controls_tab( + 'playlist_tabs_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'heading_tab_normal', + [ + 'label' => esc_html__( 'Item', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'normal_background', + [ + 'label' => esc_html__( 'Background', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tab-title' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .e-tabs-items-wrapper' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .e-tabs-items-wrapper .shadow-bottom' => 'background: linear-gradient(180deg, transparent 0%, {{VALUE}} 100%)', + '{{WRAPPER}} .e-tabs-items-wrapper .shadow-top' => 'background: linear-gradient(0deg, transparent 0%, {{VALUE}} 100%);', + ], + ] + ); + + $this->add_control( + 'normal_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tab-title .e-tab-title-text' => 'color: {{VALUE}};', + '{{WRAPPER}} .e-tab-title .e-tab-title-text a' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'normal_typography', + 'selector' => '{{WRAPPER}} .e-tab-title .e-tab-title-text', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_control( + 'heading_duration_normal', + [ + 'label' => esc_html__( 'Duration', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'normal_duration_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tab-title .e-tab-duration' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'normal_duration_typography', + 'selector' => '{{WRAPPER}} .e-tab-title .e-tab-duration', + ] + ); + + $this->add_control( + 'heading_icon_normal', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'normal_icon_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tab-title i' => 'color: {{VALUE}};', + '{{WRAPPER}} .e-tab-title svg' => 'fill: {{VALUE}};', + '{{WRAPPER}} .e-tab-title svg path' => 'fill: {{VALUE}};', + ], + ] + ); + + // Default shadow values for the icon. + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'normal_icon_top_text_shadow', + 'fields_options' => [ + 'text_shadow_type' => [ + 'label' => _x( 'Shadow', 'Text Shadow Control', 'elementor-pro' ), + ], + 'text_shadow' => [ + 'selectors' => [ + '{{WRAPPER}} .e-tab-title i' => 'text-shadow: {{HORIZONTAL}}px {{VERTICAL}}px {{BLUR}}px {{COLOR}};', + '{{WRAPPER}} .e-tab-title svg' => 'filter: drop-shadow({{HORIZONTAL}}px {{VERTICAL}}px {{BLUR}}px {{COLOR}});', + ], + ], + ], + ] + ); + + $this->add_responsive_control( + 'normal_icon_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 10, + 'max' => 30, + ], + 'em' => [ + 'min' => 1, + 'max' => 3, + ], + 'rem' => [ + 'min' => 1, + 'max' => 3, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--playlist-item-icon-size: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'heading_separator_normal', + [ + 'label' => esc_html__( 'Separator', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'normal_separator_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'solid' => _x( 'Solid', 'Border Control', 'elementor-pro' ), + 'double' => _x( 'Double', 'Border Control', 'elementor-pro' ), + 'dotted' => _x( 'Dotted', 'Border Control', 'elementor-pro' ), + 'dashed' => _x( 'Dashed', 'Border Control', 'elementor-pro' ), + 'groove' => _x( 'Groove', 'Border Control', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}} .e-tab-title' => 'border-style: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'normal_separator_weight', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 10, + ], + 'em' => [ + 'max' => 1, + ], + 'rem' => [ + 'max' => 1, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .e-tab-title' => 'border-width: 0 0 {{SIZE}}{{UNIT}} 0;', + ], + 'condition' => [ + 'normal_separator_style!' => '', + ], + ] + ); + + $this->add_control( + 'normal_separator_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tab-title' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + 'normal_separator_style!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'playlist_tabs_active', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'heading_tab_active', + [ + 'label' => esc_html__( 'Item', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'active_background', + [ + 'label' => esc_html__( 'Background', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-tabs-items .e-tab-title:where( .e-active, :hover )' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'active_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#556068', + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-tab-title:where( .e-active, :hover ) .e-tab-title-text' => 'color: {{VALUE}};', + '{{WRAPPER}} .e-tabs-items-wrapper .e-tab-title:where( .e-active, :hover ) .e-tab-title-text a' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'active_typography', + 'selector' => '{{WRAPPER}} .e-tabs-items-wrapper .e-tab-title:where( .e-active, :hover ) .e-tab-title-text', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_control( + 'heading_duration_active', + [ + 'label' => esc_html__( 'Duration', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'active_duration_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-tab-title:where( .e-active, :hover ) .e-tab-duration' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'active_duration_typography', + 'selector' => '{{WRAPPER}} .e-tabs-items-wrapper .e-tab-title:where( .e-active, :hover ) .e-tab-duration', + ] + ); + + $this->add_control( + 'heading_icon_active', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'active_icon_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-tab-title:where( .e-active, :hover ) i' => 'color: {{VALUE}};', + '{{WRAPPER}} .e-tabs-items-wrapper .e-tab-title:where( .e-active, :hover ) svg' => 'color: {{VALUE}};', + '{{WRAPPER}} .e-tabs-items-wrapper .e-tab-title:where( .e-active, :hover ) svg path' => 'fill: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'active_icon_top_text_shadow', + 'fields_options' => [ + 'text_shadow_type' => [ + 'label' => _x( 'Shadow', 'Text Shadow Control', 'elementor-pro' ), + ], + ], + 'selector' => '{{WRAPPER}} .e-tab-title:where( .e-active, :hover ) i, {{WRAPPER}} .e-tab-title:where( .e-active, :hover ) svg', + ] + ); + + $this->add_responsive_control( + 'active_icon_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 10, + 'max' => 30, + ], + 'em' => [ + 'min' => 1, + 'max' => 3, + ], + 'rem' => [ + 'min' => 1, + 'max' => 3, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .e-tab-title:where( .e-active, :hover ) span i' => 'font-size: {{SIZE}}{{UNIT}}', + '{{WRAPPER}} .e-tab-title:where( .e-active, :hover ) span svg' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'heading_separator_active', + [ + 'label' => esc_html__( 'Separator', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'active_separator_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'solid' => _x( 'Solid', 'Border Control', 'elementor-pro' ), + 'double' => _x( 'Double', 'Border Control', 'elementor-pro' ), + 'dotted' => _x( 'Dotted', 'Border Control', 'elementor-pro' ), + 'dashed' => _x( 'Dashed', 'Border Control', 'elementor-pro' ), + 'groove' => _x( 'Groove', 'Border Control', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-tab-title.e-active' => 'border-style: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'active_separator_weight', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 10, + ], + 'em' => [ + 'max' => 1, + ], + 'rem' => [ + 'max' => 1, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-tab-title.e-active' => 'border-width: 0 0 {{SIZE}}{{UNIT}} 0;', + ], + 'condition' => [ + 'active_separator_style!' => '', + ], + ] + ); + + $this->add_control( + 'active_separator_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-tabs-items .e-tab-title.e-active' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + 'active_separator_style!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_sections', + [ + 'label' => esc_html__( 'Sections', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'heading_section', + [ + 'label' => esc_html__( 'Section', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'section_background', + [ + 'label' => esc_html__( 'Background', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-section-title' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'section_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-section-title' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'section_typography', + 'selector' => '{{WRAPPER}} .e-tabs-items-wrapper .e-section-title', + ] + ); + + $this->add_control( + 'section_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'solid', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'solid' => _x( 'Solid', 'Border Control', 'elementor-pro' ), + 'double' => _x( 'Double', 'Border Control', 'elementor-pro' ), + 'dotted' => _x( 'Dotted', 'Border Control', 'elementor-pro' ), + 'dashed' => _x( 'Dashed', 'Border Control', 'elementor-pro' ), + 'groove' => _x( 'Groove', 'Border Control', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-section-title' => 'border-style: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'section_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-section-title' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'section_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-items-wrapper .e-section-title' => 'border-color: {{VALUE}};', + ], + ] + ); + $this->end_controls_section(); + + $this->start_controls_section( + 'section_inner_tab_style', + [ + 'label' => esc_html__( 'Tabs', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'inner_tab_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-wrapper' => '--inner-tabs-border-height: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-wrapper .e-inner-tab-title.e-inner-tab-active' => 'border-width: 0 0 {{SIZE}}{{UNIT}} 0;', + ], + ] + ); + + $this->add_control( + 'inner_tab_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-wrapper' => '--inner-tabs-border-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'inner_tab_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-wrapper .e-inner-tab-active' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-content-wrapper .e-inner-tab-content' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-content-wrapper .e-inner-tab-title' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'heading_inner_tab_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'inner_tab_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-wrapper .e-inner-tab-title a' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'inner_tab_active_title_color', + [ + 'label' => esc_html__( 'Active Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-wrapper .e-inner-tab-title.e-inner-tab-active a' => 'color: {{VALUE}};', + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-wrapper .e-inner-tab-title.e-inner-tab-active' => 'border-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'inner_tab_title_typography', + 'selector' => '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-wrapper .e-inner-tab-title a', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_control( + 'heading_inner_tab_content', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'inner_tab_content_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-content-wrapper .e-inner-tab-content .e-inner-tab-text' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'inner_tab_content_typography', + 'selector' => '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-content-wrapper .e-inner-tab-content .e-inner-tab-text', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + ] + ); + + $this->add_responsive_control( + 'inner_tab_content_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-content-wrapper .e-inner-tab-content' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'heading_inner_tab_show_more', + [ + 'label' => esc_html__( 'Show More', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->start_controls_tabs( 'inner_tab_show_more_color' ); + + $this->start_controls_tab( + 'inner_tab_normal_show_more', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'inner_tab_normal_show_more_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-content-wrapper .e-inner-tab-content button' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'inner_tab_hover_show_more', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'inner_tab_hover_show_more_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-content-wrapper .e-inner-tab-content button:hover' => 'color: {{VALUE}};', + ], + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + ] + ); + + $this->add_control( + 'inner_tab_hover_show_more_color_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 's', 'ms', 'custom' ], + 'default' => [ + 'unit' => 'ms', + ], + 'selectors' => [ + '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-content-wrapper .e-inner-tab-content button' => 'transition-duration: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'inner_tab_show_more_typography', + 'selector' => '{{WRAPPER}} .e-tabs-inner-tabs .e-inner-tabs-content-wrapper .e-inner-tab-content button', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + ] + ); + + $this->end_controls_section(); + } + + private function create_playlist_item_tabs_array( $playlist_item, $playlist_item_index, $tab_1_title, $tab_2_title ) { + $playlist_item_tabs_array = []; + $is_already_activated_tab = false; + + $playlist_item_tabs_content = [ + $playlist_item['inner_tab_content_1'], + $playlist_item['inner_tab_content_2'], + ]; + + $playlist_item_tabs_title = [ + $tab_1_title, + $tab_2_title, + ]; + + foreach ( $playlist_item_tabs_content as $index => $playlist_item_tab_content ) { + $playlist_item_tab_content_object = new \stdClass(); + $playlist_item_tab_content_object->tab_title = $playlist_item_tabs_title[ $index ]; + $playlist_item_tab_content_object->tab_content = $playlist_item_tab_content; + + $tab_index = $index + 1; + $playlist_item_tab_content_object->tab_content_setting_key = $this->get_repeater_setting_key( 'inner_tab_content_' . $tab_index, 'tabs', $playlist_item_index ); + + if ( $playlist_item_tab_content ) { + $playlist_item_tab_content_object->tab_attribute = ! $is_already_activated_tab ? '' : 'hidden'; + $playlist_item_tab_content_object->tab_class = ! $is_already_activated_tab ? 'e-inner-tab-active' : ''; + $is_already_activated_tab = true; + } + + $playlist_item_tabs_array [] = $playlist_item_tab_content_object; + } + + return $playlist_item_tabs_array; + } + + private function create_playlist_item_video_attributes( $index, $id_int, $playlist_item, $video_url = '' ) { + $html_attributes_object = new \stdClass(); + $tab_count = $index + 1; + + // The first tab will be activated and not hidden like the second tab. + $hidden = 1 === $tab_count ? false : 'hidden'; + + $html_attributes_object->attributes = [ + 'id' => 'e-tab-content-' . $id_int . $tab_count, + 'class' => [ 'e-tab-content', 'elementor-clearfix' ], + 'data-tab' => $tab_count, + 'role' => 'tabpanel', + 'aria-labelledby' => 'e-tab-title-' . $id_int . $tab_count, + 'tabindex' => '0', + 'data-video-url' => $video_url, + 'data-video-type' => $playlist_item['type'], + 'data-video-title' => $playlist_item['title'], + 'data-video-duration' => $playlist_item['duration'], + ]; + + if ( $hidden ) { + $html_attributes_object->attributes['hidden'] = $hidden; + } + + return $html_attributes_object; + } + + private function create_playlist_item_title_attributes( $index, $id_int ) { + $html_attributes_object = new \stdClass(); + $tab_count = $index + 1; + + $html_attributes_object->attributes = [ + 'id' => 'e-tab-title-' . $id_int . $tab_count, + 'class' => [ 'e-tab-title', 'e-tab-desktop-title' ], + 'aria-selected' => 1 === $tab_count ? 'true' : 'false', + 'data-tab' => $tab_count, + 'role' => 'tab', + 'tabindex' => 1 === $tab_count ? '0' : '-1', + 'aria-controls' => 'e-tab-content-' . $id_int . $tab_count, + ]; + + return $html_attributes_object; + } + + private function create_playlist_items_array( $playlist_items ) { + $playlist_items_array = array(); + $id_int = substr( $this->get_id_int(), 0, 3 ); + + foreach ( $playlist_items as $index => $playlist_item ) { + $playlist_item_object = new \stdClass(); + $playlist_item_object->video_url = ''; + $playlist_item_object->show_overlay_image = false; + $playlist_item_object->is_inner_tabs_visible = $playlist_item['inner_tab_is_content_visible']; + + switch ( $playlist_item['type'] ) { + case 'youtube': + case 'vimeo': + case 'hosted': + $playlist_item_object->type = $playlist_item['type']; + $playlist_item_object->video_title = $playlist_item['title']; + $playlist_item_object->video_html_tag = Utils::validate_html_tag( $playlist_item['video_html_tag'] ); + + if ( $playlist_item['youtube_url'] && 'youtube' === $playlist_item['type'] ) { + $playlist_item_object->video_url = $playlist_item['youtube_url']; + } elseif ( $playlist_item['vimeo_url'] && 'vimeo' === $playlist_item['type'] ) { + $playlist_item_object->video_url = $playlist_item['vimeo_url']; + } elseif ( $playlist_item['external_url'] && 'hosted' === $playlist_item['type'] && 'yes' === $playlist_item['is_external_url'] ) { + $playlist_item_object->video_url = $playlist_item['external_url']['url']; + } elseif ( $playlist_item['hosted_url'] && 'hosted' === $playlist_item['type'] && 'yes' !== $playlist_item['is_external_url'] ) { + $playlist_item_object->video_url = $playlist_item['hosted_url']['url']; + } + + $playlist_item_object->tab_collapsible = $this->get_settings_for_display( 'inner_tab_is_content_collapsible' ); + $playlist_item_object->read_more_label = $this->get_settings_for_display( 'inner_tab_label_show_more' ); + $playlist_item_object->read_less_label = $this->get_settings_for_display( 'inner_tab_label_show_less' ); + + $playlist_item_object->video_duration = ''; + if ( $this->get_settings_for_display( 'show_duration' ) ) { + $playlist_item_object->video_duration = $playlist_item['duration']; + } + + $playlist_item_object->video_thumbnail = ''; + if ( $this->get_settings_for_display( 'show_thumbnail' ) ) { + $playlist_item_object->video_thumbnail = $playlist_item['thumbnail']['url']; + } + + $playlist_item_object->html_attributes_title = $this->create_playlist_item_title_attributes( $index, $id_int ); + + $playlist_item_object->tabs = []; + if ( $playlist_item['inner_tab_content_1'] || $playlist_item['inner_tab_content_2'] ) { + $tab_1_title = $this->get_settings_for_display( 'inner_tab_title_1' ); + $tab_2_title = $this->get_settings_for_display( 'inner_tab_title_2' ); + if ( $playlist_item_object->is_inner_tabs_visible ) { + $playlist_item_object->tabs = $this->create_playlist_item_tabs_array( $playlist_item, $index, $tab_1_title, $tab_2_title ); + } + } + + if ( 0 === $index && 'yes' !== $this->get_settings_for_display( 'autoplay_on_load' ) && $this->get_settings_for_display( 'show_image_overlay' ) ) { + $playlist_item_object->show_overlay_image = true; + } + + break; + case 'section': + $playlist_item_object->type = $playlist_item['type']; + $playlist_item_object->section_title = $playlist_item['title']; + $playlist_item_object->section_html_tag = Utils::validate_html_tag( $playlist_item['section_html_tag'] ); + $playlist_item_object->video_title = ''; + break; + } + + $playlist_item_object->html_attributes_video = $this->create_playlist_item_video_attributes( $index, $id_int, $playlist_item, $playlist_item_object->video_url ); + + $playlist_items_array [] = $playlist_item_object; + } + + return $playlist_items_array; + } + + private function count_video_items( $playlist_items ) { + $filtered_playlist_items = $playlist_items; + foreach ( $filtered_playlist_items as $key => $tab ) { + if ( 'section' === $tab['type'] ) { + unset( $filtered_playlist_items[ $key ] ); + } + } + return count( $filtered_playlist_items ); + } + + private function prepare_video_playlist_data_object() { + $settings = $this->get_settings_for_display(); + $playlist_items = $settings['tabs']; + + $playlist_object = new \stdClass(); + $playlist_object->playlist_name = $settings['playlist_title']; + $playlist_object->playlist_title_tag = Utils::validate_html_tag( $settings['playlist_title_tag'] ); + $playlist_object->is_show_video_count = $settings['show_video_count']; + + if ( $playlist_object->is_show_video_count ) { + $playlist_object->video_count = $this->count_video_items( $playlist_items ); + } + + $playlist_object->show_thumbnails = $settings['show_thumbnail']; + $playlist_object->play_icon = $settings['play_icon']; + $playlist_object->watched_icon = $settings['watched_icon']; + $playlist_object->playlist_items = $this->create_playlist_items_array( $playlist_items ); + $playlist_object->is_image_overlay = $settings['show_image_overlay']; + $playlist_object->image_overlay_icon = $settings['show_play_icon']; + + $image_overlay = $settings['image_overlay']; + if ( ! empty( $image_overlay['url'] ) ) { + $playlist_object->image_overlay_image = Group_Control_Image_Size::get_attachment_image_src( $image_overlay['id'], 'image_overlay', $settings ); + if ( ! $playlist_object->image_overlay_image[1] ) { + $playlist_object->image_overlay_image = $image_overlay['url']; + } + } + + return $playlist_object; + } + + protected function render() { + $playlist_object = $this->prepare_video_playlist_data_object(); + ?> + +
    +
    +
    +
    + <playlist_title_tag ); ?> class="e-tabs-title">playlist_name; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?>playlist_title_tag ); ?>> +
    + is_show_video_count ) : ?> + video_count ); ?> + + 'eicons', + 'value' => 'eicon-caret-down', + ], + [ + 'aria-hidden' => 'true', + 'class' => [ + 'e-tabs-toggle-videos-display-button', + 'rotate-down', + ], + ] + ); + ?> +
    +
    +
    +
    + playlist_items as $item ) : ?> + type ) : ?> + <section_html_tag ); ?> class="e-section-title">section_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?>section_html_tag ); ?>> + +
    html_attributes_title->attributes ); ?>> + show_thumbnails ) : ?> +
    + video_thumbnail ) : ?> + <?php echo esc_attr( $item->video_title ); ?> + + play_icon, [ 'aria-hidden' => 'true' ] ); ?> + watched_icon, [ 'aria-hidden' => 'true' ] ); ?> +
    + + play_icon, [ 'aria-hidden' => 'true' ] ); ?> + watched_icon, [ 'aria-hidden' => 'true' ] ); ?> + + <video_html_tag ); ?> class="e-tab-title-text"> + video_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> + video_html_tag ); ?>> + video_duration ) : ?> + video_duration ); ?> + +
    + + +
    + + +
    +
    + +
    + playlist_items as $item ) : ?> +
    html_attributes_video->attributes ); ?>> +
    + show_overlay_image ) : ?> +
    + image_overlay_icon['value'] ) ) : ?> +
    + image_overlay_icon, [ 'aria-hidden' => 'true' ] ); ?> + +
    + +
    + +
    + +
    +
    + +
    + playlist_items as $item ) : ?> + is_inner_tabs_visible ) : ?> +
    html_attributes_video->attributes ); ?>> + tabs ) > 0 ) : ?> +
    + tabs as $tab ) : + if ( $tab->tab_content ) { ?> + + + +
    +
    + tabs as $tab ) : + if ( $tab->tab_content ) { ?> +
    + + tab_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> +
    +
    tab_attribute ); ?> class="e-inner-tab-content tab_class ); ?> tab_collapsible ); ?>"> +
    + add_inline_editing_attributes( $tab->tab_content_setting_key, 'advanced' ); ?> +
    print_render_attribute_string( $tab->tab_content_setting_key ); ?>> + + parse_text_editor( $tab->tab_content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> +
    +
    +
    + + + +
    +
    + + +
    + +
    + + +
    +
    + + <# + + function createPlaylistItemTabsArray( playlistItem, playlistItemIndex, tab1Title, tab2Title ) { + var playlistItemTabsArray = []; + var isAlreadyActivatedTab = false; + + var playlistItemTabsContent = [ + playlistItem.inner_tab_content_1, + playlistItem.inner_tab_content_2, + ]; + + var playlistItemTabsTitle = [ + tab1Title, + tab2Title, + ]; + + playlistItemTabsContent.forEach( function( playlistItemTabContent, index ) { + var playlistItemTabContentObject = {}; + playlistItemTabContentObject.tab_title = playlistItemTabsTitle[ index ]; + playlistItemTabContentObject.tab_content = playlistItemTabContent; + + var tabIndex = index + 1; + playlistItemTabContentObject.tabContentSettingKey = view.getRepeaterSettingKey( 'inner_tab_content_' + tabIndex, 'tabs', playlistItemIndex ); + + if ( playlistItemTabContent ) { + playlistItemTabContentObject.tabAttribute = ! isAlreadyActivatedTab ? '' : 'hidden'; + playlistItemTabContentObject.tabClass = ! isAlreadyActivatedTab ? 'e-inner-tab-active' : ''; + isAlreadyActivatedTab = true; + } + + playlistItemTabsArray.push(playlistItemTabContentObject); + } ); + + return playlistItemTabsArray; + } + + function createPlaylistItemVideoAttributes( index, id, playlistItem, videoUrl ) { + var videoUrl = videoUrl ? videoUrl : ''; + var htmlAttributesObject = {}; + var tabCount = index + 1; + + // The first tab will be activated and not hidden like the second tab. + var hidden = 1 === tabCount ? false : 'hidden'; + + htmlAttributesObject.attributes = { + 'id' : 'e-tab-content-' + id + tabCount, + 'class' : [ 'e-tab-content', 'elementor-clearfix' ], + 'data-tab' : tabCount, + 'role' : 'tabpanel', + 'aria-labelledby' : 'e-tab-title-' + id + tabCount, + 'tabindex' : '0', + 'data-video-url' : videoUrl, + 'data-video-type' : playlistItem.type, + 'data-video-title' : playlistItem.title, + 'data-video-duration' : playlistItem.duration, + }; + + if ( hidden ) { + htmlAttributesObject.attributes.hidden = hidden; + } + + return htmlAttributesObject; + } + + function createPlaylistItemTitleAttributes( index, id ) { + var htmlAttributesObject = {}; + var tabCount = index + 1; + + htmlAttributesObject.attributes = { + 'id' : 'e-tab-title-' + id + tabCount, + 'class' : [ 'e-tab-title', 'e-tab-desktop-title' ], + 'aria-selected' : 1 === tabCount ? 'true' : 'false', + 'data-tab' : tabCount, + 'role' : 'tab', + 'tabindex' : 1 === tabCount ? '0' : '-1', + 'aria-controls' : 'e-tab-content-' + id + tabCount, + }; + + return htmlAttributesObject; + } + + function createPlaylistItemsArray( playlistItems ) { + var playlistItemsArray = []; + id = view.getIDInt().toString().substr( 0, 3 ) + + playlistItems.forEach( function( playlistItem, index ) { + var playlistItemObject = {}; + playlistItemObject.videoUrl = ''; + playlistItemObject.showOverlayImage = false; + playlistItemObject.isInnerTabsVisible = playlistItem.inner_tab_is_content_visible; + + switch ( playlistItem.type ) { + case 'youtube': + case 'vimeo': + case 'hosted': + playlistItemObject.type = playlistItem.type; + playlistItemObject.videoTitle = playlistItem.title; + playlistItemObject.videoHtmlTag = elementor.helpers.validateHTMLTag( playlistItem.video_html_tag ); + + if ( playlistItem.youtube_url && 'youtube' === playlistItem.type ) { + playlistItemObject.videoUrl = playlistItem.youtube_url; + } else if ( playlistItem.vimeo_url && 'vimeo' === playlistItem.type ) { + playlistItemObject.videoUrl = playlistItem.vimeo_url; + } else if ( playlistItem.external_url && 'hosted' === playlistItem.type && 'yes' === playlistItem.is_external_url ) { + playlistItemObject.videoUrl = playlistItem.external_url.url; + } else if ( playlistItem.hosted_url && 'hosted' === playlistItem.type && 'yes' !== playlistItem.is_external_url ) { + playlistItemObject.videoUrl = playlistItem.hosted_url.url; + } + + playlistItemObject.tabCollapsible = settings.inner_tab_is_content_collapsible; + playlistItemObject.readMoreLabel = settings.inner_tab_label_show_more; + playlistItemObject.readLessLabel = settings.inner_tab_label_show_less; + + playlistItemObject.videoDuration = ''; + if ( settings.show_duration ) { + playlistItemObject.videoDuration = playlistItem.duration; + } + + playlistItemObject.videoThumbnail = ''; + if ( settings.show_thumbnail ) { + playlistItemObject.videoThumbnail = playlistItem.thumbnail.url; + } + + playlistItemObject.htmlAttributesTitle = createPlaylistItemTitleAttributes( index, id ); + + playlistItemObject.tabs = []; + if ( playlistItem.inner_tab_content_1 || playlistItem.inner_tab_content_2 ) { + var tab1Title = settings.inner_tab_title_1; + var tab2Title = settings.inner_tab_title_2; + if ( playlistItemObject.isInnerTabsVisible ) { + playlistItemObject.tabs = createPlaylistItemTabsArray( playlistItem, index, tab1Title, tab2Title ); + } + } + + if ( (0 === index) && (settings.autoplay_on_load !== 'yes') && settings.show_image_overlay ) { + playlistItemObject.showOverlayImage = true; + } + + break; + case 'section': + playlistItemObject.type = playlistItem.type; + playlistItemObject.sectionTitle = playlistItem.title; + playlistItemObject.sectionHtmlTag = elementor.helpers.validateHTMLTag( playlistItem.section_html_tag ); + playlistItemObject.isInnerTabsVisible = false; + break; + } + + playlistItemObject.htmlAttributesVideo = createPlaylistItemVideoAttributes( index, id, playlistItem, playlistItemObject.videoUrl ); + + playlistItemsArray.push(playlistItemObject); + } ); + + return playlistItemsArray; + } + + function countVideoItems( playlistItems ) { + var filteredPlaylistItems = playlistItems.filter(function(playlistItem) { + return 'section' !== playlistItem.type + }); + + return filteredPlaylistItems.length; + } + + function prepare_video_playlist_data_object() { + var playlistItems = settings.tabs + + playlistObject = {}; + playlistObject.playlistName = settings.playlist_title; + playlistObject.playlistNameHTMLTag = elementor.helpers.validateHTMLTag( settings.playlist_title_tag ); + playlistObject.isShowVideoCount = settings.show_video_count; + + if( playlistObject.isShowVideoCount ) { + playlistObject.videoCount = countVideoItems( playlistItems ); + } + + playlistObject.showThumbnails = settings.show_thumbnail; + playlistObject.playIcon = settings.play_icon; + playlistObject.watchedIcon = settings.watched_icon; + playlistObject.playlistItems = createPlaylistItemsArray( playlistItems ); + playlistObject.isImageOverlay = !settings.autoplay_on_load && settings.show_image_overlay ? true : false; + playlistObject.imageOverlayIcon = settings.show_play_icon; + + playlistObject.imageOverlayImage = ''; + imageOverlay = settings.image_overlay; + if ( imageOverlay.url && settings.image_overlay.id && playlistObject.isImageOverlay ) { + var image_overlay_object = { + id: settings.image_overlay.id, + url: settings.image_overlay.url, + size: settings.image_overlay_size, + dimension: settings.image_overlay_custom_dimension, + model: view.getEditModel() + }; + + playlistObject.imageOverlayImage = elementor.imagesManager.getImageUrl( image_overlay_object ); + } else { + playlistObject.imageOverlayImage = imageOverlay.url; + } + + return playlistObject; + } + + var playlistObject = prepare_video_playlist_data_object(); + + var watchedIconHTML = elementor.helpers.renderIcon( view, playlistObject.watchedIcon, { 'aria-hidden': true }, 'i' , 'object' ); + var playIconHTML = elementor.helpers.renderIcon( view, playlistObject.playIcon, { 'aria-hidden': true }, 'i' , 'object' ); + var overlayImagePlayIconHTML = elementor.helpers.renderIcon( view, playlistObject.imageOverlayIcon, { 'aria-hidden': true }, 'i' , 'object' ); + #> +
    +
    +
    +
    + <{{ playlistObject.playlistNameHTMLTag }} class="e-tabs-title"> + {{{ playlistObject.playlistName }}} + +
    + <# if ( playlistObject.isShowVideoCount ) { #> + {{{ playlistObject.videoCount }}} Videos + <# } #> + +
    +
    +
    +
    + <# _.each( playlistObject.playlistItems, function( item, index ) { #> + <# if ( 'section' === item.type ) { #> + <{{ item.sectionHtmlTag }} class="e-section-title"> + {{{ item.sectionTitle }}} + + <# } else { #> + <# + var tabTitleKey = item.htmlAttributesTitle.attributes.id; + view.addRenderAttribute(tabTitleKey, item.htmlAttributesTitle.attributes); + #> +
    + <# if ( playlistObject.showThumbnails ) { #> +
    + <# if ( item.videoThumbnail ) { #> + + <# } #> + {{{ playIconHTML.value }}} + {{{ watchedIconHTML.value }}} +
    + <# } else { #> + {{{ playIconHTML.value }}} + {{{ watchedIconHTML.value }}} + <# } #> + <{{ item.videoHtmlTag }} class="e-tab-title-text"> + {{{ item.videoTitle }}} + + <# if ( item.videoDuration ) { #> + {{{ item.videoDuration }}} + <# } #> +
    + <# } #> + <# } ); #> +
    + + +
    +
    + +
    + <# _.each( playlistObject.playlistItems, function( item, index ) { #> + + <# + var tabContentKey = view.getRepeaterSettingKey( 'tab_content', 'tabs', index ); + view.addRenderAttribute(tabContentKey, item.htmlAttributesVideo.attributes); + #> +
    +
    + <# if ( item.showOverlayImage ) { #> +
    + <# if ( playlistObject.imageOverlayIcon.value ) { #> +
    + {{{ overlayImagePlayIconHTML.value }}} +
    + <# } #> +
    + <# } #> +
    + <# } ); #> +
    +
    + +
    + <# _.each( playlistObject.playlistItems, function( item, index ) { #> + <# if ( item.isInnerTabsVisible ) { #> + <# + var tabContentKey = view.getRepeaterSettingKey( 'tab_content', 'inner-tabs', index ); + view.addRenderAttribute(tabContentKey, item.htmlAttributesVideo.attributes); + #> +
    + <# if ( item.tabs.length > 0 ) { #> +
    + <# _.each( item.tabs, function( tab ) { #> + <# if ( tab.tab_content ) { #> + + <# } #> + <# }); #> +
    +
    + <# _.each( item.tabs, function( tab ) { #> +
    + {{{ tab.tab_title }}} +
    +
    +
    + <# view.addInlineEditingAttributes( tab.tabContentSettingKey, 'advanced' ); #> +
    + {{{ tab.tab_content }}} +
    +
    +
    + + +
    +
    + <# } ); #> +
    + <# } #> +
    + <# } #> + <# } ); #> +
    +
    + get_query_results(); + + if ( empty( $result->total ) ) { + return ''; + } + + return parent::get_content(); + } +} diff --git a/modules/woocommerce/classes/current-query-renderer.php b/modules/woocommerce/classes/current-query-renderer.php new file mode 100644 index 0000000..10a85cc --- /dev/null +++ b/modules/woocommerce/classes/current-query-renderer.php @@ -0,0 +1,85 @@ +settings = $settings; + $this->type = $type; + $this->attributes = $this->parse_attributes( [ + 'paginate' => $settings['paginate'], + 'cache' => false, + ] ); + $this->query_args = $this->parse_query_args(); + } + + /** + * Override the original `get_query_results` + * with modifications that: + * 1. Remove `pre_get_posts` action if `is_added_product_filter`. + * + * @return bool|mixed|object + */ + protected function get_query_results() { + $query = $GLOBALS['wp_query']; + + $paginated = ! $query->get( 'no_found_rows' ); + + // Check is_object to indicate it's called the first time. + if ( ! empty( $query->posts ) && is_object( $query->posts[0] ) ) { + $query->posts = array_map( function ( $post ) { + return $post->ID; + }, $query->posts ); + } + + $results = (object) array( + 'ids' => wp_parse_id_list( $query->posts ), + 'total' => $paginated ? (int) $query->found_posts : count( $query->posts ), + 'total_pages' => $paginated ? (int) $query->max_num_pages : 1, + 'per_page' => (int) $query->get( 'posts_per_page' ), + 'current_page' => $paginated ? (int) max( 1, $query->get( 'paged', 1 ) ) : 1, + ); + + return $results; + } + + protected function parse_query_args() { + $settings = &$this->settings; + + if ( ! is_page( wc_get_page_id( 'shop' ) ) ) { + $query_args = $GLOBALS['wp_query']->query_vars; + } + + add_action( "woocommerce_shortcode_before_{$this->type}_loop", function () { + wc_set_loop_prop( 'is_shortcode', false ); + } ); + + if ( 'yes' === $settings['paginate'] ) { + $page = get_query_var( 'paged', 1 ); + + if ( 1 < $page ) { + $query_args['paged'] = $page; + } + + if ( 'yes' !== $settings['allow_order'] ) { + remove_action( 'woocommerce_before_shop_loop', 'woocommerce_catalog_ordering', 30 ); + } + + if ( 'yes' !== $settings['show_result_count'] ) { + remove_action( 'woocommerce_before_shop_loop', 'woocommerce_result_count', 20 ); + } + } + + // Always query only IDs. + $query_args['fields'] = 'ids'; + + return $query_args; + } + +} diff --git a/modules/woocommerce/classes/products-renderer.php b/modules/woocommerce/classes/products-renderer.php new file mode 100644 index 0000000..35aa0b9 --- /dev/null +++ b/modules/woocommerce/classes/products-renderer.php @@ -0,0 +1,351 @@ +settings_key_prefix = static::QUERY_CONTROL_NAME . '_'; + $this->settings = $settings; + $this->type = $type; + $this->attributes = $this->parse_attributes( [ + 'columns' => ! empty( $settings['columns'] ) ? $settings['columns'] : self::DEFAULT_COLUMNS_AND_ROWS, + 'rows' => ! empty( $settings['rows'] ) ? $settings['rows'] : self::DEFAULT_COLUMNS_AND_ROWS, + 'paginate' => $settings['paginate'], + 'cache' => false, + ] ); + $this->query_args = $this->parse_query_args(); + } + + /** + * Override the original `get_query_results` + * with modifications that: + * 1. Remove `pre_get_posts` action if `is_added_product_filter`. + * + * @return bool|mixed|object + */ + + protected function get_query_results() { + $results = parent::get_query_results(); + // Start edit. + if ( $this->is_added_product_filter ) { + remove_action( 'pre_get_posts', [ wc()->query, 'product_query' ] ); + } + // End edit. + + return $results; + } + + public function parse_query_args() { + $settings = &$this->settings; + + $query_args = [ + 'post_type' => 'product', + 'post_status' => 'publish', + 'ignore_sticky_posts' => true, + 'no_found_rows' => false === wc_string_to_bool( $this->attributes['paginate'] ), + 'orderby' => $settings[ $this->settings_key_prefix . 'orderby' ], + 'order' => strtoupper( $settings[ $this->settings_key_prefix . 'order' ] ), + ]; + + $query_args['meta_query'] = WC()->query->get_meta_query(); + $query_args['tax_query'] = []; + + if ( 'yes' === $settings['paginate'] && 'yes' === $settings['allow_order'] && ! is_front_page() ) { + $ordering_args = WC()->query->get_catalog_ordering_args(); + } else { + $ordering_args = WC()->query->get_catalog_ordering_args( $query_args['orderby'], $query_args['order'] ); + } + + if ( in_array( $this->settings[ $this->settings_key_prefix . 'post_type' ], [ 'related_products', 'upsells', 'cross_sells' ], true ) ) { + $query_args['post_type'] = [ 'product', 'product_variation' ]; + } + + $query_args['orderby'] = $ordering_args['orderby']; + $query_args['order'] = $ordering_args['order']; + + if ( $this->orderby_option_needs_meta_key( $query_args['orderby'] ) ) { + $ordering_args['meta_key'] = $this->get_orderby_meta_key( $query_args['orderby'] ); + $query_args['orderby'] = 'meta_value_num'; + } + + if ( $ordering_args['meta_key'] ) { + $query_args['meta_key'] = $ordering_args['meta_key']; + } + + // fallback to the widget's default settings in case settings was left empty: + $rows = $this->attributes['rows']; + $columns = $this->attributes['columns']; + $query_args['posts_per_page'] = $settings['posts_per_page'] ?? intval( $columns * $rows ); + + $this->set_visibility_query_args( $query_args ); + + $this->set_featured_query_args( $query_args ); + + $this->set_sale_products_query_args( $query_args ); + + $this->set_single_product_query_args( $query_args ); + + $this->set_ids_query_args( $query_args ); + + // Set specific types query args. + if ( method_exists( $this, "set_{$this->type}_query_args" ) ) { + $this->{"set_{$this->type}_query_args"}( $query_args ); + } + + $this->set_terms_query_args( $query_args ); + + $this->set_authors_query_args( $query_args ); + + $this->set_exclude_query_args( $query_args ); + + $this->set_pagination_args( $query_args ); + + $query_args = apply_filters( 'woocommerce_shortcode_products_query', $query_args, $this->attributes, $this->type ); + + // Always query only IDs. + $query_args['fields'] = 'ids'; + + return $query_args; + } + + protected function orderby_option_needs_meta_key( $orderby ) { + return in_array( $orderby, [ 'price', 'rating', 'popularity' ] ); + } + + protected function get_orderby_meta_key( $orderby ) { + switch ( $orderby ) { + case 'price': + return '_price'; + case 'rating': + return '_wc_average_rating'; + case 'popularity': + return 'total_sales'; + default: + return 'menu_order title'; + } + } + + protected function set_ids_query_args( &$query_args ) { + if ( 'by_id' !== $this->settings[ $this->settings_key_prefix . 'post_type' ] ) { + return; + } + + $this->set_by_id_query_args( $query_args ); + } + + protected function set_by_id_query_args( &$query_args ) { + $post__in = $this->settings[ $this->settings_key_prefix . 'posts_ids' ]; + + if ( empty( $post__in ) ) { + return; + } + + $this->set_post_in_query_args( $query_args, $post__in ); + } + + protected function set_post_in_query_args( &$query_args, $post__in ) { + $query_args['post__in'] = isset( $query_args['post__in'] ) ? array_merge( $post__in, $query_args['post__in'] ) : $post__in; + remove_action( 'pre_get_posts', [ wc()->query, 'product_query' ] ); + } + + private function set_terms_query_args( &$query_args ) { + if ( ! $this->is_include_query_type( 'terms' ) ) { + return; + } + + $tax_query = []; + + if ( ! empty( $this->settings[ $this->settings_key_prefix . 'include_term_ids' ] ) ) { + $terms = []; + foreach ( $this->settings[ $this->settings_key_prefix . 'include_term_ids' ] as $id ) { + $term_data = get_term_by( 'term_taxonomy_id', $id ); + $taxonomy = $term_data->taxonomy; + $terms[ $taxonomy ][] = $id; + } + foreach ( $terms as $taxonomy => $ids ) { + $query = [ + 'taxonomy' => $taxonomy, + 'field' => 'term_taxonomy_id', + 'terms' => $ids, + ]; + + $tax_query[] = $query; + } + } + + if ( ! empty( $tax_query ) ) { + $query_args['tax_query'] = array_merge( $query_args['tax_query'], $tax_query ); + } + } + + private function set_authors_query_args( &$query_args ) { + if ( ! $this->is_include_query_type( 'authors' ) ) { + return; + } + + if ( ! empty( $this->settings[ $this->settings_key_prefix . 'include_authors' ] ) ) { + $query_args['author__in'] = $this->settings[ $this->settings_key_prefix . 'include_authors' ]; + } + } + + protected function set_featured_query_args( &$query_args ) { + if ( 'featured' !== $this->settings[ $this->settings_key_prefix . 'post_type' ] ) { + return; + } + + $product_visibility_term_ids = wc_get_product_visibility_term_ids(); + + $query_args['tax_query'][] = [ + 'taxonomy' => 'product_visibility', + 'field' => 'term_taxonomy_id', + 'terms' => [ $product_visibility_term_ids['featured'] ], + ]; + } + + protected function set_sale_products_query_args( &$query_args ) { + if ( 'sale' !== $this->settings[ $this->settings_key_prefix . 'post_type' ] ) { + return; + } + + parent::set_sale_products_query_args( $query_args ); + } + + protected function set_single_product_query_args( &$query_args ) { + if ( ! in_array( $this->settings[ $this->settings_key_prefix . 'post_type' ], [ 'related_products', 'upsells', 'cross_sells' ], true ) ) { + return; + } + + global $product; + + $this->set_post_in_query_args( $query_args, [ 0 ] ); + + switch ( $this->settings[ $this->settings_key_prefix . 'post_type' ] ) { + case 'related_products': + if ( ! $product ) { + return; + } + + $products = array_filter( array_map( 'wc_get_product', wc_get_related_products( $product->get_id(), $query_args['posts_per_page'], $product->get_upsell_ids() ) ), 'wc_products_array_filter_visible' ); + break; + case 'upsells': + if ( ! $product ) { + return; + } + + $products = array_filter( array_map( 'wc_get_product', $product->get_upsell_ids() ), 'wc_products_array_filter_visible' ); + break; + case 'cross_sells': + if ( is_checkout() ) { + return; + } + + $products = array_filter( array_map( 'wc_get_product', WC()->cart->get_cross_sells() ), 'wc_products_array_filter_visible' ); + break; + } + + if ( empty( $products ) ) { + return; + } + + $post__in = array_map( function ( $product ) { + return $product->get_id(); + }, $products ); + + $this->set_post_in_query_args( $query_args, $post__in ); + } + + protected function set_exclude_query_args( &$query_args ) { + if ( empty( $this->settings[ $this->settings_key_prefix . 'exclude' ] ) ) { + return; + } + $post__not_in = []; + if ( in_array( 'current_post', $this->settings[ $this->settings_key_prefix . 'exclude' ], true ) ) { + if ( is_singular() ) { + $post__not_in[] = get_queried_object_id(); + } + } + + if ( in_array( 'manual_selection', $this->settings[ $this->settings_key_prefix . 'exclude' ] ) && ! empty( $this->settings[ $this->settings_key_prefix . 'exclude_ids' ] ) ) { + $post__not_in = array_merge( $post__not_in, $this->settings[ $this->settings_key_prefix . 'exclude_ids' ] ); + } + + $query_args['post__not_in'] = empty( $query_args['post__not_in'] ) ? $post__not_in : array_merge( $query_args['post__not_in'], $post__not_in ); + + /** + * WC populates `post__in` with the ids of the products that are on sale. + * Since WP_Query ignores `post__not_in` once `post__in` exists, the ids are filtered manually, using `array_diff`. + */ + if ( in_array( $this->settings[ $this->settings_key_prefix . 'post_type' ], [ 'sale', 'related_products', 'upsells', 'cross_sells' ] ) ) { + $query_args['post__in'] = array_diff( $query_args['post__in'], $query_args['post__not_in'] ); + } + + if ( in_array( 'terms', $this->settings[ $this->settings_key_prefix . 'exclude' ] ) && ! empty( $this->settings[ $this->settings_key_prefix . 'exclude_term_ids' ] ) ) { + $terms = []; + foreach ( $this->settings[ $this->settings_key_prefix . 'exclude_term_ids' ] as $to_exclude ) { + $term_data = get_term_by( 'term_taxonomy_id', $to_exclude ); + $terms[ $term_data->taxonomy ][] = $to_exclude; + } + $tax_query = []; + foreach ( $terms as $taxonomy => $ids ) { + $tax_query[] = [ + 'taxonomy' => $taxonomy, + 'field' => 'term_id', + 'terms' => $ids, + 'operator' => 'NOT IN', + ]; + } + if ( empty( $query_args['tax_query'] ) ) { + $query_args['tax_query'] = $tax_query; + } else { + $query_args['tax_query']['relation'] = 'AND'; + $query_args['tax_query'][] = $tax_query; + } + } + } + + protected function set_pagination_args( &$query_args ) { + if ( 'yes' !== $this->settings['paginate'] ) { + return; + } + + $this->set_paged_args( $query_args ); + + if ( 'yes' !== $this->settings['allow_order'] || is_front_page() ) { + remove_action( 'woocommerce_before_shop_loop', 'woocommerce_catalog_ordering', 30 ); + } + + if ( 'yes' !== $this->settings['show_result_count'] ) { + remove_action( 'woocommerce_before_shop_loop', 'woocommerce_result_count', 20 ); + } + } + + protected function set_paged_args( &$query_args ) { + $page = max( 1, get_query_var( 'paged' ), get_query_var( 'page' ) ); + $page = absint( empty( $_GET['product-page'] ) ? $page : $_GET['product-page'] ); + + if ( 1 === $page ) { + return; + } + + $query_args['paged'] = $page; + } + + private function is_include_query_type( $type ) { + return ( + ! in_array( $this->settings[ $this->settings_key_prefix . 'post_type' ], [ 'by_id', 'current_query' ] ) + && ! empty( $this->settings[ $this->settings_key_prefix . 'include' ] ) + && in_array( $type, $this->settings[ $this->settings_key_prefix . 'include' ], true ) + ); + } +} diff --git a/modules/woocommerce/conditions/product-archive.php b/modules/woocommerce/conditions/product-archive.php new file mode 100644 index 0000000..81d8e2c --- /dev/null +++ b/modules/woocommerce/conditions/product-archive.php @@ -0,0 +1,61 @@ +post_type, 'objects' ); + $this->post_taxonomies = wp_filter_object_list( $taxonomies, [ + 'public' => true, + 'show_in_nav_menus' => true, + ] ); + + parent::__construct( $data ); + } + + public static function get_type() { + return 'archive'; + } + + public function get_name() { + return 'product_archive'; + } + + public static function get_priority() { + return 40; + } + + public function get_label() { + return esc_html__( 'Product Archive', 'elementor-pro' ); + } + + public function get_all_label() { + return esc_html__( 'All Product Archives', 'elementor-pro' ); + } + + public function register_sub_conditions() { + $this->register_sub_condition( new Shop_page() ); + $this->register_sub_condition( new Product_Search() ); + + foreach ( $this->post_taxonomies as $slug => $object ) { + $condition = new ThemeBuilder\Conditions\Taxonomy( [ + 'object' => $object, + ] ); + $this->register_sub_condition( $condition ); + } + } + + public function check( $args ) { + return is_shop() || is_product_taxonomy() || Module::is_product_search(); + } +} diff --git a/modules/woocommerce/conditions/product-search.php b/modules/woocommerce/conditions/product-search.php new file mode 100644 index 0000000..45a8b46 --- /dev/null +++ b/modules/woocommerce/conditions/product-search.php @@ -0,0 +1,32 @@ + 'product', + ] ); + + $this->register_sub_condition( $product_archive ); + $this->register_sub_condition( $product_single ); + } + + public function check( $args ) { + return is_woocommerce() || Module::is_product_search(); + } +} diff --git a/modules/woocommerce/data/controller.php b/modules/woocommerce/data/controller.php new file mode 100644 index 0000000..f141936 --- /dev/null +++ b/modules/woocommerce/data/controller.php @@ -0,0 +1,20 @@ +register_endpoint( Endpoints\Set_Notice_Dismissed::class ); + $this->register_endpoint( Endpoints\Get_Notice_Dismissed::class ); + } +} diff --git a/modules/woocommerce/data/endpoints/base-endpoint.php b/modules/woocommerce/data/endpoints/base-endpoint.php new file mode 100644 index 0000000..53c4221 --- /dev/null +++ b/modules/woocommerce/data/endpoints/base-endpoint.php @@ -0,0 +1,23 @@ +get_name(); + } + + protected function permission_callback(): bool { + return current_user_can( 'edit_posts' ); + } +} diff --git a/modules/woocommerce/data/endpoints/get-notice-dismissed.php b/modules/woocommerce/data/endpoints/get-notice-dismissed.php new file mode 100644 index 0000000..096a332 --- /dev/null +++ b/modules/woocommerce/data/endpoints/get-notice-dismissed.php @@ -0,0 +1,35 @@ +get_params(); + $post_id = $data['post_id']; + $is_dismissed_database_key = self::IS_CART_NOTICE_DISMISSED; + + return [ + self::IS_CART_NOTICE_DISMISSED => get_post_meta( $post_id, $is_dismissed_database_key, true ), + ]; + } + + protected function register() { + register_rest_route( $this->controller->get_namespace(), $this->get_route() . '/(?P\d+)', [ + [ + 'methods' => \WP_REST_Server::READABLE, + 'callback' => [ $this, 'handle_post_meta_request' ], + 'permission_callback' => function () { + return $this->permission_callback(); + }, + ], + ] ); + } +} diff --git a/modules/woocommerce/data/endpoints/set-notice-dismissed.php b/modules/woocommerce/data/endpoints/set-notice-dismissed.php new file mode 100644 index 0000000..1dc72c8 --- /dev/null +++ b/modules/woocommerce/data/endpoints/set-notice-dismissed.php @@ -0,0 +1,49 @@ +get_params(); + $post_id = $data['post_id']; + $is_cart_dismissed = $data[ self::IS_CART_NOTICE_DISMISSED ]; + $is_cart_dismissed_database_value = true === $is_cart_dismissed ? 'true' : 'false'; + $is_dismissed_database_key = self::IS_CART_NOTICE_DISMISSED; + + update_post_meta( $post_id, $is_dismissed_database_key, $is_cart_dismissed_database_value ); + + return [ + self::IS_CART_NOTICE_DISMISSED => $is_cart_dismissed, + ]; + } + + protected function register() { + register_rest_route( $this->controller->get_namespace(), $this->get_route(), [ + [ + 'args' => [ + 'post_id' => [ + 'type' => 'integer', + 'required' => true, + ], + self::IS_CART_NOTICE_DISMISSED => [ + 'type' => 'boolean', + 'required' => true, + ], + ], + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'handle_post_meta_request' ], + 'permission_callback' => function () { + return $this->permission_callback(); + }, + ], + ] ); + } +} diff --git a/modules/woocommerce/documents/product-archive.php b/modules/woocommerce/documents/product-archive.php new file mode 100644 index 0000000..5352f0b --- /dev/null +++ b/modules/woocommerce/documents/product-archive.php @@ -0,0 +1,170 @@ + esc_html__( 'What is a Products Archive Template?', 'elementor-pro' ), + 'content' => esc_html__( 'A products archive template allows you to easily design the layout and style of your WooCommerce shop page or other product archive pages - those pages that show a list of products, which may be filtered by terms such as categories, tags, etc.', 'elementor-pro' ), + 'tip' => esc_html__( 'You can create multiple products archive templates, and assign each to different categories of products. This gives you the freedom to customize the appearance for each type of product being shown.', 'elementor-pro' ), + 'docs' => 'https://go.elementor.com/app-theme-builder-products-archive', + 'video_url' => 'https://www.youtube.com/embed/cQLeirgkguA', + ]; + } + + public function enqueue_scripts() { + // In preview mode it's not a real Woocommerce page - enqueue manually. + if ( Plugin::elementor()->preview->is_preview_mode( $this->get_main_id() ) ) { + wp_enqueue_script( 'woocommerce' ); + } + } + + public function get_container_attributes() { + $attributes = parent::get_container_attributes(); + + $attributes['class'] .= ' product'; + + return $attributes; + } + + public function filter_body_classes( $body_classes ) { + $body_classes = parent::filter_body_classes( $body_classes ); + + if ( get_the_ID() === $this->get_main_id() || Plugin::elementor()->preview->is_preview_mode( $this->get_main_id() ) ) { + $body_classes[] = 'woocommerce'; + } + + return $body_classes; + } + + public static function get_preview_as_default() { + return 'post_type_archive/product'; + } + + public static function get_preview_as_options() { + $post_type_archives = []; + $taxonomies = []; + $post_type = 'product'; + + $post_type_object = get_post_type_object( $post_type ); + + $post_type_archives[ 'post_type_archive/' . $post_type ] = $post_type_object->label . ' ' . esc_html__( 'Archive', 'elementor-pro' ); + + $post_type_taxonomies = get_object_taxonomies( $post_type, 'objects' ); + + $post_type_taxonomies = wp_filter_object_list( $post_type_taxonomies, [ + 'public' => true, + 'show_in_nav_menus' => true, + ] ); + + foreach ( $post_type_taxonomies as $slug => $object ) { + $taxonomies[ 'taxonomy/' . $slug ] = $object->label . ' ' . esc_html__( 'Archive', 'elementor-pro' ); + } + + $options = [ + 'search' => esc_html__( 'Search Results', 'elementor-pro' ), + ]; + + $options += $taxonomies + $post_type_archives; + + return [ + 'archive' => [ + 'label' => esc_html__( 'Archive', 'elementor-pro' ), + 'options' => $options, + ], + ]; + } + + public function __construct( array $data = [] ) { + parent::__construct( $data ); + + add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ], 11 ); + } + + protected static function get_editor_panel_categories() { + $categories = [ + 'woocommerce-elements-archive' => [ + 'title' => esc_html__( 'Product Archive', 'elementor-pro' ), + ], + // Move to top as active. + 'woocommerce-elements' => [ + 'title' => esc_html__( 'WooCommerce', 'elementor-pro' ), + 'active' => true, + ], + ]; + + $categories += parent::get_editor_panel_categories(); + + unset( $categories['theme-elements-archive'] ); + + return $categories; + } + + public static function get_editor_panel_config() { + $config = parent::get_editor_panel_config(); + $config['widgets_settings']['theme-archive-title']['categories'][] = 'woocommerce-elements-archive'; + + return $config; + } + + protected function register_controls() { + parent::register_controls(); + + $this->update_control( + 'preview_type', + [ + 'default' => 'post_type_archive/product', + ] + ); + } + + protected function get_remote_library_config() { + $config = parent::get_remote_library_config(); + + $config['category'] = 'product archive'; + + return $config; + } +} diff --git a/modules/woocommerce/documents/product-post.php b/modules/woocommerce/documents/product-post.php new file mode 100644 index 0000000..d6a3033 --- /dev/null +++ b/modules/woocommerce/documents/product-post.php @@ -0,0 +1,77 @@ + static::get_type(), + ] ); + } + + protected static function get_editor_panel_categories() { + $categories = parent::get_editor_panel_categories(); + + unset( $categories['theme-elements-single'] ); + + $categories = Utils::array_inject( + $categories, + 'theme-elements', + [ + 'woocommerce-elements-single' => [ + 'title' => esc_html__( 'Product', 'elementor-pro' ), + 'active' => false, + ], + ] + ); + + return $categories; + } + + protected function get_remote_library_config() { + $config = parent::get_remote_library_config(); + + $config['category'] = 'single product'; + + return $config; + } +} diff --git a/modules/woocommerce/documents/product.php b/modules/woocommerce/documents/product.php new file mode 100644 index 0000000..bd3995a --- /dev/null +++ b/modules/woocommerce/documents/product.php @@ -0,0 +1,195 @@ + esc_html__( 'What is a Single Product Template?', 'elementor-pro' ), + 'content' => esc_html__( 'A single product template allows you to easily design the layout and style of WooCommerce single product pages, and apply that template to various conditions that you assign.', 'elementor-pro' ), + 'tip' => esc_html__( 'You can create multiple single product templates, and assign each to different types of products, enabling a custom design for each group of similar products.', 'elementor-pro' ), + 'docs' => 'https://go.elementor.com/app-theme-builder-product', + 'video_url' => 'https://www.youtube.com/embed/PjhoB1RWkBM', + ]; + } + + public static function get_editor_panel_config() { + $config = parent::get_editor_panel_config(); + $config['widgets_settings']['woocommerce-product-content'] = [ + 'show_in_panel' => true, + ]; + + return $config; + } + + public function enqueue_scripts() { + // In preview mode it's not a real Product page - enqueue manually. + if ( Plugin::elementor()->preview->is_preview_mode( $this->get_main_id() ) ) { + global $product; + + if ( is_singular( 'product' ) ) { + $product = wc_get_product(); + } + + if ( current_theme_supports( 'wc-product-gallery-zoom' ) ) { + wp_enqueue_script( 'zoom' ); + } + if ( current_theme_supports( 'wc-product-gallery-slider' ) ) { + wp_enqueue_script( 'flexslider' ); + } + if ( current_theme_supports( 'wc-product-gallery-lightbox' ) ) { + wp_enqueue_script( 'photoswipe-ui-default' ); + wp_enqueue_style( 'photoswipe-default-skin' ); + add_action( 'wp_footer', 'woocommerce_photoswipe' ); + } + wp_enqueue_script( 'wc-single-product' ); + + wp_enqueue_style( 'photoswipe' ); + wp_enqueue_style( 'photoswipe-default-skin' ); + wp_enqueue_style( 'photoswipe-default-skin' ); + wp_enqueue_style( 'woocommerce_prettyPhoto_css' ); + } + } + + public function get_depended_widget() { + return Plugin::elementor()->widgets_manager->get_widget_types( 'woocommerce-product-data-tabs' ); + } + + public function get_container_attributes() { + $attributes = parent::get_container_attributes(); + + $attributes['class'] .= ' product'; + + return $attributes; + } + + public function filter_body_classes( $body_classes ) { + $body_classes = parent::filter_body_classes( $body_classes ); + + if ( get_the_ID() === $this->get_main_id() || Plugin::elementor()->preview->is_preview_mode( $this->get_main_id() ) ) { + $body_classes[] = 'woocommerce'; + } + + return $body_classes; + } + + public function before_get_content() { + parent::before_get_content(); + + global $product; + if ( ! is_object( $product ) ) { + $product = wc_get_product( get_the_ID() ); + } + + do_action( 'woocommerce_before_single_product' ); + } + + public function after_get_content() { + parent::after_get_content(); + + do_action( 'woocommerce_after_single_product' ); + } + + public function print_content() { + if ( post_password_required() ) { + // PHPCS - It's a safe WP template function + echo get_the_password_form(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + return; + } + + parent::print_content(); + } + + public function __construct( array $data = [] ) { + parent::__construct( $data ); + + add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ], 11 ); + } + + protected static function get_editor_panel_categories() { + $categories = [ + 'woocommerce-elements-single' => [ + 'title' => esc_html__( 'Product', 'elementor-pro' ), + + ], + // Move to top as active. + 'woocommerce-elements' => [ + 'title' => esc_html__( 'WooCommerce', 'elementor-pro' ), + 'active' => true, + ], + ]; + + $categories += parent::get_editor_panel_categories(); + + unset( $categories['theme-elements-single'] ); + + return $categories; + } + + protected function register_controls() { + parent::register_controls(); + + $this->update_control( + 'preview_type', + [ + 'type' => Controls_Manager::HIDDEN, + 'default' => 'single/product', + ] + ); + + $latest_posts = get_posts( [ + 'posts_per_page' => 1, + 'post_type' => 'product', + ] ); + + if ( ! empty( $latest_posts ) ) { + $this->update_control( + 'preview_id', + [ + 'default' => $latest_posts[0]->ID, + ] + ); + } + } + + protected function get_remote_library_config() { + $config = parent::get_remote_library_config(); + + $config['category'] = 'single product'; + + return $config; + } +} diff --git a/modules/woocommerce/module.php b/modules/woocommerce/module.php new file mode 100644 index 0000000..dd2e3d6 --- /dev/null +++ b/modules/woocommerce/module.php @@ -0,0 +1,1648 @@ + 'Products', + 'wc-products' => 'Products_Deprecated', + 'woocommerce-product-add-to-cart' => 'Product_Add_To_Cart', + 'wc-elements' => 'Elements', + 'wc-categories' => 'Categories', + 'woocommerce-product-price' => 'Product_Price', + 'woocommerce-product-title' => 'Product_Title', + 'woocommerce-product-images' => 'Product_Images', + 'woocommerce-product-upsell' => 'Product_Upsell', + 'woocommerce-product-short-description' => 'Product_Short_Description', + 'woocommerce-product-meta' => 'Product_Meta', + 'woocommerce-product-stock' => 'Product_Stock', + 'woocommerce-product-rating' => 'Product_Rating', + 'woocommerce-product-data-tabs' => 'Product_Data_Tabs', + 'woocommerce-product-related' => 'Product_Related', + 'woocommerce-breadcrumb' => 'Breadcrumb', + 'wc-add-to-cart' => 'Add_To_Cart', + 'wc-archive-products' => 'Archive_Products', + 'woocommerce-archive-products' => 'Archive_Products_Deprecated', + 'woocommerce-product-additional-information' => 'Product_Additional_Information', + 'woocommerce-menu-cart' => 'Menu_Cart', + 'woocommerce-product-content' => 'Product_Content', + 'woocommerce-archive-description' => 'Archive_Description', + 'woocommerce-checkout-page' => 'Checkout', + 'woocommerce-cart' => 'Cart', + 'woocommerce-my-account' => 'My_Account', + 'woocommerce-purchase-summary' => 'Purchase_Summary', + 'woocommerce-notices' => 'Notices', + 'wc-single-elements' => 'Single_Elements', + ]; + + protected $docs_types = []; + protected $use_mini_cart_template; + protected $woocommerce_notices_elements = []; + + public static function is_active() { + return class_exists( 'woocommerce' ); + } + + public static function is_product_search() { + return is_search() && 'product' === get_query_var( 'post_type' ); + } + + /** + * @param $settings + * @param string $icon + * @return void + */ + public static function render_menu_icon( $settings, string $icon ) { + if ( ! empty( $settings['icon'] ) && 'custom' === $settings['icon'] ) { + self::render_custom_menu_icon( $settings ); + } else { + Icons_Manager::render_icon( [ + 'library' => 'eicons', + 'value' => 'eicon-' . $icon, + ] ); + } + } + + /** + * @param $settings + * @return void + */ + private static function render_custom_menu_icon( $settings ) { + if ( empty( $settings['menu_icon_svg'] ) ) { + echo ''; // Default Custom icon. + } else { + Icons_Manager::render_icon( $settings['menu_icon_svg'], [ + 'class' => 'e-toggle-cart-custom-icon', + 'aria-hidden' => 'true', + ] ); + } + } + + public function get_name() { + return 'woocommerce'; + } + + public function get_widgets() { + return API::filter_active_features( static::WIDGET_NAME_CLASS_NAME_MAP ); + } + + const RECOMMENDED_POSTS_WIDGET_NAMES = [ + 'theme-post-featured-image', + 'woocommerce-product-title', + 'woocommerce-product-add-to-cart', + 'woocommerce-product-price', + 'woocommerce-product-rating', + 'woocommerce-product-stock', + 'woocommerce-product-meta', + 'woocommerce-product-short-description', + 'woocommerce-product-content', + 'woocommerce-product-data-tabs', + 'woocommerce-product-additional-information', + ]; + + // 'WC page name' => 'Elementor widget name' + const WC_STATUS_PAGES_MAPPED_TO_WIDGETS = [ + 'Cart' => 'woocommerce-cart', + 'Checkout' => 'woocommerce-checkout-page', + 'My account' => 'woocommerce-my-account', + ]; + + public function add_product_post_class( $classes ) { + $classes[] = 'product'; + + return $classes; + } + + public function add_products_post_class_filter() { + add_filter( 'post_class', [ $this, 'add_product_post_class' ] ); + } + + public function remove_products_post_class_filter() { + remove_filter( 'post_class', [ $this, 'add_product_post_class' ] ); + } + + public function register_tags() { + if ( ! API::is_licence_has_feature( static::DYNAMIC_TAGS_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + return; + } + + $tags = [ + 'Product_Gallery', + 'Product_Image', + 'Product_Price', + 'Product_Rating', + 'Product_Sale', + 'Product_Content', + 'Product_Short_Description', + 'Product_SKU', + 'Product_Stock', + 'Product_Terms', + 'Product_Title', + 'Category_Image', + 'Woocommerce_Add_To_Cart', + ]; + + /** @var \Elementor\Core\DynamicTags\Manager $module */ + $module = Plugin::elementor()->dynamic_tags; + + $module->register_group( self::WOOCOMMERCE_GROUP, [ + 'title' => esc_html__( 'WooCommerce', 'elementor-pro' ), + ] ); + + foreach ( $tags as $tag ) { + $tag = 'ElementorPro\\Modules\\Woocommerce\\tags\\' . $tag; + + $module->register( new $tag() ); + } + } + + public function register_wc_hooks() { + wc()->frontend_includes(); + } + + /** + * @param Conditions_Manager $conditions_manager + */ + public function register_conditions( $conditions_manager ) { + $woocommerce_condition = new Woocommerce(); + + $conditions_manager->get_condition( 'general' )->register_sub_condition( $woocommerce_condition ); + } + + /** + * @param Documents_Manager $documents_manager + */ + public function register_documents( $documents_manager ) { + if ( API::is_licence_has_feature( static::SINGLE_PRODUCT_TEMPLATE_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->docs_types = [ + 'product-post' => Product_Post::get_class_full_name(), + ]; + + $this->docs_types['product'] = Product::get_class_full_name(); + } + + if ( API::is_licence_has_feature( static::ARCHIVE_PRODUCT_TEMPLATE_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->docs_types['product-archive'] = Product_Archive::get_class_full_name(); + } + + foreach ( $this->docs_types as $type => $class_name ) { + $documents_manager->register_document_type( $type, $class_name ); + } + } + + public static function render_menu_cart_toggle_button( $settings ) { + if ( null === WC()->cart ) { + return; + } + $product_count = WC()->cart->get_cart_contents_count(); + $sub_total = WC()->cart->get_cart_subtotal(); + $icon = ! empty( $settings['icon'] ) ? $settings['icon'] : 'cart-medium'; + ?> + + cart ) { + return; + } + + $widget_cart_is_hidden = apply_filters( 'woocommerce_widget_cart_is_hidden', false ); + $is_edit_mode = Plugin::elementor()->editor->is_edit_mode(); + ?> +
    + +
    + + +
    + +
    + +
    + 'e-close-cart-custom-icon', + 'aria-hidden' => 'true', + ] ); + } + ?> +
    + editor->set_edit_mode( true ); + } + + foreach ( $templates as $id ) { + $this->get_all_fragments( $id, $all_fragments ); + } + + wp_send_json( [ 'fragments' => $all_fragments ] ); + } + + /** + * Get All Fragments. + * + * @since 3.7.0 + * + * @param $id + * @param $all_fragments + * @return void + */ + public function get_all_fragments( $id, &$all_fragments ) { + $fragments_in_document = $this->get_fragments_in_document( $id ); + + if ( $fragments_in_document ) { + $all_fragments += $fragments_in_document; + } + } + + /** + * Get Fragments In Document. + * + * A general function that will return any needed fragments for a Post. + * + * @since 3.7.0 + * @access public + * + * @param int $id + * + * @return mixed $fragments + */ + public function get_fragments_in_document( $id ) { + $document = Plugin::elementor()->documents->get( $id ); + + if ( ! is_object( $document ) ) { + return false; + } + + $fragments = []; + + $data = $document->get_elements_data(); + + Plugin::elementor()->db->iterate_data( + $data, + $this->get_fragments_handler( $fragments ) + ); + + return ! empty( $fragments ) ? $fragments : false; + } + + /** + * Get Fragments Handler. + * + * @since 3.7.0 + * + * @param array $fragments + * @return void + */ + public function get_fragments_handler( array &$fragments ) { + return function ( $element ) use ( &$fragments ) { + if ( ! isset( $element['widgetType'] ) ) { + return; + } + + $fragment_data = $this->get_fragment_data( $element ); + $total_fragments = count( $fragment_data ) / 2; + + for ( $i = 0; $i < $total_fragments; $i++ ) { + $fragments[ $fragment_data['selector'][ $i ] ] = $fragment_data['html'][ $i ]; + } + }; + } + + /** + * Empty Cart Fragments + * + * When the Cart is emptied, the selected 'Empty Cart Template' needs to be added as an item + * in the WooCommerce `$fragments` array, so that WC will push the custom Template content into the DOM. + * This is done to prevent the need for a page refresh after the cart is cleared. + * + * @since 3.7.0 + * + * @param array $fragments + * @return array + */ + public function empty_cart_fragments( $fragments ) { + // Only do this when the cart is empty. + if ( WC()->cart->get_cart_contents_count() !== 0 ) { + return $fragments; + } + + $document = Plugin::elementor()->documents->get( url_to_postid( wp_get_referer() ) ); + + if ( is_object( $document ) ) { + $data = $document->get_elements_data(); + + Plugin::elementor()->db->iterate_data( $data, function( $element ) use ( &$fragments ) { + if ( + isset( $element['widgetType'] ) + && 'woocommerce-cart' === $element['widgetType'] + && ( isset( $element['settings']['additional_template_switch'] ) && 'active' === $element['settings']['additional_template_switch'] ) + && ( isset( $element['settings']['additional_template_select'] ) && 0 < $element['settings']['additional_template_select'] ) + ) { + $fragments[ 'div.elementor-element-' . $element['id'] . ' .elementor-widget-container' ] = '
    ' . do_shortcode( '[elementor-template id="' . $element['settings']['additional_template_select'] . '"]' ) . '
    '; + } + } ); + } + + return $fragments; + } + + public function maybe_init_cart() { + $has_cart = is_a( WC()->cart, 'WC_Cart' ); + + if ( ! $has_cart ) { + $session_class = apply_filters( 'woocommerce_session_handler', 'WC_Session_Handler' ); + WC()->session = new $session_class(); + WC()->session->init(); + WC()->cart = new \WC_Cart(); + WC()->customer = new \WC_Customer( get_current_user_id(), true ); + } + } + + public function localized_settings_frontend( $settings ) { + $has_cart = is_a( WC()->cart, 'WC_Cart' ); + + if ( $has_cart ) { + $settings['woocommerce']['menu_cart'] = [ + 'cart_page_url' => wc_get_cart_url(), + 'checkout_page_url' => wc_get_checkout_url(), + 'fragments_nonce' => wp_create_nonce( self::MENU_CART_FRAGMENTS_ACTION ), + ]; + } + return $settings; + } + + public function theme_template_include( $need_override_location, $location ) { + if ( is_product() && 'single' === $location ) { + $need_override_location = true; + } + + return $need_override_location; + } + + public function add_loop_recommended_widgets( $config, $post_id ) { + if ( ! $this->is_source_set_to_products( $post_id ) ) { + return $config; + } + + $config = $this->add_woocommerce_widgets_to_recommended( $config ); + return $this->hide_woocommerce_widgets_in_loop_document( $config ); + } + + /** + * Add plugin path to wc template search path. + * Based on: https://www.skyverge.com/blog/override-woocommerce-template-file-within-a-plugin/ + * @param $template + * @param $template_name + * @param $template_path + * + * @return string + */ + public function woocommerce_locate_template( $template, $template_name, $template_path ) { + + if ( self::TEMPLATE_MINI_CART !== $template_name ) { + return $template; + } + + if ( ! $this->use_mini_cart_template ) { + return $template; + } + + $plugin_path = plugin_dir_path( __DIR__ ) . 'woocommerce/wc-templates/'; + + if ( file_exists( $plugin_path . $template_name ) ) { + $template = $plugin_path . $template_name; + } + + return $template; + } + + /** + * WooCommerce/WordPress widget(s), some of the widgets have css classes that used by final selectors. + * before this filter, all those widgets were warped by `.elementor-widget-container` without chain original widget + * classes, now they will be warped by div with the original css classes. + * + * @param array $default_widget_args + * @param \Elementor\Widget_WordPress $widget + * + * @return array $default_widget_args + */ + public function woocommerce_wordpress_widget_css_class( $default_widget_args, $widget ) { + $widget_instance = $widget->get_widget_instance(); + + if ( ! empty( $widget_instance->widget_cssclass ) ) { + $default_widget_args['before_widget'] .= '
    '; + $default_widget_args['after_widget'] .= '
    '; + } + + return $default_widget_args; + } + + public function register_admin_fields( Settings $settings ) { + if ( ! API::is_licence_has_feature( static::MENU_CART_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + return; + } + + $settings->add_section( Settings::TAB_INTEGRATIONS, 'woocommerce', [ + 'callback' => function() { + echo '

    ' . esc_html__( 'WooCommerce', 'elementor-pro' ) . '

    '; + }, + 'fields' => [ + self::OPTION_NAME_USE_MINI_CART => [ + 'label' => esc_html__( 'Mini Cart Template', 'elementor-pro' ), + 'field_args' => [ + 'type' => 'select', + 'std' => 'initial', + 'options' => [ + 'initial' => '', // Relevant until either menu-cart widget is used or option is explicitly set to 'no'. + 'no' => esc_html__( 'Disable', 'elementor-pro' ), + 'yes' => esc_html__( 'Enable', 'elementor-pro' ), + ], + 'desc' => esc_html__( 'Set to `Disable` in order to use your Theme\'s or WooCommerce\'s mini-cart template instead of Elementor\'s.', 'elementor-pro' ), + ], + ], + ], + ] ); + } + + /** + * Load Widget Before WooCommerce Ajax. + * + * When outputting the complex WooCommerce shortcodes (which we use in our widgets) e.g. Checkout, Cart, etc. WC + * immediately does more ajax calls and retrieves updated html fragments based on the data in the forms that may + * be autofilled by the current user's browser e.g. the Payment section holding the "Place order" button. + * + * This function runs before these ajax calls. Using the `elementorPageId` and `elementorWidgetId` querystring + * appended to the forms `_wp_http_referer` url field, or the referer page ID, it loads the relevant Elementor widget. + * The rendered Elementor widget replaces the default WooCommerce template used to refresh WooCommerce elements in the page. + * + * This is necessary for example in the Checkout Payment section where we modify the Terms & Conditions text + * using settings from the widget or when updating shipping methods on the Cart. + * + * @since 3.5.0 + */ + public function load_widget_before_wc_ajax() { + // Make sure is a WooCommerce ajax call. + $wc_ajax = ProUtils::_unstable_get_super_global_value( $_GET, 'wc-ajax' ); + if ( ! $wc_ajax ) { + return; + } + + // Only handle relevant WC AJAX calls + if ( ! in_array( $wc_ajax, [ 'update_order_review', 'update_shipping_method' ], true ) ) { + return; + } + + // Security checks. + switch ( $wc_ajax ) { + case 'update_order_review': + check_ajax_referer( 'update-order-review', 'security' ); + break; + case 'update_shipping_method': + check_ajax_referer( 'update-shipping-method', 'security' ); + break; + } + + $page_id = false; + $widget_id = false; + + // Try to get the `$page_id` and `$widget_id` we added as a query string to `_wp_http_referer` in `post_data`. + // This is only available when a form is submitted. + $raw_post_data = ProUtils::_unstable_get_super_global_value( $_POST, 'post_data' ); + if ( $raw_post_data ) { + $raw_post_data = html_entity_decode( $raw_post_data ); + + parse_str( $raw_post_data, $post_data ); + + if ( isset( $post_data['_wp_http_referer'] ) ) { + $wp_http_referer = wp_unslash( $post_data['_wp_http_referer'] ); + + $wp_http_referer_query_string = wp_parse_url( $wp_http_referer, PHP_URL_QUERY ); + if ( ! empty( $wp_http_referer_query_string ) ) { + parse_str( $wp_http_referer_query_string, $wp_http_referer_query_string ); + + if ( isset( $wp_http_referer_query_string['elementorPageId'] ) ) { + $page_id = $wp_http_referer_query_string['elementorPageId']; + } + + if ( isset( $wp_http_referer_query_string['elementorWidgetId'] ) ) { + $widget_id = $wp_http_referer_query_string['elementorWidgetId']; + } + } + } + } + + if ( ! $page_id ) { + $page_id = url_to_postid( wp_get_referer() ); + } + + // Bail if no `$page_id`. + if ( ! $page_id ) { + return; + } + + // Get Elementor document from `$page_id`. + $document = Plugin::elementor()->documents->get_doc_for_frontend( $page_id ); + + // Bail if not Elementor page. + if ( ! $document ) { + return; + } + + // Setup $page_id as the WP global $post, so is available to our widgets. + $post = get_post( $page_id, OBJECT ); + setup_postdata( $post ); + + $widget_data = false; + if ( $widget_id ) { + // If we did manage to pass `$widget_id` to this ajax call we get the widget data by its ID. + $widget_data = Utils::find_element_recursive( $document->get_elements_data(), $widget_id ); + } else { + // If we didn't manage to pass `$widget_id` to this ajax call we use this alternate method and get the first + // of the type of widget used on the WC endpoint pages responsible for these ajax calls - cart or checkout widget. + $woocommerce_widgets = [ 'woocommerce-cart', 'woocommerce-checkout-page' ]; + + $document_data = $document->get_elements_data(); + Plugin::elementor()->db->iterate_data( $document_data, function( $element ) use ( $woocommerce_widgets, &$widget_data ) { + if ( $widget_data && ( ! isset( $element['widgetType'] ) || ! in_array( $element['widgetType'], $woocommerce_widgets, true ) ) ) { + return; + } + $widget_data = $element; + } ); + } + + // If we found a widget then run `add_render_hooks()` widget method. + if ( $widget_data ) { + $widget_instance = Plugin::elementor()->elements_manager->create_element_instance( $widget_data ); + if ( method_exists( $widget_instance, 'add_render_hooks' ) ) { + $widget_instance->add_render_hooks(); + } + } + } + + /** + * Elementor Woocommerce Checkout Login User + * + * Handle the Ajax call for the custom login form on the Checkout Widget + * + * @since 3.5.0 + */ + public function elementor_woocommerce_checkout_login_user() { + if ( is_user_logged_in() ) { + wp_logout(); + } + + $error = false; + $error_message = ''; + + if ( ! wp_verify_nonce( ProUtils::_unstable_get_super_global_value( $_POST, 'nonce' ), 'woocommerce-login' ) ) { + $error = true; + $error_message = sprintf( + /* translators: 1: Bold text opening tag, 2: Bold text closing tag. */ + esc_html__( '%1$sError:%2$s The nonce security check didn’t pass. Please reload the page and try again. You may want to try clearing your browser cache as a last attempt.', 'elementor-pro' ), + '', + '' + ); + } else { + $info = [ + 'user_login' => trim( ProUtils::_unstable_get_super_global_value( $_POST, 'username' ) ), + 'user_password' => $_POST['password'] ?? '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, A password should not be sanitized. + 'remember' => ProUtils::_unstable_get_super_global_value( $_POST, 'remember' ), + ]; + + $user_signon = wp_signon( $info, false ); + + if ( is_wp_error( $user_signon ) ) { + $error = true; + $error_message = $user_signon->get_error_message(); + } + } + + if ( $error ) { + wc_add_notice( + $error_message, + 'error' + ); + $response = [ + 'logged_in' => false, + 'message' => wc_print_notices( true ), + ]; + } else { + $response = [ 'logged_in' => true ]; + } + + echo wp_json_encode( $response ); + wp_die(); + } + + /** + * Register Ajax Actions. + * + * Registers ajax action used by the Editor js. + * + * @since 3.5.0 + * + * @param Ajax $ajax + */ + public function register_ajax_actions( Ajax $ajax ) { + // `woocommerce_update_page_option` is called in the editor save-show-modal.js. + $ajax->register_ajax_action( 'pro_woocommerce_update_page_option', [ $this, 'update_page_option' ] ); + $ajax->register_ajax_action( 'pro_woocommerce_mock_notices', [ $this, 'woocommerce_mock_notices' ] ); + } + + /** + * @throws \Exception + */ + public function woocommerce_mock_notices( $data ) { + $document = ProUtils::_unstable_get_document_for_edit( $data['editor_post_id'] ); + + if ( in_array( 'wc_error', $data['notice_elements'], true ) ) { + $notice_message = sprintf( + '%1$s %2$s', + esc_html__( 'This is how an error notice would look.', 'elementor-pro' ), + esc_html__( 'Here\'s a link', 'elementor-pro' ) + ); + wc_add_notice( $notice_message, 'error' ); + } + + if ( in_array( 'wc_message', $data['notice_elements'], true ) ) { + $notice_message = sprintf( + '%1$s %2$s %3$s', + esc_html__( 'Button', 'elementor-pro' ), + esc_html__( 'This is what a WooCommerce message notice looks like.', 'elementor-pro' ), + esc_html__( 'Here\'s a link', 'elementor-pro' ) + ); + wc_add_notice( $notice_message, 'success' ); + } + + if ( in_array( 'wc_info', $data['notice_elements'], true ) ) { + $notice_message = sprintf( + '%1$s %2$s', + esc_html__( 'Button', 'elementor-pro' ), + esc_html__( 'This is how WooCommerce provides an info notice.', 'elementor-pro' ) + ); + wc_add_notice( $notice_message, 'notice' ); + } + + return '
    ' . wc_print_notices( true ) . '
    '; + } + + /** + * Update Page Option. + * + * Ajax action can be used to update any WooCommerce option. + * + * @since 3.5.0 + * + * @param array $data + */ + public function update_page_option( $data ) { + $is_admin = current_user_can( 'manage_options' ); + $is_shop_manager = current_user_can( 'manage_woocommerce' ); + $is_allowed = $is_admin || $is_shop_manager; + + if ( ! $is_allowed ) { + return new \WP_Error( 401 ); + } + + $allowed_options = [ + 'woocommerce_checkout_page_id', + 'woocommerce_cart_page_id', + 'woocommerce_myaccount_page_id', + 'elementor_woocommerce_purchase_summary_page_id', + ]; + + $option_name = $data['option_name']; + $post_id = absint( $data['editor_post_id'] ); + + if ( ! in_array( $option_name, $allowed_options, true ) ) { + return new \WP_Error( 400 ); + } + + update_option( $option_name, $post_id ); + } + + public function init_site_settings( \Elementor\Core\Kits\Documents\Kit $kit ) { + $kit->register_tab( 'settings-woocommerce', \ElementorPro\Modules\Woocommerce\Settings\Settings_Woocommerce::class ); + } + + public function add_products_type_to_template_popup( $form ) { + $this->add_products_to_options( $form, '_elementor_source' ); + } + + public function add_products_type_to_loop_settings_query( $form ) { + $this->add_products_to_options( $form, 'source' ); + } + + public function add_products_taxonomy_type_to_template_popup( $form ) { + $this->add_taxonomies_to_options( $form, '_elementor_source' ); + } + + public function add_products_taxonomy_type_to_loop_settings_query( $form ) { + $this->add_taxonomies_to_options( $form, 'source' ); + } + + public function e_cart_count_fragments( $fragments ) { + $product_count = WC()->cart->get_cart_contents_count(); + + $fragments['.elementor-menu-cart__toggle_button span.elementor-button-text'] = '' . WC()->cart->get_cart_subtotal() . ''; + $fragments['.elementor-menu-cart__toggle_button span.elementor-button-icon-qty'] = '' . $product_count . ''; + + return $fragments; + } + + /** + * @param $form + * @param $control_name + * @return void + */ + protected function add_products_to_options( $form, $control_name ) { + if ( empty( $form ) ) { + return; + } + + $controls = $form->get_controls( $control_name ); + if ( ! $controls || ! isset( $controls['options'] ) ) { + return; + } + + $options = $controls['options']; + $options[ self::LOOP_PRODUCT_SKIN_ID ] = esc_html__( 'Products', 'elementor-pro' ); + + $form->update_control( $control_name, [ + 'options' => $options, + ] ); + } + + protected function add_taxonomies_to_options( $form, $control_name ) { + $controls = $form->get_controls( $control_name ); + + if ( ! $controls || ! isset( $controls['options'] ) ) { + return; + } + + $options = $controls['options']; + $options[ self::LOOP_PRODUCT_TAXONOMY_SKIN_ID ] = esc_html__( 'Product Taxonomy', 'elementor-pro' ); + + $form->update_control($control_name, [ + 'options' => $options, + ]); + } + + /** + * Add Update Kit Settings Hooks + * + * Add hooks that update the corresponding kit setting when the WooCommerce option is updated. + */ + public function add_update_kit_settings_hooks() { + add_action( 'update_option_woocommerce_cart_page_id', function( $old_value, $value ) { + Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_cart_page_id', $value ); + }, 10, 2 ); + + add_action( 'update_option_woocommerce_checkout_page_id', function( $old_value, $value ) { + Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_checkout_page_id', $value ); + }, 10, 2 ); + + add_action( 'update_option_woocommerce_myaccount_page_id', function( $old_value, $value ) { + Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_myaccount_page_id', $value ); + }, 10, 2 ); + + add_action( 'update_option_woocommerce_terms_page_id', function( $old_value, $value ) { + Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_terms_page_id', $value ); + }, 10, 2 ); + } + + /** + * Elementor WC My Account Logout + * + * Programatically log out if $_REQUEST['elementor_wc_logout'] is set. + * The $_REQUEST variables we have generated a custom logout URL for in the My Account menu. + * + * @since 3.5.0 + */ + public function elementor_wc_my_account_logout() { + $elementor_wc_logout = ProUtils::_unstable_get_super_global_value( $_REQUEST, 'elementor_wc_logout' ); + $nonce = ProUtils::_unstable_get_super_global_value( $_REQUEST, '_wpnonce' ); + + if ( $elementor_wc_logout && $nonce && wp_verify_nonce( $nonce, 'customer-logout' ) ) { + wp_logout(); // Log the user out Programatically. + wp_safe_redirect( esc_url( ProUtils::_unstable_get_super_global_value( $_REQUEST, 'elementor_my_account_redirect' ) ) ); // Redirect back to the widget page. + exit; + } + } + + /** + * Add Localize Data + * + * Makes `woocommercePages` available with the page name and the associated post ID for use with the various + * widgets site settings modal. + * + * @param $settings + * @return array + */ + public function add_localize_data( $settings ) { + $settings['woocommerce']['woocommercePages'] = [ + 'checkout' => wc_get_page_id( 'checkout' ), + 'cart' => wc_get_page_id( 'cart' ), + 'myaccount' => wc_get_page_id( 'myaccount' ), + 'purchase_summary' => get_option( 'elementor_woocommerce_purchase_summary_page_id' ), + ]; + + return $settings; + } + + /** + * Localize Added To Cart On Product Single + * + * WooCommerce doesn't trigger `added_to_cart` event on its products single page which is required for us to + * automatically open our Menu Cart if the settings is chosen. We make the `productAddedToCart` setting + * available that we can use in the Menu Cart js to check if a product has just been added. + * + * @since 3.5.0 + */ + public function localize_added_to_cart_on_product_single() { + add_filter( 'elementor_pro/frontend/localize_settings', function ( $settings ) { + $settings['woocommerce']['productAddedToCart'] = true; + return $settings; + } ); + } + + public function e_notices_body_classes( $classes ) { + if ( $this->should_load_wc_notices_styles() ) { + foreach ( $this->get_styled_notice_elements() as $notice_element ) { + $classes[] = 'e-' . str_replace( '_', '-', $notice_element ) . '-notice'; + } + } + + return $classes; + } + + public function get_styled_notice_elements() { + if ( empty( $this->woocommerce_notices_elements ) ) { + $kit = Plugin::elementor()->kits_manager->get_active_kit_for_frontend(); + $this->woocommerce_notices_elements = $kit->get_settings_for_display( 'woocommerce_notices_elements' ); + } + + return ! empty( $this->woocommerce_notices_elements ) ? $this->woocommerce_notices_elements : []; + } + + public function custom_gutenberg_woocommerce_notice() { + $min_suffix = Utils::is_script_debug() ? '' : '.min'; + + wp_enqueue_script( + 'elementor-gutenberg-woocommerce-notice', + ELEMENTOR_PRO_URL . '/assets/js/gutenberg-woocommerce-notice' . $min_suffix . '.js', + [ 'wp-blocks' ], + ELEMENTOR_PRO_VERSION, + false + ); + + wp_set_script_translations( 'elementor-gutenberg-woocommerce-notice', 'elementor-pro' ); + } + + public function e_notices_css() { + if ( ! $this->should_load_wc_notices_styles() ) { + return false; + } + + wp_enqueue_style( + 'e-woocommerce-notices', + ELEMENTOR_PRO_URL . 'assets/css/woocommerce-notices.min.css', + [], + ELEMENTOR_PRO_VERSION + ); + } + + public function get_order_received_endpoint_url( $url, $endpoint, $value ) { + $order_received_endpoint = get_option( 'woocommerce_checkout_order_received_endpoint', 'order-received' ); + + if ( $order_received_endpoint === $endpoint ) { + $woocommerce_purchase_summary_page_id = get_option( 'elementor_woocommerce_purchase_summary_page_id' ); + $order = wc_get_order( $value ); + + if ( $woocommerce_purchase_summary_page_id && $order ) { + $url = trailingslashit( trailingslashit( trailingslashit( get_permalink( $woocommerce_purchase_summary_page_id ) ) . $order_received_endpoint ) . $order->get_id() ); + } + } + + return $url; + } + + public function maybe_define_woocommerce_checkout() { + $woocommerce_purchase_summary_page_id = get_option( 'elementor_woocommerce_purchase_summary_page_id' ); + + if ( $woocommerce_purchase_summary_page_id && intval( $woocommerce_purchase_summary_page_id ) === get_queried_object_id() ) { + if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) { + define( 'WOOCOMMERCE_CHECKOUT', true ); + } + } + } + + /** + * Products Query Sources Fragments. + * + * Since we introduced additional query sources to the Products Widget, + * some of these query sources can now be used outside of the Single Product template. + * + * For example the Related Products and Cross-Sells. + * + * But now we'll need to make those sections also update when the Cart is updated. So + * we'll do this by creating fragments for each of these. + * + * @since 3.7.0 + * + * @param array $fragments + * + * @return array + */ + public function products_query_sources_fragments( $fragments ) { + if ( WC()->cart->get_cart_contents_count() !== 0 ) { + $document = Plugin::elementor()->documents->get( url_to_postid( wp_get_referer() ) ); + + if ( is_object( $document ) ) { + $data = $document->get_elements_data(); + + Plugin::elementor()->db->iterate_data( $data, function( $element ) use ( &$fragments ) { + if ( + isset( $element['widgetType'] ) + && 'woocommerce-products' === $element['widgetType'] + ) { + $settings = $element['settings']; + if ( isset( $settings[ Products_Renderer::QUERY_CONTROL_NAME . '_post_type' ] ) ) { + $query_type = $settings[ Products_Renderer::QUERY_CONTROL_NAME . '_post_type' ]; + $query_types_to_check = [ 'related_products', 'upsells', 'cross_sells' ]; + + if ( in_array( $query_type, $query_types_to_check, true ) ) { + switch ( $query_type ) { + case 'related_products': + $content = self::get_products_related_content( $settings ); + break; + case 'upsells': + $content = self::get_upsells_content( $settings ); + break; + case 'cross_sells': + $content = self::get_cross_sells_content( $settings ); + break; + default: + $content = null; + } + + if ( $content ) { + $fragments[ 'div.elementor-element-' . $element['id'] . ' div.elementor-widget-container' ] = '
    ' . $content . '
    '; + } + } + } + } + } ); + } + } else { + $fragments['div.elementor-widget-container .woocommerce .cross-sells'] = '
    '; + + $fragments['div.elementor-widget-container .woocommerce section.up-sells'] = '
    '; + } + + return $fragments; + } + + /** + * Get Products Related Content. + * + * A function to return content for the 'related' products query type in the Products widget. + * This function is declared in the Module file so it can be accessed during a WC fragment refresh + * and also be used in the Product widget's render method. + * + * @since 3.7.0 + * @access public + * + * @param array $settings + * + * @return mixed The content or false + */ + public static function get_products_related_content( $settings ) { + global $product; + + $product = wc_get_product(); + + if ( ! $product ) { + return; + } + + return self::get_product_widget_content( + $settings, + 'related_products', + 'woocommerce_product_related_products_heading', + 'products_related_title_text' + ); + } + + /** + * Get Upsells Content. + * + * A function to return content for the 'upsell' query type in the Products widget. + * This function is declared in the Module file so it can be accessed during a WC fragment refresh + * and also be used in the Product widget's render method. + * + * @since 3.7.0 + * @access public + * + * @param array $settings + * + * @return mixed The content or false + */ + public static function get_upsells_content( $settings ) { + return self::get_product_widget_content( + $settings, + 'upsells', + 'woocommerce_product_upsells_products_heading', + 'products_upsells_title_text' + ); + } + + /** + * Get Cross Sells Content. + * + * A function to return content for the 'cross_sells' query type in the Products widget. + * This function is declared in the Module file so it can be accessed during a WC fragment refresh + * and also be used in the Product widget's render method. + * + * @since 3.7.0 + * @access public + * + * @param array $settings + * + * @return mixed The content or false + */ + public static function get_cross_sells_content( $settings ) { + return self::get_product_widget_content( + $settings, + 'cross_sells', + 'woocommerce_product_cross_sells_products_heading', + 'products_cross_sells_title_text' + ); + } + + /** + * Print Woocommerce Shipping Message + * + * Format the shipping messages that will be displayed on the Cart and Checkout Widgets. + * This will add extra classes to those messages so that we can target certain messages + * with certain style controls. + * + * @since 3.5.0 + * + * @param string $html the original HTML from WC + * @param string $classes the classes we will surround $html with + * @return string the final formatted HTML that will be rendered + */ + private function print_woocommerce_shipping_message( $html, $classes ) { + return '' . $html . ''; + } + + /** + * Should load WC Notices Styles + * + * Determine if we should load the WooCommerce notices CSS. + * It should only load: + * - When we are in the Editor, regardless if any notices have been activated. + * - If WooCoomerce is active. + * - When we are on the front end, if at least one notice is activated. + * + * It should not load in WP Admin. + * + * @return boolean + */ + private function should_load_wc_notices_styles() { + if ( ! API::is_licence_has_feature( static::SITE_SETTINGS_NOTICES_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + return false; + } + + $woocommerce_active = in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ); + $is_editor = ProUtils::_unstable_get_super_global_value( $_GET, 'elementor-preview' ); + + // Editor checks. + if ( $woocommerce_active && $is_editor ) { + return true; + } + + // Front end checks. + if ( + 0 < count( $this->get_styled_notice_elements() ) // At least one notice has been activated. + && $woocommerce_active // WooCommerce is active. + && ( ! is_admin() || $is_editor ) // We are not in WP Admin. + ) { + return true; + } + + return false; + } + + /** + * Get Product Widget Content. + * + * A general function to create markup for the new query types in the Products widget. + * + * @since 3.7.0 + * @access private + * + * @param array $settings The widget settings. + * @param string $type The query type to create content for. + * @param string $title_hook The hook name to filter in the widget title. + * @param string $title_key The control ID for the section title. + * + * @return mixed The content or false + */ + private static function get_product_widget_content( $settings, $type, $title_hook, $title_key = '' ) { + add_filter( $title_hook, function ( $heading ) use ( $settings, $title_key ) { + $title_text = isset( $settings[ $title_key ] ) ? $settings[ $title_key ] : ''; + + if ( ! empty( $title_text ) ) { + return $title_text; + } + + return $heading; + }, 10, 1 ); + + ob_start(); + + $args = self::parse_product_widget_args( $settings, $type ); + + if ( 'related_products' === $type ) { + woocommerce_related_products( $args ); + } elseif ( 'upsells' === $type ) { + woocommerce_upsell_display( $args['limit'], $args['columns'], $args['orderby'], $args['order'] ); + } else { + /** + * We need to wrap this content in the 'woocommerce' class for the layout to have the correct styling. + * Because this will only be used as a separate widget on the Cart page, + * the normal 'woocommerce' div from the cart widget will be closed before this content. + */ + echo '
    '; + woocommerce_cross_sell_display( $args['limit'], $args['columns'], $args['orderby'], $args['order'] ); + echo '
    '; + } + + $products_html = ob_get_clean(); + + remove_filter( $title_hook, function() {}, 10 ); + + if ( $products_html ) { + $products_html = str_replace( '
      '-1', + 'columns' => ! empty( $settings['columns'] ) ? $settings['columns'] : 4, + 'orderby' => ! empty( $settings[ "{$query_name}_orderby" ] ) ? $settings[ "{$query_name}_orderby" ] : 'rand', + 'order' => ! empty( $settings[ "{$query_name}_order" ] ) ? $settings[ "{$query_name}_order" ] : 'desc', + ]; + + if ( ! empty( $settings['rows'] ) ) { + $args[ $limit_key ] = $args['columns'] * $settings['rows']; + } + + return $args; + } + + /** + * Get Fragment Data. + * + * A function that will return the selector and HTML for WC fragments. + * + * @since 3.7.0 + * @access private + * + * @param array $element + * + * @return array $fragment_data + */ + private function get_fragment_data( $element ) { + $fragment_data = []; + + if ( 'woocommerce-menu-cart' === $element['widgetType'] ) { + ob_start(); + self::render_menu_cart_toggle_button( $element['settings'] ); + $fragment_data['html'][] = ob_get_clean(); + + $fragment_data['selector'][] = 'div.elementor-element-' . $element['id'] . ' div.elementor-menu-cart__toggle'; + } + + return $fragment_data; + } + + /** + * Is Preview + * + * Helper to check if we are doing either: + * - Viewing the WP Preview page - also used as the Elementor preview page when clicking "Preview Changes" in the editor + * - Viewing the page in the editor, but not the active page being edited e.g. if you click Edit Header while editing a page + * + * @since 3.7.0 + * + * @return bool + */ + public static function is_preview() { + return Plugin::elementor()->preview->is_preview_mode() || is_preview(); + } + + public function __construct() { + parent::__construct(); + + new WoocommerceDataController(); + + add_action( 'elementor/kit/register_tabs', [ $this, 'init_site_settings' ], 1, 40 ); + $this->add_update_kit_settings_hooks(); + + add_action( 'elementor/template-library/create_new_dialog_fields', [ $this, 'add_products_type_to_template_popup' ], 11 ); + add_action( 'elementor-pro/modules/loop-builder/documents/loop/query_settings', [ $this, 'add_products_type_to_loop_settings_query' ], 11 ); + + add_action( 'elementor/template-library/create_new_dialog_fields', [ $this, 'add_products_taxonomy_type_to_template_popup' ], 13 ); + add_action( 'elementor-pro/modules/loop-builder/documents/loop/query_settings', [ $this, 'add_products_taxonomy_type_to_loop_settings_query' ], 13 ); + + $this->use_mini_cart_template = 'yes' === get_option( 'elementor_' . self::OPTION_NAME_USE_MINI_CART, 'no' ); + + if ( is_admin() ) { + add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 15 ); + } + + add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'maybe_init_cart' ] ); + add_action( 'elementor/dynamic_tags/register', [ $this, 'register_tags' ] ); + add_action( 'elementor/documents/register', [ $this, 'register_documents' ] ); + add_action( 'elementor/theme/register_conditions', [ $this, 'register_conditions' ] ); + + add_action( 'wp_ajax_elementor_woocommerce_checkout_login_user', [ $this, 'elementor_woocommerce_checkout_login_user' ] ); + add_action( 'wp_ajax_nopriv_elementor_woocommerce_checkout_login_user', [ $this, 'elementor_woocommerce_checkout_login_user' ] ); + + add_action( 'wp_ajax_elementor_menu_cart_fragments', [ $this, 'menu_cart_fragments' ] ); + add_action( 'wp_ajax_nopriv_elementor_menu_cart_fragments', [ $this, 'menu_cart_fragments' ] ); + + add_filter( 'woocommerce_add_to_cart_fragments', [ $this, 'e_cart_count_fragments' ] ); + + add_filter( 'elementor/theme/need_override_location', [ $this, 'theme_template_include' ], 10, 2 ); + + add_filter( 'elementor/document/config', [ $this, 'add_loop_recommended_widgets' ], 11, 2 ); + + add_filter( 'elementor_pro/frontend/localize_settings', [ $this, 'localized_settings_frontend' ] ); + + // Load our widget Before WooCommerce Ajax. See the variable's PHPDoc for details. + add_action( 'woocommerce_checkout_update_order_review', [ $this, 'load_widget_before_wc_ajax' ] ); + add_action( 'woocommerce_before_calculate_totals', [ $this, 'load_widget_before_wc_ajax' ] ); + + // On Editor - Register WooCommerce frontend hooks before the Editor init. + // Priority = 5, in order to allow plugins remove/add their wc hooks on init. + $action = ProUtils::_unstable_get_super_global_value( $_REQUEST, 'action' ); + if ( 'elementor' === $action && is_admin() ) { + add_action( 'init', [ $this, 'register_wc_hooks' ], 5 ); + } + + // Allow viewing of Checkout page in the Editor with an empty cart. + if ( + ( 'elementor' === $action && is_admin() ) // Elementor Editor + || 'elementor_ajax' === $action // Elementor Editor Preview - Ajax Render Widget + || ProUtils::_unstable_get_super_global_value( $_REQUEST, 'elementor-preview' ) // Elementor Editor Preview + ) { + add_filter( 'woocommerce_checkout_redirect_empty_cart', '__return_false', 5 ); + } + + if ( $this->use_mini_cart_template ) { + add_filter( 'woocommerce_locate_template', [ $this, 'woocommerce_locate_template' ], 10, 3 ); + } + + $wc_ajax = ProUtils::_unstable_get_super_global_value( $_REQUEST, 'wc-ajax' ); + if ( 'get_refreshed_fragments' === $wc_ajax ) { + add_action( 'woocommerce_add_to_cart_fragments', [ $this, 'products_query_sources_fragments' ] ); + // Render the Empty Cart Template on WC fragment refresh + add_action( 'woocommerce_add_to_cart_fragments', [ $this, 'empty_cart_fragments' ] ); + } + + add_filter( 'elementor/widgets/wordpress/widget_args', [ $this, 'woocommerce_wordpress_widget_css_class' ], 10, 2 ); + + add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); + + // Make the Logout redirect go to our my account widget page instead of the set My Account Page. + add_action( 'init', [ $this, 'elementor_wc_my_account_logout' ], 5 ); + + add_filter( 'elementor_pro/editor/localize_settings', [ $this, 'add_localize_data' ] ); + + add_action( 'wp', [ $this, 'maybe_define_woocommerce_checkout' ] ); + + add_filter( 'woocommerce_get_endpoint_url', [ $this, 'get_order_received_endpoint_url' ], 10, 3 ); + + // Filters for messages on the Shipping calculator + add_filter( 'woocommerce_shipping_may_be_available_html', function ( $html ) { + return $this->print_woocommerce_shipping_message( $html, 'woocommerce-shipping-may-be-available-html e-checkout-message e-cart-content' ); + }, 10, 1 ); + + add_filter( 'woocommerce_shipping_not_enabled_on_cart_html', function ( $html ) { + return $this->print_woocommerce_shipping_message( $html, 'woocommerce-shipping-not_enabled-on-cart-html e-checkout-message e-cart-content' ); + }, 10, 1 ); + + add_filter( 'woocommerce_shipping_estimate_html', function ( $html ) { + return $this->print_woocommerce_shipping_message( $html, 'woocommerce-shipping-estimate-html e-checkout-message e-cart-content' ); + }, 10, 1 ); + + add_filter( 'woocommerce_cart_no_shipping_available_html', function ( $html ) { + return $this->print_woocommerce_shipping_message( $html, 'woocommerce-cart-no-shipping-available-html e-checkout-message e-cart-content' ); + }, 10, 1 ); + + add_filter( 'woocommerce_no_available_payment_methods_message', function ( $html ) { + return $this->print_woocommerce_shipping_message( $html, 'woocommerce-no-available-payment-methods-message e-description' ); + }, 10, 1 ); + + add_filter( 'woocommerce_no_shipping_available_html', function ( $html ) { + return $this->print_woocommerce_shipping_message( $html, 'woocommerce-no-shipping-available-html e-checkout-message' ); + }, 10, 1 ); + + add_action( 'woocommerce_add_to_cart', [ $this, 'localize_added_to_cart_on_product_single' ] ); + + foreach ( LoopBuilderModule::LOOP_WIDGETS as $widget_type ) { + add_action( 'elementor/widget/' . $widget_type . '/skins_init', function( Widget_Base $widget ) { + $widget->add_skin( new Skin_Loop_Product( $widget ) ); + } ); + add_action( 'elementor/widget/' . $widget_type . '/skins_init', function ( Widget_Base $widget ) { + $widget->add_skin( new Skin_Loop_Product_Taxonomy( $widget ) ); + }, 13 ); + } + + // WooCommerce Notice Site Settings + add_filter( 'body_class', [ $this, 'e_notices_body_classes' ] ); + add_filter( 'wp_enqueue_scripts', [ $this, 'e_notices_css' ] ); + add_action( 'enqueue_block_editor_assets', [ $this, 'custom_gutenberg_woocommerce_notice' ] ); + + add_filter( 'elementor/query/query_args', function( $query_args, $widget ) { + return $this->loop_query( $query_args, $widget ); + }, 10, 2 ); + + add_filter( 'woocommerce_rest_prepare_system_status', function( $response, $system_status, $request ) { + return $this->add_system_status_data( $response, $system_status, $request ); + }, 10, 3 ); + + add_filter( 'elementor/editor/localize_settings', function ( $config ) { + return $this->populate_persistent_settings( $config ); + }); + } + + public function add_system_status_data( $response, $system_status, $request ) { + foreach ( $response->data['pages'] as $index => $wc_page ) { + $this->modify_response_if_widget_exists_in_page( $wc_page, $response, $index ); + } + + return $response; + } + + private function modify_response_if_widget_exists_in_page( $wc_page, &$response, $index ) { + if ( empty( $wc_page['page_name'] ) || empty( $wc_page['page_id'] ) || ! array_key_exists( $wc_page['page_name'], static::WC_STATUS_PAGES_MAPPED_TO_WIDGETS ) ) { + return; + } + + if ( isset( $wc_page['shortcode_present'] ) && false !== $wc_page['shortcode_present'] ) { + return; + } + + $document = Plugin::elementor()->documents->get( $wc_page['page_id'] ); + + if ( ! $document || ! $document->is_built_with_elementor() ) { + return; + } + + $elementor_data = get_post_meta( $wc_page['page_id'], '_elementor_data', true ); + $widget_name = static::WC_STATUS_PAGES_MAPPED_TO_WIDGETS[ $wc_page['page_name'] ]; + $widget_exists_in_page = false !== strpos( $elementor_data, $widget_name ); + + if ( $widget_exists_in_page ) { + $response->data['pages'][ $index ]['shortcode_present'] = true; + } + } + + public function loop_query( $query_args, $widget ) { + if ( ! $this->is_product_query( $widget ) ) { + return $query_args; + } + + return $this->parse_loop_query_args( $widget, $query_args ); + } + + private function is_product_query( $widget ) { + $widget_config = $widget->get_config(); + + return ( ! empty( $widget_config['is_loop'] ) && 'product' === $widget->get_current_skin_id() ); + } + + private function parse_loop_query_args( $widget, $query_args ) { + $settings = $this->adjust_setting_for_product_renderer( $widget ); + + // For Products_Renderer. + if ( ! isset( $GLOBALS['post'] ) ) { + $GLOBALS['post'] = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + } + + $shortcode = Products_Widget::get_shortcode_object( $settings ); + + $parsed_query_args = $shortcode->parse_query_args(); + + unset( $parsed_query_args['fields'] ); + + $override_various_query_args = array_filter( $query_args, function( $key ) { + return in_array( $key, [ 'posts_per_page', 'offset', 'paged' ], true ); + }, ARRAY_FILTER_USE_KEY ); + + return wp_parse_args( $override_various_query_args, $parsed_query_args ); + } + + private function adjust_setting_for_product_renderer( $widget ) { + $settings = $widget->get_settings_for_display(); + + $query_name = $widget->get_query_name(); + + $unique_query_settings = array_filter( $settings, function( $key ) use ( $query_name ) { + return 0 === strpos( $key, $query_name ); + }, ARRAY_FILTER_USE_KEY ); + + $query_settings = []; + + foreach ( $unique_query_settings as $key => $value ) { + $query_settings[ 'query' . str_replace( $query_name, '', $key ) ] = $value; + } + + $settings = array_merge( $settings, $query_settings ); + + if ( isset( $settings['posts_per_page'] ) && isset( $settings['columns'] ) ) { + $settings['rows'] = ceil( $settings['posts_per_page'] / $settings['columns'] ); + } + + $settings['paginate'] = 'yes'; + $settings['allow_order'] = 'no'; + $settings['show_result_count'] = 'no'; + $settings['query_fields'] = false; + + return $settings; + } + + /** + * @param $post_id + * @return bool + */ + private function is_source_set_to_products( $post_id ) { + return 'product' === get_post_meta( $post_id, '_elementor_source', true ); + } + + /** + * @param array $config + * @return array + */ + private function add_woocommerce_widgets_to_recommended( array $config ) { + foreach ( static::RECOMMENDED_POSTS_WIDGET_NAMES as $recommended_posts_widget_name ) { + $config['panel']['widgets_settings'][ $recommended_posts_widget_name ] = [ + 'categories' => [ 'recommended' ], + 'show_in_panel' => true, + ]; + } + return $config; + } + + private function hide_woocommerce_widgets_in_loop_document( array $config ) { + $config['panel']['widgets_settings']['woocommerce-product-images'] = [ + 'show_in_panel' => false, + ]; + return $config; + } + + private function populate_persistent_settings( array $config ) { + $config['persistent_keys'] = array_key_exists( 'persistent_keys', $config ) ? + array_merge( $config['persistent_keys'], self::WC_PERSISTENT_SITE_SETTINGS ) : + self::WC_PERSISTENT_SITE_SETTINGS; + + return $config; + } +} diff --git a/modules/woocommerce/settings/settings-woocommerce.php b/modules/woocommerce/settings/settings-woocommerce.php new file mode 100644 index 0000000..47196c6 --- /dev/null +++ b/modules/woocommerce/settings/settings-woocommerce.php @@ -0,0 +1,899 @@ +start_controls_section( + 'section_woocommerce_pages', + [ + 'label' => esc_html__( 'WooCommerce Pages', 'elementor-pro' ), + 'tab' => $this->get_id(), + ] + ); + + if ( API::is_licence_has_feature( Woocommerce::SITE_SETTINGS_PAGES_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->register_woocommerce_pages_controls(); + } else { + $this->register_woocommerce_pages_promotion(); + } + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_woocommerce_notices', + [ + 'label' => esc_html__( 'Notices', 'elementor-pro' ), + 'tab' => $this->get_id(), + ] + ); + + if ( API::is_licence_has_feature( Woocommerce::SITE_SETTINGS_NOTICES_LICENSE_FEATURE_NAME, API::BC_VALIDATION_CALLBACK ) ) { + $this->register_woocommerce_notices_controls(); + } else { + $this->register_woocommerce_notices_promotion(); + } + + $this->end_controls_section(); + + $this->start_controls_section( + 'woocommerce_error_notices', + [ + 'label' => esc_html__( 'Error Notices', 'elementor-pro' ), + 'tab' => $this->get_id(), + 'condition' => [ + 'woocommerce_notices_elements' => 'wc_error', + ], + ] + ); + + $this->add_notice_text_controls( 'error', $this->get_notice_text_selectors( 'error' ) ); + + $this->add_control( + 'error_message_link_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Link Text', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'error_message_link_typography', + 'selector' => $this->get_main_selector( 'error', 'body', ' a.wc-backward' ), + ] + ); + + $this->start_controls_tabs( 'error_message_links' ); + + $this->start_controls_tab( 'error_message_normal_links', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $this->add_control( + 'error_message_normal_links_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + $this->get_main_selector( 'error' ) => '--error-message-normal-links-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'error_message_hover_links', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] ); + + $this->add_control( + 'error_message_hover_links_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + $this->get_main_selector( 'error' ) => '--error-message-hover-links-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_notice_box_controls( 'error', $this->get_notice_box_selectors( 'error' ) ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'woocommerce_message_notices', + [ + 'label' => esc_html__( 'Message Notices', 'elementor-pro' ), + 'tab' => $this->get_id(), + 'condition' => [ + 'woocommerce_notices_elements' => 'wc_message', + ], + ] + ); + + $this->add_notice_text_controls( 'message', $this->get_notice_text_selectors( 'message' ) ); + + $this->add_control( + 'notice_message_link_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Link Text', 'elementor-pro' ), + ] + ); + + $message_link_typography_selector = $this->get_main_selector( 'message', 'body', ' .restore-item' ); + $message_link_typography_selector .= ', '; + $message_link_typography_selector .= $this->get_main_selector( 'message', 'body', ' a:not([class])' ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'notice_message_link_typography', + 'selector' => $message_link_typography_selector, + ] + ); + + $this->start_controls_tabs( 'notice_message_links' ); + + $this->start_controls_tab( 'notice_message_normal_links', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $message_normal_link_color_selector = $this->get_main_selector( 'message', '', ' .restore-item' ); + $message_normal_link_color_selector .= ', '; + $message_normal_link_color_selector .= $this->get_main_selector( 'message', '', ' a:not([class])' ); + + $this->add_control( + 'notice_message_normal_links_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + $message_normal_link_color_selector => '--notice-message-normal-links-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'notice_message_hover_links', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] ); + + $message_hover_link_color_selector = $this->get_main_selector( 'message', '', ' .restore-item:hover' ); + $message_hover_link_color_selector .= ', '; + $message_hover_link_color_selector .= $this->get_main_selector( 'message', '', ' a:not([class]):hover' ); + + $this->add_control( + 'notice_message_hover_links_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + $message_hover_link_color_selector => '--notice-message-hover-links-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_notice_box_controls( 'message', $this->get_notice_box_selectors( 'message' ) ); + + $this->add_notice_button_controls( 'message', $this->get_notice_button_selectors( 'message' ) ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'woocommerce_info_notices', + [ + 'label' => esc_html__( 'Info Notices', 'elementor-pro' ), + 'tab' => $this->get_id(), + 'condition' => [ + 'woocommerce_notices_elements' => 'wc_info', + ], + ] + ); + + $this->add_notice_text_controls( 'info', $this->get_notice_text_selectors( 'info' ) ); + + $this->add_notice_box_controls( 'info', $this->get_notice_box_selectors( 'info' ) ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_notice_button_controls( 'info', $this->get_notice_button_selectors( 'info' ) ); + + $this->end_controls_section(); + } + + private function get_main_selector( $notice_type, $selector_prefix = '', $selector_suffix = '' ) { + $notice_name = 'message' === $notice_type ? 'success' : $notice_type; + $old_notice_selector = $selector_prefix . '.e-wc-' . $notice_type . '-notice .woocommerce-' . $notice_type . $selector_suffix; + $block_notice_selector = $selector_prefix . '.e-wc-' . $notice_type . '-notice .wc-block-components-notice-banner.is-' . $notice_name . $selector_suffix; + + return $old_notice_selector . ', ' . $block_notice_selector; + } + + private function get_notice_text_selectors( $notice_type ) { + return [ + $notice_type . '_message_text_color' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-message-text-color: {{VALUE}};', + ], + $notice_type . '_message_text_typography' => $this->get_main_selector( $notice_type ), + $notice_type . '_message_text_shadow' => $this->get_main_selector( $notice_type ), + $notice_type . '_message_icon_color' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-message-icon-color: {{VALUE}};', + ], + $notice_type . '_message_icon_size' => [ + $this->get_main_selector( $notice_type, '', ':before' ) => 'font-size: {{SIZE}}{{UNIT}};', + ], + $notice_type . '_message_icon_spacing' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-message-icon-spacing: {{SIZE}}{{UNIT}};', + ], + ]; + } + + private function get_notice_box_selectors( $notice_type ) { + return [ + $notice_type . '_notice_box_background' => $this->get_main_selector( $notice_type, 'body' ), + $notice_type . '_notice_box_box_shadow' => $this->get_main_selector( $notice_type ), + $notice_type . '_notice_box_border' => $this->get_main_selector( $notice_type, 'body' ), + $notice_type . '_notice_box_border_radius' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-box-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + $notice_type . '_notice_box_padding' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-box-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ]; + } + + private function get_notice_button_selectors( $notice_type ) { + $button_hover_background_selector = $this->get_main_selector( $notice_type, 'body', ' .button:hover' ); + + if ( 'info' === $notice_type ) { + // Override styling from the My Account widget. + $button_hover_background_selector .= ', body.e-wc-info-notice .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .woocommerce-info .woocommerce-Button:hover'; + $button_hover_background_selector .= ', body.e-wc-info-notice .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .wc-block-components-notice-banner.is-info .woocommerce-Button:hover'; + } + + return [ + $notice_type . '_button_typography' => $this->get_main_selector( $notice_type, 'body', ' .button' ), + $notice_type . '_button_text_shadow' => $this->get_main_selector( $notice_type, 'body', ' .button' ), + $notice_type . '_buttons_normal_text_color' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-buttons-normal-text-color: {{VALUE}};', + ], + $notice_type . '_buttons_normal_background' => $this->get_main_selector( $notice_type, 'body', ' .button' ), + $notice_type . '_buttons_normal_box_shadow' => $this->get_main_selector( $notice_type, '', ' .button' ), + $notice_type . '_buttons_hover_text_color' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-buttons-hover-text-color: {{VALUE}};', + ], + $notice_type . '_buttons_hover_background' => $button_hover_background_selector, + $notice_type . '_buttons_focus_box_shadow' => $this->get_main_selector( $notice_type, '', ' .button:hover' ), + $notice_type . '_buttons_hover_border_color' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-buttons-hover-border-color: {{VALUE}};', + ], + $notice_type . '_buttons_hover_transition_duration' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-buttons-hover-transition-duration: {{SIZE}}ms;', + ], + $notice_type . '_buttons_border_type' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-border-type: {{VALUE}};', + ], + $notice_type . '_buttons_border_width' => [ + $this->get_main_selector( $notice_type, 'body', ' .button' ) => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + $notice_type . '_buttons_border_color' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-border-color: {{VALUE}};', + ], + $notice_type . '_buttons_border_radius' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-buttons-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + $notice_type . '_buttons_padding' => [ + $this->get_main_selector( $notice_type ) => '--' . $notice_type . '-buttons-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ]; + } + + private function add_notice_text_controls( $notice_type, $selectors ) { + $this->add_control( + $notice_type . '_message_text_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Notice Text', 'elementor-pro' ), + ] + ); + + $this->add_control( + $notice_type . '_message_text_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => $selectors[ $notice_type . '_message_text_color' ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => $notice_type . '_message_text_typography', + 'selector' => $selectors[ $notice_type . '_message_text_typography' ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => $notice_type . '_message_text_shadow', + 'selector' => $selectors[ $notice_type . '_message_text_shadow' ], + ] + ); + + $this->add_control( + $notice_type . '_message_icon_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + ] + ); + + $this->add_control( + $notice_type . '_message_icon_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => $selectors[ $notice_type . '_message_icon_color' ], + ] + ); + } + + private function add_notice_box_controls( $notice_type, $selectors ) { + $this->add_control( + $notice_type . '_notice_box_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Notice Box', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => $notice_type . '_notice_box_background', + 'selector' => $selectors[ $notice_type . '_notice_box_background' ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => $notice_type . '_notice_box_box_shadow', + 'selector' => $selectors[ $notice_type . '_notice_box_box_shadow' ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => $notice_type . '_notice_box_border', + 'selector' => $selectors[ $notice_type . '_notice_box_border' ], + ] + ); + + $this->add_responsive_control( + $notice_type . '_notice_box_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => $selectors[ $notice_type . '_notice_box_border_radius' ], + ] + ); + } + + private function add_notice_button_controls( $notice_type, $selectors ) { + $this->add_control( + $notice_type . '_button_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Button', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => $notice_type . '_button_typography', + 'selector' => $selectors[ $notice_type . '_button_typography' ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => $notice_type . '_button_text_shadow', + 'selector' => $selectors[ $notice_type . '_button_text_shadow' ], + ] + ); + + $this->start_controls_tabs( $notice_type . '_buttons_styles' ); + + $this->start_controls_tab( $notice_type . '_buttons_normal_styles', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $this->add_control( + $notice_type . '_buttons_normal_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => $selectors[ $notice_type . '_buttons_normal_text_color' ], + ] + ); + + $button_background_selectors = [ + 'image' => [ + 'selectors' => [ + '{{SELECTOR}}' => 'background-image: url("{{URL}}") !important;', + ], + ], + 'color' => [ + 'selectors' => [ + '{{SELECTOR}}' => 'background-color: {{VALUE}} !important; background-image: none !important', + ], + ], + 'gradient_angle' => [ + 'selectors' => [ + '{{SELECTOR}}' => 'background-color: transparent !important; background-image: linear-gradient({{SIZE}}{{UNIT}}, {{color.VALUE}} {{color_stop.SIZE}}{{color_stop.UNIT}}, {{color_b.VALUE}} {{color_b_stop.SIZE}}{{color_b_stop.UNIT}}) !important', + ], + ], + 'gradient_position' => [ + 'selectors' => [ + '{{SELECTOR}}' => 'background-color: transparent !important; background-image: radial-gradient(at {{VALUE}}, {{color.VALUE}} {{color_stop.SIZE}}{{color_stop.UNIT}}, {{color_b.VALUE}} {{color_b_stop.SIZE}}{{color_b_stop.UNIT}}) !important', + ], + ], + 'position' => [ + 'selectors' => [ + '{{SELECTOR}}' => 'background-position: {{VALUE}} !important;', + ], + ], + 'xpos' => [ + 'selectors' => [ + '{{SELECTOR}}' => 'background-position: {{SIZE}}{{UNIT}} {{ypos.SIZE}}{{ypos.UNIT}} !important', + ], + ], + 'ypos' => [ + 'selectors' => [ + '{{SELECTOR}}' => 'background-position: {{xpos.SIZE}}{{xpos.UNIT}} {{SIZE}}{{UNIT}} !important', + ], + ], + 'repeat' => [ + 'selectors' => [ + '{{SELECTOR}}' => 'background-repeat: {{VALUE}} !important;', + ], + ], + 'size' => [ + 'selectors' => [ + '{{SELECTOR}}' => 'background-size: {{VALUE}} !important;', + ], + ], + 'bg_width' => [ + 'selectors' => [ + '{{SELECTOR}}' => 'background-size: {{SIZE}}{{UNIT}} auto !important', + ], + ], + ]; + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => $notice_type . '_buttons_normal_background', + 'selector' => $selectors[ $notice_type . '_buttons_normal_background' ], + 'fields_options' => $button_background_selectors, + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => $notice_type . '_buttons_normal_box_shadow', + 'selector' => $selectors[ $notice_type . '_buttons_normal_box_shadow' ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( $notice_type . '_buttons_hover_styles', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] ); + + $this->add_control( + $notice_type . '_buttons_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => $selectors[ $notice_type . '_buttons_hover_text_color' ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => $notice_type . '_buttons_hover_background', + 'selector' => $selectors[ $notice_type . '_buttons_hover_background' ], + 'fields_options' => $button_background_selectors, + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => $notice_type . '_buttons_focus_box_shadow', + 'selector' => $selectors[ $notice_type . '_buttons_focus_box_shadow' ], + ] + ); + + $this->add_control( + $notice_type . '_buttons_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => $selectors[ $notice_type . '_buttons_hover_border_color' ], + 'condition' => [ + $notice_type . '_buttons_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + $notice_type . '_buttons_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => $selectors[ $notice_type . '_buttons_hover_transition_duration' ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + $notice_type . '_buttons_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'solid' => esc_html__( 'Solid', 'elementor-pro' ), + 'double' => esc_html__( 'Double', 'elementor-pro' ), + 'dotted' => esc_html__( 'Dotted', 'elementor-pro' ), + 'dashed' => esc_html__( 'Dashed', 'elementor-pro' ), + 'groove' => esc_html__( 'Groove', 'elementor-pro' ), + ], + 'selectors' => $selectors[ $notice_type . '_buttons_border_type' ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + $notice_type . '_buttons_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => $selectors[ $notice_type . '_buttons_border_width' ], + 'condition' => [ + $notice_type . '_buttons_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + $notice_type . '_buttons_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => $selectors[ $notice_type . '_buttons_border_color' ], + 'condition' => [ + $notice_type . '_buttons_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + $notice_type . '_buttons_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => $selectors[ $notice_type . '_buttons_border_radius' ], + ] + ); + + $this->add_responsive_control( + $notice_type . '_buttons_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => $selectors[ $notice_type . '_buttons_padding' ], + ] + ); + } + + public function on_save( $data ) { + if ( + ! isset( $data['settings']['post_status'] ) || + Document::STATUS_PUBLISH !== $data['settings']['post_status'] || + // Should check for the current action to avoid infinite loop + // when updating options like: "blogname" and "blogdescription". + strpos( current_action(), 'update_option_' ) === 0 + ) { + return; + } + + $ec_wc_key_mapping = [ + 'woocommerce_cart_page_id' => 'woocommerce_cart_page_id', + 'woocommerce_checkout_page_id' => 'woocommerce_checkout_page_id', + 'woocommerce_myaccount_page_id' => 'woocommerce_myaccount_page_id', + 'woocommerce_terms_page_id' => 'woocommerce_terms_page_id', + 'woocommerce_purchase_summary_page_id' => 'elementor_woocommerce_purchase_summary_page_id', + 'woocommerce_shop_page_id' => 'woocommerce_shop_page_id', + ]; + foreach ( $ec_wc_key_mapping as $ec_key => $wc_key ) { + if ( array_key_exists( $ec_key, $data['settings'] ) ) { + $value = $data['settings'][ $ec_key ] ? $data['settings'][ $ec_key ] : ''; + update_option( $wc_key, $value ); + } + } + } + + private function register_woocommerce_pages_controls() { + $this->add_control( + 'woocommerce_pages_intro', + [ + 'raw' => esc_html__( 'Select the pages you want to use as your default WooCommerce shop pages', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + ] + ); + + $autocomplete = [ + 'object' => QueryModule::QUERY_OBJECT_POST, + 'query' => [ + 'post_type' => [ 'page' ], + ], + ]; + + $this->add_control( + 'woocommerce_cart_page_id', + [ + 'label' => esc_html__( 'Cart', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'placeholder' => esc_html__( 'Select a page', 'elementor-pro' ), + ], + 'autocomplete' => $autocomplete, + 'default' => get_option( 'woocommerce_cart_page_id' ), + ] + ); + + $this->add_control( + 'woocommerce_checkout_page_id', + [ + 'label' => esc_html__( 'Checkout', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'placeholder' => esc_html__( 'Select a page', 'elementor-pro' ), + ], + 'autocomplete' => $autocomplete, + 'default' => get_option( 'woocommerce_checkout_page_id' ), + ] + ); + + $this->add_control( + 'woocommerce_myaccount_page_id', + [ + 'label' => esc_html__( 'My Account', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'placeholder' => esc_html__( 'Select a page', 'elementor-pro' ), + ], + 'autocomplete' => $autocomplete, + 'default' => get_option( 'woocommerce_myaccount_page_id' ), + ] + ); + + $this->add_control( + 'woocommerce_terms_page_id', + [ + 'label' => esc_html__( 'Terms & Conditions', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'placeholder' => esc_html__( 'Select a page', 'elementor-pro' ), + ], + 'autocomplete' => $autocomplete, + 'default' => get_option( 'woocommerce_terms_page_id' ), + ] + ); + + $this->add_control( + 'woocommerce_purchase_summary_page_id', + [ + 'label' => esc_html__( 'Purchase Summary', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'placeholder' => esc_html__( 'Select a page', 'elementor-pro' ), + ], + 'autocomplete' => $autocomplete, + 'default' => get_option( 'elementor_woocommerce_purchase_summary_page_id' ), // This is not in WC core. This is a custom page added by Elementor. + ] + ); + + $this->add_control( + 'woocommerce_shop_page_id', + [ + 'label' => esc_html__( 'Shop', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'select2options' => [ + 'placeholder' => esc_html__( 'Select a page', 'elementor-pro' ), + ], + 'autocomplete' => $autocomplete, + 'default' => get_option( 'woocommerce_shop_page_id' ), + ] + ); + + $this->add_control( + 'woocommerce_pages_notice', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'Note: Changes you make here will also be reflected in the WooCommerce settings on your WP dashboard', 'elementor-pro' ), + ] + ); + } + + private function register_woocommerce_notices_controls() { + $this->add_control( + 'woocommerce_notices_intro', + [ + 'raw' => esc_html__( 'Here\'s where you can customize how notices form WooCommerce will appear for your customers', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + ] + ); + + $this->add_control( + 'woocommerce_notices_elements', + [ + 'label' => esc_html__( 'Notice Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'options' => [ + 'wc_error' => esc_html__( 'Error Notices', 'elementor-pro' ), + 'wc_message' => esc_html__( 'Message Notices', 'elementor-pro' ), + 'wc_info' => esc_html__( 'Info Notices', 'elementor-pro' ), + ], + 'render_type' => 'ui', + 'label_block' => true, + 'frontend_available' => true, + 'default' => [], + ] + ); + } + + /** + * @return array + */ + public function get_notices_promotion_data() { + return [ + 'title' => sprintf( + esc_html__( 'Say hello to %s WooCommerce notices!', 'elementor-pro' ), + '
      ' + ), + 'messages' => [ + esc_html__( 'Upgrade your subscription to customize these and much more.', 'elementor-pro' ), + ], + 'link' => 'https://go.elementor.com/go-pro-advanced-site-settings-woocommerce-notices/', + ]; + } + + /** + * @return array + */ + private function get_pages_promotion_data(): array { + return [ + 'title' => sprintf( + esc_html__( 'Say hello to %s WooCommerce pages!', 'elementor-pro' ), + '
      ' + ), + 'messages' => [ + esc_html__( 'Upgrade your subscription to customize these and much more.', 'elementor-pro' ), + ], + 'link' => 'https://go.elementor.com/go-pro-advanced-site-settings-woocommerce-pages/', + ]; + } + + /** + * @return void + */ + private function register_woocommerce_pages_promotion(): void { + $this->add_control( + 'woocommerce_pages_promotion', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => Tiers::get_promotion_template( $this->get_pages_promotion_data() ), + ] + ); + } + + /** + * @return void + */ + private function register_woocommerce_notices_promotion(): void { + $this->add_control( + 'woocommerce_notices_promotion', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => Tiers::get_promotion_template( $this->get_notices_promotion_data() ), + ] + ); + } +} diff --git a/modules/woocommerce/skins/skin-classic.php b/modules/woocommerce/skins/skin-classic.php new file mode 100644 index 0000000..f28e1fd --- /dev/null +++ b/modules/woocommerce/skins/skin-classic.php @@ -0,0 +1,89 @@ +parent = $widget; + + $this->add_control( + 'columns', + [ + 'label' => esc_html__( 'Columns', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '1' => '1', + '2' => '2', + '3' => '3', + '4' => '4', + '5' => '5', + '6' => '6', + ], + 'default' => '4', + ] + ); + } + + public function render() { + $this->parent->query_posts(); + + /** @var \WP_Query $query */ + $query = $this->parent->get_query(); + + if ( ! $query->have_posts() ) { + return; + } + + global $woocommerce_loop; + + $woocommerce_loop['columns'] = (int) $this->get_instance_value( 'columns' ); + + Module::instance()->add_products_post_class_filter(); + + echo '
      '; + + woocommerce_product_loop_start(); + + while ( $query->have_posts() ) { + $query->the_post(); + + wc_get_template_part( 'content', 'product' ); + } + + woocommerce_product_loop_end(); + + woocommerce_reset_loop(); + + wp_reset_postdata(); + + echo '
      '; + + Module::instance()->remove_products_post_class_filter(); + } +} diff --git a/modules/woocommerce/skins/skin-loop-product-taxonomy.php b/modules/woocommerce/skins/skin-loop-product-taxonomy.php new file mode 100644 index 0000000..041d009 --- /dev/null +++ b/modules/woocommerce/skins/skin-loop-product-taxonomy.php @@ -0,0 +1,31 @@ +parent->add_render_attribute( '_wrapper', 'class', 'woocommerce' ); + parent::render(); + } + + protected function get_default_source_option() { + return Taxonomy_Loop_Provider::PRODUCT_CATEGORY_TAXONOMY; + } +} diff --git a/modules/woocommerce/skins/skin-loop-product.php b/modules/woocommerce/skins/skin-loop-product.php new file mode 100644 index 0000000..4c55e2b --- /dev/null +++ b/modules/woocommerce/skins/skin-loop-product.php @@ -0,0 +1,58 @@ +parent->add_render_attribute( '_wrapper', 'class', 'woocommerce' ); + + parent::render(); + } + + /** + * Register Query Controls + * + * Registers the controls for the query used by the Loop. + * + * @since 3.8.0 + */ + public function register_query_controls( Loop_Widget_Base $widget ) { + $this->parent = $widget; + + $this->add_query_controls( Loop_Module::QUERY_ID ); + } + + protected function render_post() { + global $product; + $product = wc_get_product( get_the_ID() ); + + parent::render_post(); + } +} diff --git a/modules/woocommerce/tags/base-data-tag.php b/modules/woocommerce/tags/base-data-tag.php new file mode 100644 index 0000000..491fdaa --- /dev/null +++ b/modules/woocommerce/tags/base-data-tag.php @@ -0,0 +1,21 @@ +get_category_ids(); + if ( ! empty( $category_ids ) ) { + $category_id = $category_ids[0]; + } + } + } elseif ( Taxonomy_Loop_Provider::is_loop_taxonomy() ) { + $category_id = $this->get_data_id_from_taxonomy_loop_query(); + } + + if ( $category_id ) { + $image_id = get_term_meta( $category_id, 'thumbnail_id', true ); + } + + if ( empty( $image_id ) ) { + return []; + } + + $src = wp_get_attachment_image_src( $image_id, 'full' ); + + return [ + 'id' => $image_id, + 'url' => $src[0], + ]; + } +} diff --git a/modules/woocommerce/tags/product-content.php b/modules/woocommerce/tags/product-content.php new file mode 100644 index 0000000..f913daa --- /dev/null +++ b/modules/woocommerce/tags/product-content.php @@ -0,0 +1,30 @@ +add_product_id_control(); + } + + public function render() { + $product = $this->get_product( $this->get_settings( 'product_id' ) ); + + if ( ! $product ) { + return; + } + + echo wp_kses_post( $product->get_description() ); + } +} diff --git a/modules/woocommerce/tags/product-gallery.php b/modules/woocommerce/tags/product-gallery.php new file mode 100644 index 0000000..e3dfdda --- /dev/null +++ b/modules/woocommerce/tags/product-gallery.php @@ -0,0 +1,46 @@ +get_product( $this->get_settings( 'product_id' ) ); + + if ( ! $product ) { + return []; + } + + $value = []; + + $attachment_ids = $product->get_gallery_image_ids(); + + foreach ( $attachment_ids as $attachment_id ) { + $value[] = [ + 'id' => $attachment_id, + ]; + } + + return $value; + } +} diff --git a/modules/woocommerce/tags/product-image.php b/modules/woocommerce/tags/product-image.php new file mode 100644 index 0000000..27a1d69 --- /dev/null +++ b/modules/woocommerce/tags/product-image.php @@ -0,0 +1,57 @@ +add_product_id_control(); + } + + public function get_group() { + return Module::WOOCOMMERCE_GROUP; + } + + public function get_categories() { + return [ \Elementor\Modules\DynamicTags\Module::IMAGE_CATEGORY ]; + } + + public function get_value( array $options = [] ) { + $product = $this->get_product( $this->get_settings( 'product_id' ) ); + + if ( ! $product ) { + return []; + } + + $image_id = $product->get_image_id(); + + if ( ! $image_id ) { + return []; + } + + $src = wp_get_attachment_image_src( $image_id, 'full' ); + + return [ + 'id' => $image_id, + 'url' => $src[0], + ]; + } +} diff --git a/modules/woocommerce/tags/product-price.php b/modules/woocommerce/tags/product-price.php new file mode 100644 index 0000000..95e6208 --- /dev/null +++ b/modules/woocommerce/tags/product-price.php @@ -0,0 +1,60 @@ +add_control( 'format', [ + 'label' => esc_html__( 'Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'both' => esc_html__( 'Both', 'elementor-pro' ), + 'original' => esc_html__( 'Original', 'elementor-pro' ), + 'sale' => esc_html__( 'Sale', 'elementor-pro' ), + ], + 'default' => 'both', + ] ); + + $this->add_product_id_control(); + } + + public function render() { + $settings = $this->get_settings(); + + $product = $this->get_product( $settings['product_id'] ); + + if ( ! $product ) { + return ''; + } + + $format = $settings['format']; + $value = ''; + switch ( $format ) { + case 'both': + $value = $product->get_price_html(); + break; + case 'original': + $value = wc_price( $product->get_regular_price() ) . $product->get_price_suffix(); + break; + case 'sale' && $product->is_on_sale(): + $value = wc_price( $product->get_sale_price() ) . $product->get_price_suffix(); + break; + } + + // PHPCS - Just passing WC price as is + echo $value; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } +} diff --git a/modules/woocommerce/tags/product-rating.php b/modules/woocommerce/tags/product-rating.php new file mode 100644 index 0000000..53f0c3e --- /dev/null +++ b/modules/woocommerce/tags/product-rating.php @@ -0,0 +1,60 @@ +add_control( 'field', [ + 'label' => esc_html__( 'Format', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'average_rating' => esc_html__( 'Average Rating', 'elementor-pro' ), + 'rating_count' => esc_html__( 'Rating Count', 'elementor-pro' ), + 'review_count' => esc_html__( 'Review Count', 'elementor-pro' ), + ], + 'default' => 'average_rating', + ] ); + + $this->add_product_id_control(); + } + + public function render() { + $settings = $this->get_settings_for_display(); + + $product = $this->get_product( $settings['product_id'] ); + + if ( ! $product ) { + return ''; + } + + $field = $settings['field']; + $value = ''; + switch ( $field ) { + case 'average_rating': + $value = $product->get_average_rating(); + break; + case 'rating_count': + $value = $product->get_rating_count(); + break; + case 'review_count': + $value = $product->get_review_count(); + break; + } + + // PHPCS - Safe WC data + echo $value; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } +} diff --git a/modules/woocommerce/tags/product-sale.php b/modules/woocommerce/tags/product-sale.php new file mode 100644 index 0000000..4dc4214 --- /dev/null +++ b/modules/woocommerce/tags/product-sale.php @@ -0,0 +1,46 @@ +add_control( 'text', [ + 'label' => esc_html__( 'Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Sale!', 'elementor-pro' ), + ] ); + + $this->add_product_id_control(); + } + + public function render() { + $settings = $this->get_settings_for_display(); + + $product = $this->get_product( $settings['product_id'] ); + + if ( ! $product ) { + return; + } + + $value = ''; + + if ( $product->is_on_sale() ) { + $value = $settings['text']; + } + + echo wp_kses_post( $value ); + } +} diff --git a/modules/woocommerce/tags/product-short-description.php b/modules/woocommerce/tags/product-short-description.php new file mode 100644 index 0000000..6919210 --- /dev/null +++ b/modules/woocommerce/tags/product-short-description.php @@ -0,0 +1,30 @@ +add_product_id_control(); + } + + public function render() { + $product = $this->get_product( $this->get_settings( 'product_id' ) ); + + if ( ! $product ) { + return; + } + + echo wp_kses_post( $product->get_short_description() ); + } +} diff --git a/modules/woocommerce/tags/product-sku.php b/modules/woocommerce/tags/product-sku.php new file mode 100644 index 0000000..7ce50c0 --- /dev/null +++ b/modules/woocommerce/tags/product-sku.php @@ -0,0 +1,36 @@ +add_product_id_control(); + } + + public function render() { + $product = $this->get_product( $this->get_settings( 'product_id' ) ); + + if ( ! $product ) { + return; + } + + $value = ''; + + if ( $product->get_sku() ) { + $value = $product->get_sku(); + } + + echo esc_html( $value ); + } +} diff --git a/modules/woocommerce/tags/product-stock.php b/modules/woocommerce/tags/product-stock.php new file mode 100644 index 0000000..b8fc4cd --- /dev/null +++ b/modules/woocommerce/tags/product-stock.php @@ -0,0 +1,52 @@ +get_settings_for_display(); + + $product = $this->get_product( $settings['product_id'] ); + + if ( ! $product ) { + return; + } + + if ( 'yes' === $settings['show_text'] ) { + $value = wc_get_stock_html( $product ); + } else { + $value = (int) $product->get_stock_quantity(); + } + + // PHPCS - `wc_get_stock_html` is safe, and `get_stock_quantity` protected with (int). + echo $value; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + protected function register_controls() { + $this->add_control( + 'show_text', + [ + 'label' => esc_html__( 'Show Text', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => 'yes', + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + ] + ); + + $this->add_product_id_control(); + } +} diff --git a/modules/woocommerce/tags/product-terms.php b/modules/woocommerce/tags/product-terms.php new file mode 100644 index 0000000..b546b82 --- /dev/null +++ b/modules/woocommerce/tags/product-terms.php @@ -0,0 +1,79 @@ +update_control( + 'before', + [ + 'default' => esc_html__( 'Categories', 'elementor-pro' ) . ': ', + ] + ); + } + + protected function register_controls() { + $taxonomy_filter_args = [ + 'show_in_nav_menus' => true, + 'object_type' => [ 'product' ], + ]; + + $taxonomies = get_taxonomies( $taxonomy_filter_args, 'objects' ); + + $options = []; + + foreach ( $taxonomies as $taxonomy => $object ) { + $options[ $taxonomy ] = $object->label; + } + + $this->add_control( + 'taxonomy', + [ + 'label' => esc_html__( 'Taxonomy', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $options, + 'default' => 'product_cat', + ] + ); + + $this->add_control( + 'separator', + [ + 'label' => esc_html__( 'Separator', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => ', ', + ] + ); + + $this->add_product_id_control(); + } + + public function render() { + $settings = $this->get_settings_for_display(); + + $product = $this->get_product( $settings['product_id'] ); + + if ( ! $product ) { + return; + } + + $value = get_the_term_list( $product->get_id(), $settings['taxonomy'], '', $settings['separator'] ); + + echo wp_kses_post( $value ); + } +} diff --git a/modules/woocommerce/tags/product-title.php b/modules/woocommerce/tags/product-title.php new file mode 100644 index 0000000..cf39ed4 --- /dev/null +++ b/modules/woocommerce/tags/product-title.php @@ -0,0 +1,36 @@ +add_product_id_control(); + } + + public function render() { + $product = $this->get_product( $this->get_settings( 'product_id' ) ); + + if ( ! $product ) { + return; + } + + if ( 'variation' === $product->get_type() ) { + $title = $product->get_name(); + } else { + $title = get_the_title( $product->get_id() ); + } + + echo wp_kses_post( $title ); + } +} diff --git a/modules/woocommerce/tags/traits/tag-product-id.php b/modules/woocommerce/tags/traits/tag-product-id.php new file mode 100644 index 0000000..d1fc38a --- /dev/null +++ b/modules/woocommerce/tags/traits/tag-product-id.php @@ -0,0 +1,28 @@ +add_control( + 'product_id', + [ + 'label' => esc_html__( 'Product', 'elementor-pro' ), + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_POST, + 'query' => [ + 'post_type' => [ 'product' ], + ], + ], + // Since we're using the `wc_get_product` method to retrieve products, when no product selected manually + // by the dynamic tag - the default should be `false` so the method will use the product id given in the + // http request instead. + 'default' => false, + ] + ); + } +} diff --git a/modules/woocommerce/tags/woocommerce-add-to-cart.php b/modules/woocommerce/tags/woocommerce-add-to-cart.php new file mode 100644 index 0000000..d78cac9 --- /dev/null +++ b/modules/woocommerce/tags/woocommerce-add-to-cart.php @@ -0,0 +1,93 @@ +add_control( + 'product_id', + [ + 'label' => esc_html__( 'Product', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_POST, + 'query' => [ + 'post_type' => [ 'product' ], + 'tax_query' => [ + [ + 'taxonomy' => 'product_type', + 'field' => 'slug', + 'terms' => 'simple', + ], + ], + ], + ], + ] + ); + + $this->add_control( + 'quantity', + [ + 'label' => esc_html__( 'Quantity', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'ai' => [ + 'active' => false, + ], + 'default' => 1, + ] + ); + } + + public function get_value( array $options = [] ) { + $settings = $this->get_settings_for_display(); + + if ( ! $settings['product_id'] ) { + global $product; + + $product = wc_get_product(); + + if ( empty( $product ) ) { + return; + } + + $product_id = $product->get_id(); + } else { + $product_id = absint( $settings['product_id'] ); + } + + $quantity = absint( $settings['quantity'] ); + + $url = 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) + ? wc_get_cart_url() + : get_permalink( $product_id ); + + return home_url() . '?add-to-cart=' . $product_id . '&quantity=' . $quantity . '&e-redirect=' . $url; + } +} diff --git a/modules/woocommerce/traits/product-id-trait.php b/modules/woocommerce/traits/product-id-trait.php new file mode 100644 index 0000000..5b04d22 --- /dev/null +++ b/modules/woocommerce/traits/product-id-trait.php @@ -0,0 +1,27 @@ +get_product_variation( $product_id ); + } + + $product = wc_get_product( $product_id ); + + if ( ! $product ) { + $product = wc_get_product(); + } + + return $product; + } + + public function get_product_variation( $product_id = false ) { + return wc_get_product( get_the_ID() ); + } +} diff --git a/modules/woocommerce/traits/products-trait.php b/modules/woocommerce/traits/products-trait.php new file mode 100644 index 0000000..7d4747f --- /dev/null +++ b/modules/woocommerce/traits/products-trait.php @@ -0,0 +1,205 @@ + [ + 'default' => 'product', + 'options' => [ + 'current_query' => esc_html__( 'Current Query', 'elementor-pro' ), + 'product' => esc_html__( 'Latest Products', 'elementor-pro' ), + 'sale' => esc_html__( 'Sale', 'elementor-pro' ), + 'featured' => esc_html__( 'Featured', 'elementor-pro' ), + 'by_id' => _x( 'Manual Selection', 'Posts Query Control', 'elementor-pro' ), + 'related_products' => esc_html__( 'Related Products', 'elementor-pro' ), + 'upsells' => esc_html__( 'Upsells', 'elementor-pro' ), + 'cross_sells' => esc_html__( 'Cross-Sells', 'elementor-pro' ), + ], + ], + 'orderby' => [ + 'default' => 'date', + 'options' => [ + 'date' => esc_html__( 'Date', 'elementor-pro' ), + 'title' => esc_html__( 'Title', 'elementor-pro' ), + 'price' => esc_html__( 'Price', 'elementor-pro' ), + 'popularity' => esc_html__( 'Popularity', 'elementor-pro' ), + 'rating' => esc_html__( 'Rating', 'elementor-pro' ), + 'rand' => esc_html__( 'Random', 'elementor-pro' ), + 'menu_order' => esc_html__( 'Menu Order', 'elementor-pro' ), + ], + ], + 'exclude' => [ + 'options' => [ + 'current_post' => esc_html__( 'Current Post', 'elementor-pro' ), + 'manual_selection' => esc_html__( 'Manual Selection', 'elementor-pro' ), + 'terms' => esc_html__( 'Term', 'elementor-pro' ), + ], + ], + 'include' => [ + 'options' => [ + 'terms' => esc_html__( 'Term', 'elementor-pro' ), + ], + ], + ]; + } + + private function init_query_settings( $name ) { + $this->product_query_group_control_name = $name; + $this->product_query_control_args = $this->get_query_control_args(); + $this->product_query_post_type_control_id = $this->get_query_post_type_control_id(); + } + + /** + * @return array + */ + private function get_query_control_args() { + $args = [ + 'name' => $this->product_query_group_control_name, + 'post_type' => 'product', + 'presets' => [ 'include', 'exclude', 'order' ], + 'fields_options' => $this->get_query_fields_options(), + 'exclude' => [ + 'posts_per_page', + 'exclude_authors', + 'authors', + 'offset', + 'related_fallback', + 'related_ids', + 'query_id', + 'avoid_duplicates', + 'ignore_sticky_posts', + ], + ]; + + $args['fields_options'] = array_merge( $args['fields_options'], $this->get_query_exclude_conditions() ); + + return $args; + } + + private function get_query_exclude_conditions() { + $fields = []; + foreach ( $this->product_query_controls_to_hide as $control_name ) { + $fields = $this->add_query_not_supported_types( $control_name, $fields ); + } + + return $fields; + } + + private function add_query_not_supported_types( $control_name, $fields ) { + foreach ( $this->product_query_types as $query_type ) { + $fields[ $control_name ]['condition']['post_type!'][] = $query_type; + } + + return $fields; + } + + /** + * @return string + */ + private function get_query_post_type_control_id() { + $control_id = $this->product_query_control_args['name'] . '_post_type'; + + // Check if the trait is currently being used by a widget or skin. Group controls add + // the post_type as a prefix when added by a skin. + if ( method_exists( $this, 'get_control_id' ) ) { + $control_id = $this->product_query_control_args['post_type'] . '_' . $control_id; + } + + return $control_id; + } + + private function add_query_controls( $name ) { + $this->init_query_settings( $name ); + + $this->add_group_control( + Group_Control_Query::get_type(), + $this->product_query_control_args + ); + + $this->add_control( + 'related_products_note', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'Note: The Related Products Query is available when creating a Single Product template', 'elementor-pro' ), + 'condition' => [ + $this->product_query_post_type_control_id => 'related_products', + ], + ] + ); + + $this->add_control( + 'upsells_products_note', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'Note: The Upsells Query is available when creating a Single Product template', 'elementor-pro' ), + 'condition' => [ + $this->product_query_post_type_control_id => 'upsells', + ], + ] + ); + + $this->add_control( + 'cross_sells_products_note', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'Note: The Cross-Sells Query is available when creating a Cart page', 'elementor-pro' ), + 'condition' => [ + $this->product_query_post_type_control_id => 'cross_sells', + ], + ] + ); + } +} diff --git a/modules/woocommerce/wc-templates/cart/mini-cart.php b/modules/woocommerce/wc-templates/cart/mini-cart.php new file mode 100644 index 0000000..d7038c5 --- /dev/null +++ b/modules/woocommerce/wc-templates/cart/mini-cart.php @@ -0,0 +1,101 @@ +exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_widget_cart_item_visible', true, $cart_item, $cart_item_key ) ); + + if ( ! $is_product_visible ) { + return; + } + + $product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key ); + $product_price = apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key ); + $product_permalink = apply_filters( 'woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink( $cart_item ) : '', $cart_item, $cart_item_key ); + ?> +
      + +
      + get_image(), $cart_item, $cart_item_key ); + + if ( ! $product_permalink ) : + echo wp_kses_post( $thumbnail ); + else : + printf( '%s', esc_url( $product_permalink ), wp_kses_post( $thumbnail ) ); + endif; + ?> +
      + +
      + get_name(), $cart_item, $cart_item_key ) . ' ' ); + else : + echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', sprintf( '%s', esc_url( $product_permalink ), $_product->get_name() ), $cart_item, $cart_item_key ) ); + endif; + + do_action( 'woocommerce_after_cart_item_name', $cart_item, $cart_item_key ); + + // Meta data. + echo wc_get_formatted_cart_item_data( $cart_item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> +
      + +
      + ' . sprintf( '%s × %s', $cart_item['quantity'], $product_price ) . '', $cart_item, $cart_item_key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> +
      + +
      + ', + esc_url( wc_get_cart_remove_url( $cart_item_key ) ), + $class, + __( 'Remove this item', 'elementor-pro' ), + esc_attr( $product_id ), + esc_attr( $cart_item_key ), + esc_attr( $_product->get_sku() ) + ), $cart_item_key ); + } ?> +
      +
      + cart->get_cart(); + +if ( empty( $cart_items ) ) { ?> +
      + +
      + $cart_item ) { + elementor_pro_render_mini_cart_item( $cart_item_key, $cart_item ); + } + + do_action( 'woocommerce_mini_cart_contents' ); + ?> +
      + +
      + : + cart->get_cart_subtotal(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> +
      + + diff --git a/modules/woocommerce/widgets/add-to-cart.php b/modules/woocommerce/widgets/add-to-cart.php new file mode 100644 index 0000000..f9e0ea3 --- /dev/null +++ b/modules/woocommerce/widgets/add-to-cart.php @@ -0,0 +1,312 @@ +start_controls_section( + 'section_product', + [ + 'label' => esc_html__( 'Product', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'product_id', + [ + 'label' => esc_html__( 'Product', 'elementor-pro' ), + 'type' => Module::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'autocomplete' => [ + 'object' => Module::QUERY_OBJECT_POST, + 'query' => [ + 'post_type' => [ 'product' ], + ], + ], + ] + ); + + $this->add_control( + 'show_quantity', + [ + 'label' => esc_html__( 'Show Quantity', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'description' => esc_html__( 'Please note that switching on this option will disable some of the design controls.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'quantity', + [ + 'label' => esc_html__( 'Quantity', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 1, + 'condition' => [ + 'show_quantity' => '', + ], + ] + ); + + $this->end_controls_section(); + + parent::register_controls(); + + $this->start_controls_section( + 'section_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_control( + 'layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Inline', 'elementor-pro' ), + 'stacked' => esc_html__( 'Stacked', 'elementor-pro' ), + 'auto' => esc_html__( 'Auto', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-add-to-cart--layout-', + 'render_type' => 'template', + ] + ); + + $this->end_controls_section(); + + $this->update_control( + 'link', + [ + 'type' => Controls_Manager::HIDDEN, + 'default' => [ + 'url' => '', + ], + ] + ); + + $this->update_control( + 'text', + [ + 'default' => esc_html__( 'Add to Cart', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Add to Cart', 'elementor-pro' ), + ] + ); + + $this->update_responsive_control( + 'align', + [ + 'prefix_class' => 'elementor-add-to-cart%s--align-', + ] + ); + + $this->update_control( + 'selected_icon', + [ + 'default' => [ + 'value' => 'fas fa-shopping-cart', + 'library' => 'fa-solid', + ], + ] + ); + + $this->update_control( + 'size', + [ + 'condition' => [ + 'show_quantity' => '', + ], + ] + ); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + if ( ! empty( $settings['product_id'] ) ) { + $product_id = $settings['product_id']; + } elseif ( wp_doing_ajax() && ! empty( $settings['product_id'] ) ) { + // PHPCS - No nonce is required. + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $product_id = (int) Utils::_unstable_get_super_global_value( $_POST, 'post_id' ); + } else { + $product_id = get_queried_object_id(); + } + + global $product; + $product = $this->get_product( $product_id ); + + $settings = $this->get_settings_for_display(); + + if ( in_array( $settings['layout'], [ 'auto', 'stacked' ], true ) ) { + add_action( 'woocommerce_before_add_to_cart_quantity', [ $this, 'before_add_to_cart_quantity' ], 95 ); + add_action( 'woocommerce_after_add_to_cart_button', [ $this, 'after_add_to_cart_button' ], 5 ); + } + + if ( 'yes' === $settings['show_quantity'] ) { + $this->render_form_button( $product ); + } else { + $this->render_ajax_button( $product ); + } + + if ( in_array( $settings['layout'], [ 'auto', 'stacked' ], true ) ) { + remove_action( 'woocommerce_before_add_to_cart_quantity', [ $this, 'before_add_to_cart_quantity' ], 95 ); + remove_action( 'woocommerce_after_add_to_cart_button', [ $this, 'after_add_to_cart_button' ], 5 ); + } + } + + /** + * Before Add to Cart Quantity + * + * Added wrapper tag around the quantity input and "Add to Cart" button + * used to more solidly accommodate the layout when additional elements + * are added by 3rd party plugins. + * + * @since 3.6.0 + */ + public function before_add_to_cart_quantity() { + ?> +
      + +
      + get_settings_for_display(); + + if ( $product ) { + if ( version_compare( WC()->version, '3.0.0', '>=' ) ) { + $product_type = $product->get_type(); + } else { + $product_type = $product->product_type; + } + + $class = implode( ' ', array_filter( [ + 'product_type_' . $product_type, + $product->is_purchasable() && $product->is_in_stock() ? 'add_to_cart_button' : '', + $product->supports( 'ajax_add_to_cart' ) ? 'ajax_add_to_cart' : '', + ] ) ); + + $this->add_render_attribute( 'button', + [ + 'rel' => 'nofollow', + 'href' => $product->add_to_cart_url(), + 'data-quantity' => ( isset( $settings['quantity'] ) ? $settings['quantity'] : 1 ), + 'data-product_id' => $product->get_id(), + 'class' => $class, + ] + ); + + } elseif ( current_user_can( 'manage_options' ) ) { + $settings['text'] = esc_html__( 'Please set a valid product', 'elementor-pro' ); + $this->set_settings( $settings ); + } + + parent::render(); + } + + private function render_form_button( $product ) { + if ( ! $product && current_user_can( 'manage_options' ) ) { + echo esc_html__( 'Please set a valid product', 'elementor-pro' ); + + return; + } + + $text_callback = function() { + ob_start(); + $this->render_text(); + + return ob_get_clean(); + }; + + add_filter( 'woocommerce_get_stock_html', '__return_empty_string' ); + add_filter( 'woocommerce_product_single_add_to_cart_text', $text_callback ); + add_filter( 'esc_html', [ $this, 'unescape_html' ], 10, 2 ); + + ob_start(); + woocommerce_template_single_add_to_cart(); + $form = ob_get_clean(); + $form = str_replace( 'single_add_to_cart_button', 'single_add_to_cart_button elementor-button', $form ); + + // PHPCS - The HTML from 'woocommerce_template_single_add_to_cart' is safe. + echo $form; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + + remove_filter( 'woocommerce_product_single_add_to_cart_text', $text_callback ); + remove_filter( 'woocommerce_get_stock_html', '__return_empty_string' ); + remove_filter( 'esc_html', [ $this, 'unescape_html' ] ); + } + + /** + * Written as a Backbone JavaScript template and used to generate the live preview. + * + * @since 2.9.0 + * @access protected + */ + // Force remote render + protected function content_template() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/archive-description.php b/modules/woocommerce/widgets/archive-description.php new file mode 100644 index 0000000..d279260 --- /dev/null +++ b/modules/woocommerce/widgets/archive-description.php @@ -0,0 +1,115 @@ +start_controls_section( + 'section_product_description_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'text_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .term-description' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'text_typography', + 'selector' => '.woocommerce {{WRAPPER}} .term-description', + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + do_action( 'woocommerce_archive_description' ); + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/archive-products-deprecated.php b/modules/woocommerce/widgets/archive-products-deprecated.php new file mode 100644 index 0000000..220b894 --- /dev/null +++ b/modules/woocommerce/widgets/archive-products-deprecated.php @@ -0,0 +1,163 @@ +deprecated_notice( Plugin::get_title(), '2.5.0', '', esc_html__( 'Archive Products', 'elementor-pro' ) ); + + parent::register_controls(); + + $this->start_injection( [ + 'at' => 'before', + 'of' => 'columns', + ] ); + + $this->add_control( + 'wc_notice_do_not_use_customizer', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'Note that these layout settings will override settings made in Appearance > Customize', 'elementor-pro' ), + ] + ); + + $this->end_injection(); + + $this->update_control( + 'rows', + [ + 'default' => 4, + ], + [ + 'recursive' => true, + ] + ); + + $this->update_control( + 'paginate', + [ + 'default' => 'yes', + ] + ); + + $this->update_control( + 'section_query', + [ + 'type' => 'hidden', + ] + ); + + $this->update_control( + 'query_post_type', + [ + 'default' => 'current_query', + ] + ); + + $this->start_controls_section( + 'section_advanced', + [ + 'label' => esc_html__( 'Advanced', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'nothing_found_message', + [ + 'label' => esc_html__( 'Nothing Found Message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => esc_html__( 'It seems we can\'t find what you\'re looking for.', 'elementor-pro' ), + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_nothing_found_style', + [ + 'tab' => Controls_Manager::TAB_STYLE, + 'label' => esc_html__( 'Nothing Found Message', 'elementor-pro' ), + 'condition' => [ + 'nothing_found_message!' => '', + ], + ] + ); + + $this->add_control( + 'nothing_found_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-products-nothing-found' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'nothing_found_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-products-nothing-found', + ] + ); + + $this->end_controls_section(); + } + + public function render_no_results() { + echo '
      ' . esc_html( $this->get_settings( 'nothing_found_message' ) ) . '
      '; + } + + protected function render() { + add_action( 'woocommerce_shortcode_products_loop_no_results', [ $this, 'render_no_results' ] ); + + parent::render(); + } + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/archive-products.php b/modules/woocommerce/widgets/archive-products.php new file mode 100644 index 0000000..e612175 --- /dev/null +++ b/modules/woocommerce/widgets/archive-products.php @@ -0,0 +1,188 @@ +remove_responsive_control( 'columns' ); + $this->remove_responsive_control( 'rows' ); + $this->remove_control( 'orderby' ); + $this->remove_control( 'order' ); + + $this->update_control( + 'products_class', + [ + 'prefix_class' => 'elementor-products-grid elementor-', + ] + ); + + // Should be kept as hidden since required for "allow_order" + $this->update_control( + 'paginate', + [ + 'type' => 'hidden', + 'default' => 'yes', + ] + ); + + $this->update_control( + 'allow_order', + [ + 'default' => 'yes', + ] + ); + + $this->start_injection( [ + 'at' => 'before', + 'of' => 'allow_order', + ] ); + + if ( ! get_theme_support( 'woocommerce' ) ) { + $this->add_control( + 'wc_notice_wc_not_supported', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'warning', + 'content' => esc_html__( 'Looks like you are using WooCommerce, while your theme does not support it. Please consider switching themes.', 'elementor-pro' ), + ] + ); + } + + $this->add_control( + 'wc_notice_use_customizer', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'To change the Products Archive’s layout, go to Appearance > Customize.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'wc_notice_wrong_data', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The editor preview might look different from the live site. Please make sure to check the frontend.', 'elementor-pro' ), + ] + ); + + $this->end_injection(); + + $this->update_control( + 'show_result_count', + [ + 'default' => 'yes', + ] + ); + + $this->update_control( + 'section_query', + [ + 'type' => 'hidden', + ] + ); + + $this->update_control( + Products_Renderer::QUERY_CONTROL_NAME . '_post_type', + [ + 'default' => 'current_query', + ] + ); + + $this->start_controls_section( + 'section_advanced', + [ + 'label' => esc_html__( 'Advanced', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'nothing_found_message', + [ + 'label' => esc_html__( 'Nothing Found Message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXTAREA, + 'default' => esc_html__( 'It seems we can\'t find what you\'re looking for.', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_nothing_found_style', + [ + 'tab' => Controls_Manager::TAB_STYLE, + 'label' => esc_html__( 'Nothing Found Message', 'elementor-pro' ), + 'condition' => [ + 'nothing_found_message!' => '', + ], + ] + ); + + $this->add_control( + 'nothing_found_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-products-nothing-found' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'nothing_found_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .elementor-products-nothing-found', + ] + ); + + $this->end_controls_section(); + } + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/base-widget.php b/modules/woocommerce/widgets/base-widget.php new file mode 100644 index 0000000..7199879 --- /dev/null +++ b/modules/woocommerce/widgets/base-widget.php @@ -0,0 +1,153 @@ + $breakpoint_config ) { + $devices_required[ $breakpoint_name ] = [ + 'required' => false, + ]; + } + + return $devices_required; + } + + protected function add_columns_responsive_control() { + $this->add_responsive_control( + 'columns', + [ + 'label' => esc_html__( 'Columns', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'prefix_class' => 'elementor-grid%s-', + 'min' => 1, + 'max' => 12, + 'default' => Products_Renderer::DEFAULT_COLUMNS_AND_ROWS, + 'tablet_default' => '3', + 'mobile_default' => '2', + 'required' => true, + 'device_args' => $this->get_devices_default_args(), + 'min_affected_device' => [ + Controls_Stack::RESPONSIVE_DESKTOP => Controls_Stack::RESPONSIVE_TABLET, + Controls_Stack::RESPONSIVE_TABLET => Controls_Stack::RESPONSIVE_TABLET, + ], + ] + ); + } + + /** + * Is WooCommerce Feature Active. + * + * Checks whether a specific WooCommerce feature is active. These checks can sometimes look at multiple WooCommerce + * settings at once so this simplifies and centralizes the checking. + * + * @since 3.5.0 + * + * @param string $feature + * @return bool + */ + protected function is_wc_feature_active( $feature ) { + switch ( $feature ) { + case 'checkout_login_reminder': + return 'yes' === get_option( 'woocommerce_enable_checkout_login_reminder' ); + case 'shipping': + if ( class_exists( 'WC_Shipping_Zones' ) ) { + $all_zones = \WC_Shipping_Zones::get_zones(); + if ( count( $all_zones ) > 0 ) { + return true; + } + } + break; + case 'coupons': + return function_exists( 'wc_coupons_enabled' ) && wc_coupons_enabled(); + case 'signup_and_login_from_checkout': + return 'yes' === get_option( 'woocommerce_enable_signup_and_login_from_checkout' ); + case 'ship_to_billing_address_only': + return wc_ship_to_billing_address_only(); + } + + return false; + } + + /** + * Get Custom Border Type Options + * + * Return a set of border options to be used in different WooCommerce widgets. + * + * This will be used in cases where the Group Border Control could not be used. + * + * @since 3.5.0 + * + * @return array + */ + public static function get_custom_border_type_options() { + return [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'solid' => esc_html__( 'Solid', 'elementor-pro' ), + 'double' => esc_html__( 'Double', 'elementor-pro' ), + 'dotted' => esc_html__( 'Dotted', 'elementor-pro' ), + 'dashed' => esc_html__( 'Dashed', 'elementor-pro' ), + 'groove' => esc_html__( 'Groove', 'elementor-pro' ), + ]; + } + + /** + * Init Gettext Modifications + * + * Should be overridden by a method in the Widget class. + * + * @since 3.5.0 + */ + protected function init_gettext_modifications() { + $this->gettext_modifications = []; + } + + /** + * Filter Gettext. + * + * Filter runs when text is output to the page using the translation functions (`_e()`, `__()`, etc.) + * used to apply text changes from the widget settings. + * + * This allows us to make text changes without having to ovveride WooCommerce templates, which would + * lead to dev tax to keep all the templates up to date with each future WC release. + * + * @since 3.5.0 + * + * @param string $translation + * @param string $text + * @param string $domain + * @return string + */ + public function filter_gettext( $translation, $text, $domain ) { + if ( 'woocommerce' !== $domain && 'elementor-pro' !== $domain ) { + return $translation; + } + + if ( ! isset( $this->gettext_modifications ) ) { + $this->init_gettext_modifications(); + } + + return array_key_exists( $text, $this->gettext_modifications ) ? $this->gettext_modifications[ $text ] : $translation; + } +} diff --git a/modules/woocommerce/widgets/breadcrumb.php b/modules/woocommerce/widgets/breadcrumb.php new file mode 100644 index 0000000..7bcb28d --- /dev/null +++ b/modules/woocommerce/widgets/breadcrumb.php @@ -0,0 +1,120 @@ +start_controls_section( + 'section_product_rating_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-breadcrumb' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'link_color', + [ + 'label' => esc_html__( 'Link Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-breadcrumb > a' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'text_typography', + 'selector' => '{{WRAPPER}} .woocommerce-breadcrumb', + ] + ); + + $this->add_responsive_control( + 'alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-breadcrumb' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + woocommerce_breadcrumb(); + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/cart.php b/modules/woocommerce/widgets/cart.php new file mode 100644 index 0000000..08bc05f --- /dev/null +++ b/modules/woocommerce/widgets/cart.php @@ -0,0 +1,2661 @@ +start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'General', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'cart_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'two-column' => esc_html__( 'Two columns', 'elementor-pro' ), + 'one-column' => esc_html__( 'One column', 'elementor-pro' ), + ], + 'default' => 'two-column', + 'prefix_class' => 'e-cart-layout-', + ] + ); + + $this->add_control( + 'sticky_right_column', + [ + 'label' => esc_html__( 'Sticky Right Column', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'description' => esc_html__( 'This option will allow the right column (e.g, Cart Totals) to be sticky while scrolling.', 'elementor-pro' ), + 'frontend_available' => true, + 'render_type' => 'none', + 'condition' => [ + 'cart_layout!' => 'one-column', + ], + ] + ); + + $this->add_control( + 'sticky_right_column_offset', + [ + 'label' => esc_html__( 'Offset', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'frontend_available' => true, + 'conditions' => [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'sticky_right_column', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'cart_layout', + 'operator' => '!==', + 'value' => 'one-column', + ], + ], + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_order_summary', + [ + 'label' => esc_html__( 'Order Summary', 'elementor-pro' ), + 'condition' => [ + 'update_cart_automatically' => '', + ], + ] + ); + + $this->add_control( + 'update_cart_button_heading', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Update Cart Button', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'update_cart_button_text', + [ + 'label' => esc_html__( 'Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'Update Cart', 'elementor-pro' ), + 'default' => esc_html__( 'Update Cart', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'update_cart_button_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justify', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-cart-form' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => '--update-cart-button-alignment: start; --update-cart-button-width: auto;', + 'center' => '--update-cart-button-alignment: center; --update-cart-button-width: auto;', + 'end' => '--update-cart-button-alignment: end; --update-cart-button-width: auto;', + 'justify' => '--update-cart-button-alignment: justify; --update-cart-button-width: 100%;', + ], + ] + ); + + $this->end_controls_section(); + + if ( $this->is_wc_feature_active( 'coupons' ) ) { + + $this->start_controls_section( + 'section_coupon', + [ + 'label' => esc_html__( 'Coupon', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'section_coupon_display', + [ + 'label' => esc_html__( 'Coupon', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'apply_coupon_heading', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Apply Coupon Button', 'elementor-pro' ), + 'condition' => [ + 'section_coupon_display' => 'yes', + ], + ] + ); + + $this->add_control( + 'apply_coupon_button_text', + [ + 'label' => esc_html__( 'Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'Apply coupon', 'elementor-pro' ), + 'default' => esc_html__( 'Apply coupon', 'elementor-pro' ), + 'condition' => [ + 'section_coupon_display' => 'yes', + ], + ] + ); + + $this->add_responsive_control( + 'apply_coupon_button_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justify', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .coupon' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => '--apply-coupon-button-alignment: start; --apply-coupon-button-width: auto;', + 'center' => '--apply-coupon-button-alignment: center; --apply-coupon-button-width: auto;', + 'end' => '--apply-coupon-button-alignment: end; --apply-coupon-button-width: auto;', + 'justify' => '--apply-coupon-button-alignment: center; --apply-coupon-button-width: 100%;', + ], + 'condition' => [ + 'section_coupon_display' => 'yes', + ], + ] + ); + + $this->add_control( + 'coupon_button_alignment_note', + [ + 'raw' => esc_html__( 'Note: This control will only affect screen sizes Tablet and below', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'section_coupon_display' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + } + + $this->start_controls_section( + 'section_totals', + [ + 'label' => esc_html__( 'Totals', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'totals_section_title', + [ + 'label' => esc_html__( 'Section Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'Cart Totals', 'elementor-pro' ), + 'default' => esc_html__( 'Cart Totals', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'totals_section_title_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--totals-title-alignment: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'update_shipping_button_heading', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Update Shipping Button', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'update_shipping_button_text', + [ + 'label' => esc_html__( 'Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'Update', 'elementor-pro' ), + 'default' => esc_html__( 'Update', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'update_shipping_button_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justify', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .shipping-calculator-form' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => '--update-shipping-button-alignment: start; --update-shipping-button-width: auto;', + 'center' => '--update-shipping-button-alignment: center; --update-shipping-button-width: auto;', + 'end' => '--update-shipping-button-alignment: end; --update-shipping-button-width: auto;', + 'justify' => '--update-shipping-button-alignment: center; --update-shipping-button-width: 100%;', + ], + ] + ); + + $this->add_control( + 'checkout_button_heading', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Checkout Button', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'checkout_button_text', + [ + 'label' => esc_html__( 'Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'placeholder' => esc_html__( 'Proceed to Checkout', 'elementor-pro' ), + 'default' => esc_html__( 'Proceed to Checkout', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'checkout_button_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justify', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .wc-proceed-to-checkout' => '{{VALUE}};', + ], + 'selectors_dictionary' => [ + 'start' => '--place-order-title-alignment: flex-start; --checkout-button-width: fit-content;', + 'center' => '--place-order-title-alignment: center; --checkout-button-width: fit-content;', + 'end' => '--place-order-title-alignment: flex-end; --checkout-button-width: fit-content;', + 'justify' => '--place-order-title-alignment: stretch; --checkout-button-width: 100%;', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_additional_options', + [ + 'label' => esc_html__( 'Additional Options', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'update_cart_automatically', + [ + 'label' => esc_html__( 'Update Cart Automatically', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}};', + ], + 'selectors_dictionary' => [ + 'yes' => '--update-cart-automatically-display: none;', + ], + 'frontend_available' => true, + 'render_type' => 'template', + ] + ); + + $this->add_control( + 'update_cart_automatically_description', + [ + 'raw' => esc_html__( 'Changes to the cart will update automatically.', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + ] + ); + + $this->add_control( + 'additional_template_switch', + [ + 'label' => esc_html__( 'Customize empty cart', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'return_value' => 'active', + 'default' => '', + 'render_type' => 'template', + 'prefix_class' => 'e-cart-empty-template-', + ] + ); + + $this->add_control( + 'additional_template_description', + [ + 'raw' => sprintf( + /* translators: 1: Saved templates link opening tag, 2: Link closing tag. */ + esc_html__( 'Replaces the default WooCommerce Empty Cart screen with a custom template. (Don’t have one? Head over to %1$sSaved Templates%2$s)', 'elementor-pro' ), + sprintf( '', admin_url( 'edit.php?post_type=elementor_library&tabs_group=library#add_new' ) ), + '' + ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor elementor-descriptor-subtle', + 'condition' => [ + 'additional_template_switch' => 'active', + ], + ] + ); + + $this->add_control( + 'additional_template_select_heading', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Choose template', 'elementor-pro' ), + 'condition' => [ + 'additional_template_switch' => 'active', + ], + ] + ); + + $document_types = Plugin::elementor()->documents->get_document_types( [ + 'show_in_library' => true, + ] ); + + $this->add_control( + 'additional_template_select', + [ + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'label_block' => true, + 'show_label' => false, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_LIBRARY_TEMPLATE, + 'query' => [ + 'meta_query' => [ + [ + 'key' => Document::TYPE_META_KEY, + 'value' => array_keys( $document_types ), + 'compare' => 'IN', + ], + ], + ], + ], + 'frontend_available' => true, + 'condition' => [ + 'additional_template_switch' => 'active', + ], + 'render_type' => 'template', + ] + ); + + $this->add_control( + 'edit_button', + [ + 'raw' => sprintf( ' %s', esc_html__( 'Edit Template', 'elementor-pro' ) ), + 'type' => \Elementor\Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-edit-template-wrapper', + 'condition' => [ + 'additional_template_switch' => 'active', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_cart_tabs_style', + [ + 'label' => esc_html__( 'Sections', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'sections_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'section_normal_box_shadow', + 'selector' => '{{WRAPPER}} .e-cart-section', + ] + ); + + $this->add_control( + 'sections_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'sections_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-cart-section' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'sections_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'sections_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-color: {{VALUE}};', + ], + 'condition' => [ + 'sections_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'sections_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'sections_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'sections_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-margin: {{BOTTOM}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_cart_tabs_typography', + [ + 'label' => esc_html__( 'Typography', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'sections_typography', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Titles', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'sections_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sections_titles_typography', + 'selector' => '{{WRAPPER}} .cart_totals h2', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'sections_titles_text_shadow', + 'selector' => '{{WRAPPER}} .cart_totals h2', + ] + ); + + $this->add_responsive_control( + 'sections_title_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-title-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'sections_descriptions_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Descriptions', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'sections_descriptions_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} ' => '--sections-descriptions-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sections_descriptions_typography', + 'selector' => '{{WRAPPER}} .e-cart-content, {{WRAPPER}} .woocommerce-shipping-destination, {{WRAPPER}} .shipping-calculator-button', + ] + ); + + $this->add_responsive_control( + 'sections_descriptions_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-descriptions-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'sections_links_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Links', 'elementor-pro' ), + ] + ); + + $this->start_controls_tabs( 'links_colors' ); + + $this->start_controls_tab( 'links_normal_colors', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'links_normal_color', + [ + 'label' => esc_html__( 'Link Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--links-normal-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'links_hover_colors', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'links_hover_color', + [ + 'label' => esc_html__( 'Link Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--links-hover-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'sections_radio_buttons_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Radio Buttons', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sections_radio_buttons_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-radio-buttons-color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sections_radio_buttons_typography', + 'selector' => '{{WRAPPER}} #shipping_method li label', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_cart_tabs_forms', + [ + 'label' => esc_html__( 'Forms', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'forms_rows_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-rows-gap: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'forms_field_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Field', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'forms_field_typography', + 'selector' => '{{WRAPPER}} .coupon .input-text, {{WRAPPER}} .cart-collaterals .input-text, {{WRAPPER}} select, {{WRAPPER}} .select2-selection--single', + ] + ); + + $this->start_controls_tabs( 'forms_fields_styles' ); + + $this->start_controls_tab( 'forms_fields_normal_styles', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'forms_fields_normal_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-normal-color: {{VALUE}};', + '.e-woo-select2-wrapper .select2-results__option' => 'color: {{VALUE}};', + // style select2 arrow + '{{WRAPPER}} .select2-container--default .select2-selection--single .select2-selection__arrow b' => 'border-color: {{VALUE}} transparent transparent transparent;', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_fields_normal_background', + 'selector' => '{{WRAPPER}} .coupon .input-text, {{WRAPPER}} .e-cart-totals .input-text, {{WRAPPER}} select, {{WRAPPER}} .select2-selection--single', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_fields_normal_box_shadow', + 'selector' => '{{WRAPPER}} .coupon .input-text, {{WRAPPER}} .e-cart-totals .input-text, {{WRAPPER}} select, {{WRAPPER}} .select2-selection--single', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'forms_fields_focus_styles', [ 'label' => esc_html__( 'Focus', 'elementor-pro' ) ] ); + + $this->add_control( + 'forms_fields_focus_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-focus-color: {{VALUE}}', + '.e-woo-select2-wrapper .select2-results__option:focus' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_fields_focus_background', + 'selector' => '{{WRAPPER}} .coupon .input-text:focus, {{WRAPPER}} .e-cart-totals .input-text:focus, {{WRAPPER}} select:focus, {{WRAPPER}} .select2-selection--single:focus', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_fields_focus_box_shadow', + 'selector' => '{{WRAPPER}} .coupon .input-text:focus, {{WRAPPER}} .e-cart-totals .input-text:focus, {{WRAPPER}} select:focus, {{WRAPPER}} .select2-selection--single:focus', + ] + ); + + $this->add_control( + 'forms_fields_focus_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-focus-border-color: {{VALUE}}', + ], + 'condition' => [ + 'forms_fields_border_border!' => '', + ], + ] + ); + + $this->add_control( + 'forms_fields_focus_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-focus-transition-duration: {{SIZE}}ms', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'forms_fields_border', + 'selector' => '{{WRAPPER}} .coupon .input-text, {{WRAPPER}} .cart-collaterals .input-text, {{WRAPPER}} select, {{WRAPPER}} .select2-selection--single', + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'forms_fields_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'forms_fields_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} ' => '--forms-fields-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + // style select2 + '{{WRAPPER}} .select2-container--default .select2-selection--single .select2-selection__rendered' => 'line-height: calc( ({{TOP}}{{UNIT}}*2) + 16px ); padding-left: {{LEFT}}{{UNIT}}; padding-right: {{RIGHT}}{{UNIT}};', + '{{WRAPPER}} .select2-container--default .select2-selection--single .select2-selection__arrow' => 'height: calc( ({{TOP}}{{UNIT}}*2) + 16px ); right: {{RIGHT}}{{UNIT}};', + '{{WRAPPER}} .select2-container--default .select2-selection--single' => 'height: auto;', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'forms_button_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Buttons', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'forms_button_typography', + 'selector' => '{{WRAPPER}} .shop_table .button', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'forms_button_text_shadow', + 'selector' => '{{WRAPPER}} .shop_table .button', + ] + ); + + $this->start_controls_tabs( 'forms_buttons_styles' ); + + $this->start_controls_tab( 'forms_buttons_normal_styles', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'forms_buttons_normal_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-normal-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_buttons_normal_background', + 'selector' => '{{WRAPPER}} .shop_table .button', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_buttons_normal_box_shadow', + 'selector' => '{{WRAPPER}} .shop_table .button', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'forms_buttons_hover_styles', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'forms_buttons_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-hover-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_buttons_hover_background', + 'selector' => '{{WRAPPER}} .shop_table .button:hover, {{WRAPPER}} .shop_table .button:disabled[disabled]:hover', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_buttons_focus_box_shadow', + 'selector' => '{{WRAPPER}} .shop_table .button:hover', + ] + ); + + $this->add_control( + 'forms_buttons_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-hover-border-color: {{VALUE}}', + ], + 'condition' => [ + 'forms_buttons_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'forms_buttons_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-hover-transition-duration: {{SIZE}}ms', + ], + ] + ); + + $this->add_control( + 'forms_buttons_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + 'frontend_available' => true, + 'render_type' => 'template', + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'forms_buttons_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'forms_buttons_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .shop_table .button' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'forms_buttons_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'forms_buttons_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-border-color: {{VALUE}};', + ], + 'condition' => [ + 'forms_buttons_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'forms_buttons_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'forms_buttons_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}; --forms-buttons-width: auto;', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'tabs_order_summary', + [ + 'label' => esc_html__( 'Order Summary', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'order_summary_rows_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-rows-gap-top: calc( {{SIZE}}{{UNIT}}/2 ); --order-summary-rows-gap-bottom: calc( {{SIZE}}{{UNIT}}/2 );', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'order_summary_titles_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Titles', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-cart-form' => '--order-summary-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_summary_title_typography', + 'selector' => '{{WRAPPER}} .e-shop-table .cart th, {{WRAPPER}} .e-shop-table .cart td:before', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'order_summary_title_text_shadow', + 'selector' => '{{WRAPPER}} .e-shop-table .cart th, {{WRAPPER}} .e-shop-table .cart td:before', + ] + ); + + $this->add_responsive_control( + 'order_summary_title_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-title-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'order_summary_items_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Items', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_summary_items_typography', + 'selector' => '{{WRAPPER}} .cart td span, {{WRAPPER}} .cart td, {{WRAPPER}} .input-text.qty', + ] + ); + + $this->add_control( + 'order_summary_variations_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Variations', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_variations_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-variations-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_summary_variations_typography', + 'selector' => '{{WRAPPER}} .product-name .variation', + ] + ); + + $this->add_control( + 'order_summary_product_link_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Product Link', 'elementor-pro' ), + ] + ); + + $this->start_controls_tabs( 'order_summary' ); + + $this->start_controls_tab( 'order_summary_normal', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'product_link_normal_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--product-link-normal-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'order_summary_hover', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'product_link_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--product-link-hover-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'order_summary_divider_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Dividers', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'order_summary_items_divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-items-divider-color: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'order_summary_items_divider_weight', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-items-divider-weight: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'order_summary_quantity_border_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Quantity Borders', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'order_summary_quantity_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-quantity-border-color: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'order_summary_quantity_border_weight', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-quantity-border-weight: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'order_summary_remove_icon_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Remove icon', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->start_controls_tabs( 'order_summary_remove_icon' ); + + $this->start_controls_tab( 'order_summary_remove_icon_normal', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'order_summary_remove_icon_normal_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-remove-icon-normal-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'order_summary_remove_icon_hover', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'order_summary_remove_icon_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-remove-icon-hover-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_cart_totals', + [ + 'label' => esc_html__( 'Totals', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'totals_rows_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--totals-rows-gap-top: calc( {{SIZE}}{{UNIT}}/2 ); --totals-rows-gap-bottom: calc( {{SIZE}}{{UNIT}}/2 );', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'totals_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Titles & Totals', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'totals_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--totals-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'totals_typography', + 'selector' => '{{WRAPPER}} .cart_totals .shop_table td:before, {{WRAPPER}} .cart_totals .shop_table td .woocommerce-Price-amount', + ] + ); + + $this->add_control( + 'totals_divider_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Divider Total', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'totals_divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--totals-divider-color: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'totals_divider_weight', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--totals-divider-weight: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_cart_tabs_checkout_button', + [ + 'label' => esc_html__( 'Checkout Button', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'checkout_button_typography', + 'selector' => '{{WRAPPER}} .checkout-button', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'checkout_button_text_shadow', + 'selector' => '{{WRAPPER}} .checkout-button', + ] + ); + + $this->start_controls_tabs( 'checkout_button_styles' ); + + $this->start_controls_tab( 'checkout_button_normal_styles', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'checkout_button_normal_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--checkout-button-normal-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'checkout_button_normal_background', + 'selector' => '{{WRAPPER}} .woocommerce .wc-proceed-to-checkout .checkout-button', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'checkout_button_normal_box_shadow', + 'selector' => '{{WRAPPER}} .checkout-button', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'checkout_button_hover_styles', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'checkout_button_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--checkout-button-hover-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'checkout_button_hover_background', + 'selector' => '{{WRAPPER}} .woocommerce .wc-proceed-to-checkout .checkout-button:hover', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'checkout_button_hover_box_shadow', + 'selector' => '{{WRAPPER}} .checkout-button:hover', + ] + ); + + $this->add_control( + 'checkout_button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--checkout-button-hover-border-color: {{VALUE}}', + ], + 'condition' => [ + 'checkout_button_border_border!' => '', + ], + ] + ); + + $this->add_control( + 'checkout_button_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--checkout-button-hover-transition-duration: {{SIZE}}ms', + ], + ] + ); + + $this->add_control( + 'checkout_button_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + 'frontend_available' => true, + 'render_type' => 'template', + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'checkout_button_border', + 'selector' => '{{WRAPPER}} .checkout-button', + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'checkout_button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--checkout-button-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'checkout_button_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--checkout-button-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}; --checkout-button-width: fit-content;', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_cart_tabs_customize', + [ + 'label' => esc_html__( 'Customize', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $customize_options = []; + + $customize_options += [ + 'customize_order_summary' => esc_html__( 'Order Summary', 'elementor-pro' ), + ]; + + if ( $this->is_wc_feature_active( 'coupons' ) ) { + $customize_options += [ + 'customize_coupon' => esc_html__( 'Coupon', 'elementor-pro' ), + ]; + } + + $customize_options += [ + 'customize_totals' => esc_html__( 'Totals', 'elementor-pro' ), + ]; + + $this->add_control( + 'section_cart_show_customize_elements', + [ + 'label' => esc_html__( 'Select sections of the cart to customize:', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'options' => $customize_options, + 'render_type' => 'ui', + 'label_block' => true, + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_cart_tabs_customize_order_summary', + [ + 'label' => esc_html__( 'Customize: Order Summary', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'section_cart_show_customize_elements' => 'customize_order_summary', + ], + ] + ); + + $this->add_control( + 'order_summary_section_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Section', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_section_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-shop-table' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'order_summary_section_normal_box_shadow', + 'selector' => '{{WRAPPER}} .e-shop-table', + 'separator' => 'after', + ] + ); + + $this->add_control( + 'order_summary_section_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}} .e-shop-table' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'order_summary_section_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-shop-table' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'order_summary_section_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'order_summary_section_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-shop-table' => '--sections-border-color: {{VALUE}};', + ], + 'condition' => [ + 'order_summary_section_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'order_summary_section_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-shop-table' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'order_summary_section_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-shop-table' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'order_summary_section_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-shop-table' => '--sections-margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_cart_tabs_customize_totals', + [ + 'label' => esc_html__( 'Customize: Totals', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'section_cart_show_customize_elements' => 'customize_totals', + ], + ] + ); + + $this->add_control( + 'customize_totals_section_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Section', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'sections_totals_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-cart-totals' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'totals_section_normal_box_shadow', + 'selector' => '{{WRAPPER}} .e-cart-totals', + ] + ); + + $this->add_control( + 'totals_section_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}} .e-cart-totals' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'totals_section_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-cart-totals' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'totals_section_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'totals_section_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-cart-totals' => '--sections-border-color: {{VALUE}};', + ], + 'condition' => [ + 'totals_section_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'totals_section_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-cart-totals' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'checkout_sections_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-cart-totals' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'totals_section_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-cart-totals' => '--sections-margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'totals_section_titles_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Title', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'totals_section_titles_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .cart_totals' => '--sections-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'totals_section_titles_typography', + 'selector' => '{{WRAPPER}} .cart_totals h2', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'totals_section_titles_text_shadow', + 'selector' => '{{WRAPPER}} .cart_totals h2', + 'separator' => 'after', + ] + ); + + $this->add_control( + 'totals_section_content_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Description', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'totals_section_content_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-cart-totals' => '--sections-descriptions-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'totals_section_content_typography', + 'selector' => '{{WRAPPER}} .e-cart-totals .e-cart-content, {{WRAPPER}} .e-cart-totals .woocommerce-shipping-destination, {{WRAPPER}} .e-cart-totals .shipping-calculator-button', + ] + ); + + $this->add_control( + 'totals_section_link_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Link', 'elementor-pro' ), + ] + ); + + $this->start_controls_tabs( 'totals_section_links_colors' ); + + $this->start_controls_tab( 'totals_section_links_normal_colors', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'totals_section_links_normal_color', + [ + 'label' => esc_html__( 'Link Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-cart-totals' => '--links-normal-color: {{VALUE}} !important;', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'totals_section_links_hover_colors', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'totals_section_links_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-cart-totals' => '--links-hover-color: {{VALUE}} !important;', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_cart_tabs_customize_coupon', + [ + 'label' => esc_html__( 'Customize: Coupon', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'section_cart_show_customize_elements' => 'customize_coupon', + ], + ] + ); + + $this->add_control( + 'coupon_section_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Section', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'customize_coupon_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .coupon' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'customize_coupon_section_normal_box_shadow', + 'selector' => '{{WRAPPER}} .coupon', + ] + ); + + $this->add_control( + 'customize_coupon_section_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}} .coupon' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'customize_coupon_section_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .coupon' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'customize_coupon_section_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'customize_coupon_section_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .coupon' => '--sections-border-color: {{VALUE}};', + ], + 'condition' => [ + 'customize_coupon_section_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'customize_coupon_section_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .coupon' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'customize_coupon_section_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .coupon' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'customize_coupon_section_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .coupon' => '--sections-margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + '{{WRAPPER}} .e-cart__container' => 'grid-row-gap: {{BOTTOM}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->end_controls_section(); + } + + /** + * Init Gettext Modifications + * + * Sets the `$gettext_modifications` property used with the `filter_gettext()` in the extended Base_Widget. + * + * @since 3.5.0 + */ + protected function init_gettext_modifications() { + $instance = $this->get_settings_for_display(); + + $this->gettext_modifications = [ + 'Update cart' => isset( $instance['update_cart_button_text'] ) ? $instance['update_cart_button_text'] : '', + 'Cart totals' => isset( $instance['totals_section_title'] ) ? $instance['totals_section_title'] : '', + 'Proceed to checkout' => isset( $instance['checkout_button_text'] ) ? $instance['checkout_button_text'] : '', + 'Update' => isset( $instance['update_shipping_button_text'] ) ? $instance['update_shipping_button_text'] : '', + 'Apply coupon' => isset( $instance['apply_coupon_button_text'] ) ? $instance['apply_coupon_button_text'] : '', + ]; + } + + /** + * Check if an Elementor template has been selected to display the empty cart notification + * + * @since 3.7.0 + * @return boolean + */ + protected function has_empty_cart_template() { + $additional_template_select = $this->get_settings_for_display( 'additional_template_select' ); + return ! empty( $additional_template_select ) && 0 < $additional_template_select; + } + + /** + * Render Woocommerce Cart Coupon Form + * + * A custom function to render a coupon form on the Cart widget. The default WC coupon form + * was removed in this file's render() method. + * + * We are doing this in order to match the placement of the coupon form to the provided design. + * + * @since 3.5.0 + */ + private function render_woocommerce_cart_coupon_form() { + $settings = $this->get_settings_for_display(); + $button_classes = [ 'button', 'e-apply-coupon' ]; + if ( $settings['forms_buttons_hover_animation'] ) { + $button_classes[] = 'elementor-animation-' . $settings['forms_buttons_hover_animation']; + } + $this->add_render_attribute( + 'button_coupon', [ + 'class' => $button_classes, + 'name' => 'apply_coupon', + 'type' => 'submit', + ] + ); + ?> +
      +
      +
      + +
      +
      + +
      + +
      +
      + +
      + +
      + + get_settings_for_display(); + $coupon_display_control = true; + + if ( '' === $settings['section_coupon_display'] ) { + $coupon_display_control = false; + } + + return wc_coupons_enabled() && $coupon_display_control; + } + + /** + * Woocommerce Before Cart Table + * + * Output containing elements. Callback function for the woocommerce_before_cart_table hook + * + * This eliminates the need for template overrides. + * + * @since 3.5.0 + */ + public function woocommerce_before_cart_table() { + $section_classes = [ 'e-shop-table', 'e-cart-section' ]; + + if ( ! $this->should_render_coupon() ) { + $section_classes[] = 'e-cart-section--no-coupon'; + } + + $this->add_render_attribute( + 'before_cart_table', [ + 'class' => $section_classes, + ] + ); + ?> +
      print_render_attribute_string( 'before_cart_table' ); ?>> + + +
      + +
      + should_render_coupon() ) { + $this->render_woocommerce_cart_coupon_form(); + } + } + + /** + * Woocommerce Before Cart Collaterals + * + * Output containing elements. * Callback function for the woocommerce_before_cart_collaterals hook + * + * This eliminates the need for template overrides. + * + * @since 3.5.0 + */ + public function woocommerce_before_cart_collaterals() { + ?> + +
      +
      + +
      + +
      + + + +
      + +
      + +
      + +
      + modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.7.0' ); + + $url_components = wp_parse_url( $url ); + + if ( ! isset( $url_components['query'] ) ) { + return $url; + } + + $params = []; + parse_str( html_entity_decode( $url_components['query'] ), $params ); + + $params['_wp_http_referer'] = rawurlencode( Plugin::elementor()->documents->get_current()->get_wp_preview_url() ); + + return add_query_arg( $params, get_site_url() ); + } + + /** + * WooCommerce Get Cart Url + * + * Used with the `woocommerce_get_cart_url`. This sets the url to the current page, so links like the `remove_url` + * are set to the current page, and not the existing WooCommerce cart endpoint. + * + * @since 3.7.0 + * + * @param $url + * @return string + */ + public function woocommerce_get_cart_url( $url ) { + global $post; + + if ( ! $post ) { + return $url; + } + + if ( Module::is_preview() || Plugin::elementor()->editor->is_edit_mode() ) { + return Plugin::elementor()->documents->get_current()->get_wp_preview_url(); + } + + return get_permalink( $post->ID ); + } + + /** + * The following disabling of cart coupon needs to be done this way so that + * we only disable the display of coupon interface in our cart widget and + * `wc_coupons_enabled()` can still be reliably used elsewhere. + */ + public function disable_cart_coupon() { + add_filter( 'woocommerce_coupons_enabled', [ $this, 'cart_coupon_return_false' ], 90 ); + } + public function enable_cart_coupon() { + remove_filter( 'woocommerce_coupons_enabled', [ $this, 'cart_coupon_return_false' ], 90 ); + } + public function cart_coupon_return_false() { + return false; + } + + /** + * Add Render Hooks + * + * Add actions & filters before displaying our widget. + * + * @since 3.7.0 + */ + public function add_render_hooks() { + $is_editor = Plugin::elementor()->editor->is_edit_mode(); + $is_preview = Module::is_preview(); + + /** + * Add actions & filters before displaying our Widget. + */ + add_filter( 'gettext', [ $this, 'filter_gettext' ], 20, 3 ); + + add_action( 'woocommerce_before_cart', [ $this, 'woocommerce_before_cart' ] ); + add_action( 'woocommerce_after_cart_table', [ $this, 'woocommerce_after_cart_table' ] ); + add_action( 'woocommerce_before_cart_table', [ $this, 'woocommerce_before_cart_table' ] ); + add_action( 'woocommerce_before_cart_collaterals', [ $this, 'woocommerce_before_cart_collaterals' ] ); + add_action( 'woocommerce_after_cart', [ $this, 'woocommerce_after_cart' ] ); + // The following disabling of cart coupon needs to be done this way so that + // we only disable the display of coupon interface in our cart widget and + // `wc_coupons_enabled()` can still be reliably used elsewhere. + add_action( 'woocommerce_cart_contents', [ $this, 'disable_cart_coupon' ] ); + add_action( 'woocommerce_after_cart_contents', [ $this, 'enable_cart_coupon' ] ); + add_filter( 'woocommerce_get_cart_url', [ $this, 'woocommerce_get_cart_url' ] ); + + if ( $this->has_empty_cart_template() ) { + remove_action( 'woocommerce_cart_is_empty', 'wc_empty_cart_message', 10 ); + } + + // Remove cross-sells in cart. + remove_action( 'woocommerce_cart_collaterals', 'woocommerce_cross_sell_display' ); + } + + /** + * Remove Render Hooks + * + * Remove actions & filters after displaying our widget. + * + * @since 3.7.0 + */ + public function remove_render_hooks() { + remove_filter( 'gettext', [ $this, 'filter_gettext' ], 20 ); + + remove_action( 'woocommerce_before_cart', [ $this, 'woocommerce_before_cart' ] ); + remove_action( 'woocommerce_after_cart_table', [ $this, 'woocommerce_after_cart_table' ] ); + remove_action( 'woocommerce_before_cart_table', [ $this, 'woocommerce_before_cart_table' ] ); + remove_action( 'woocommerce_before_cart_collaterals', [ $this, 'woocommerce_before_cart_collaterals' ] ); + remove_action( 'woocommerce_after_cart', [ $this, 'woocommerce_after_cart' ] ); + remove_filter( 'woocommerce_coupons_enabled', [ $this, 'hide_coupon_field_on_cart' ] ); + remove_filter( 'woocommerce_get_remove_url', [ $this, 'woocommerce_get_remove_url' ] ); + remove_action( 'woocommerce_cart_contents', [ $this, 'disable_cart_coupon' ] ); + remove_action( 'woocommerce_after_cart_contents', [ $this, 'enable_cart_coupon' ] ); + remove_action( 'woocommerce_get_cart_url', [ $this, 'woocommerce_get_cart_url' ] ); + add_action( 'woocommerce_cart_collaterals', 'woocommerce_cross_sell_display' ); + + if ( $this->has_empty_cart_template() ) { + add_action( 'woocommerce_cart_is_empty', 'wc_empty_cart_message', 10 ); + } + } + + public function render() { + // Add actions & filters before displaying our Widget. + $this->add_render_hooks(); + + // Display our Widget. + if ( $this->has_empty_cart_template() && WC()->cart->get_cart_contents_count() === 0 ) { + $template_id = intval( $this->get_settings_for_display( 'additional_template_select' ) ); + echo do_shortcode( '[elementor-template id="' . $template_id . '"]' ); + } else { + echo do_shortcode( '[woocommerce_cart]' ); + } + + // Remove actions & filters after displaying our Widget. + $this->remove_render_hooks(); + } + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/categories.php b/modules/woocommerce/widgets/categories.php new file mode 100644 index 0000000..ba579cf --- /dev/null +++ b/modules/woocommerce/widgets/categories.php @@ -0,0 +1,408 @@ +start_controls_section( + 'section_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_columns_responsive_control(); + + $this->add_control( + 'number', + [ + 'label' => esc_html__( 'Categories Count', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => '4', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_filter', + [ + 'label' => esc_html__( 'Query', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_control( + 'source', + [ + 'label' => esc_html__( 'Source', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Show All', 'elementor-pro' ), + 'by_id' => esc_html__( 'Manual Selection', 'elementor-pro' ), + 'by_parent' => esc_html__( 'By Parent', 'elementor-pro' ), + 'current_subcategories' => esc_html__( 'Current Subcategories', 'elementor-pro' ), + ], + 'label_block' => true, + ] + ); + + $categories = get_terms( 'product_cat' ); + + $options = []; + foreach ( $categories as $category ) { + $options[ $category->term_id ] = $category->name; + } + + $this->add_control( + 'categories', + [ + 'label' => esc_html__( 'Categories', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'options' => $options, + 'default' => [], + 'label_block' => true, + 'multiple' => true, + 'condition' => [ + 'source' => 'by_id', + ], + ] + ); + + $parent_options = [ '0' => esc_html__( 'Only Top Level', 'elementor-pro' ) ] + $options; + $this->add_control( + 'parent', + [ + 'label' => esc_html__( 'Parent', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '0', + 'options' => $parent_options, + 'condition' => [ + 'source' => 'by_parent', + ], + ] + ); + + $this->add_control( + 'hide_empty', + [ + 'label' => esc_html__( 'Hide Empty', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'label_on' => 'Hide', + 'label_off' => 'Show', + ] + ); + + $this->add_control( + 'orderby', + [ + 'label' => esc_html__( 'Order By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'name', + 'options' => [ + 'name' => esc_html__( 'Name', 'elementor-pro' ), + 'slug' => esc_html__( 'Slug', 'elementor-pro' ), + 'description' => esc_html__( 'Description', 'elementor-pro' ), + 'count' => esc_html__( 'Count', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'order', + [ + 'label' => esc_html__( 'Order', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'desc', + 'options' => [ + 'asc' => esc_html__( 'ASC', 'elementor-pro' ), + 'desc' => esc_html__( 'DESC', 'elementor-pro' ), + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_products_style', + [ + 'label' => esc_html__( 'Products', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'products_class', + [ + 'type' => Controls_Manager::HIDDEN, + 'default' => 'wc-products', + 'prefix_class' => 'elementor-products-grid elementor-', + ] + ); + + $this->add_control( + 'column_gap', + [ + 'label' => esc_html__( 'Columns Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 20, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products' => 'grid-column-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'row_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 40, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products' => 'grid-row-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'prefix_class' => 'elementor-product-loop-item--align-', + 'selectors' => [ + '{{WRAPPER}} .product' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'heading_image_style', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'image_border', + 'selector' => '{{WRAPPER}} a > img', + ] + ); + + $this->add_responsive_control( + 'image_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} a > img' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'image_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} a > img' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'heading_title_style', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce .woocommerce-loop-category__title' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'title_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .woocommerce .woocommerce-loop-category__title', + ] + ); + + $this->add_control( + 'heading_count_style', + [ + 'label' => esc_html__( 'Count', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'count_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-loop-category__title .count' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'count_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .woocommerce-loop-category__title .count', + ] + ); + + $this->end_controls_section(); + } + + private function get_shortcode() { + $settings = $this->get_settings(); + + $attributes = [ + 'number' => $settings['number'], + 'columns' => $settings['columns'], + 'hide_empty' => ( 'yes' === $settings['hide_empty'] ) ? 1 : 0, + 'orderby' => $settings['orderby'], + 'order' => $settings['order'], + ]; + + if ( 'by_id' === $settings['source'] ) { + $attributes['ids'] = implode( ',', $settings['categories'] ); + } elseif ( 'by_parent' === $settings['source'] ) { + $attributes['parent'] = $settings['parent']; + } elseif ( 'current_subcategories' === $settings['source'] ) { + $attributes['parent'] = get_queried_object_id(); + } + + $this->add_render_attribute( 'shortcode', $attributes ); + + $shortcode = sprintf( '[product_categories %s]', $this->get_render_attribute_string( 'shortcode' ) ); + + return $shortcode; + } + + public function render() { + $product_categories_html = do_shortcode( $this->get_shortcode() ); + + if ( $product_categories_html ) { + $product_categories_html = str_replace( '
        get_shortcode() ); + } + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/category-image.php b/modules/woocommerce/widgets/category-image.php new file mode 100644 index 0000000..b1140d2 --- /dev/null +++ b/modules/woocommerce/widgets/category-image.php @@ -0,0 +1,68 @@ + 'image', + 'is_core_dependency' => true, + ], + ]; + } + + protected function register_controls() { + parent::register_controls(); + + $this->update_control( + 'image', + [ + 'dynamic' => [ + 'default' => Plugin::elementor()->dynamic_tags->tag_data_to_tag_text( null, 'woocommerce-category-image-tag' ), + ], + ], + [ + 'recursive' => true, + ] + ); + } + + protected function get_html_wrapper_class() { + return parent::get_html_wrapper_class() . ' elementor-widget-' . parent::get_name(); + } + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/checkout.php b/modules/woocommerce/widgets/checkout.php new file mode 100644 index 0000000..9032e39 --- /dev/null +++ b/modules/woocommerce/widgets/checkout.php @@ -0,0 +1,4338 @@ +start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'General', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'checkout_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'two-column' => esc_html__( 'Two columns', 'elementor-pro' ), + 'one-column' => esc_html__( 'One column', 'elementor-pro' ), + ], + 'default' => 'two-column', + 'prefix_class' => 'e-checkout-layout-', + ] + ); + + $this->add_control( + 'sticky_right_column', + [ + 'label' => esc_html__( 'Sticky Right Column', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'return_value' => 'yes', + 'description' => esc_html__( 'The Order Summary and Payment sections will remain in place while scrolling.', 'elementor-pro' ), + 'frontend_available' => true, + 'render_type' => 'none', + 'condition' => [ + 'checkout_layout' => 'two-column', + ], + ] + ); + + $this->add_control( + 'sticky_right_column_offset', + [ + 'label' => esc_html__( 'Offset', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'frontend_available' => true, + 'conditions' => [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'sticky_right_column', + 'operator' => '!==', + 'value' => '', + ], + [ + 'name' => 'checkout_layout', + 'operator' => '=', + 'value' => 'two-column', + ], + ], + ], + ] + ); + + $this->end_controls_section(); + + if ( $this->is_wc_feature_active( 'checkout_login_reminder' ) ) { + $this->add_checkout_login_reminder_controls(); + } + + $this->start_controls_section( + 'billing_details_section', + [ + 'label' => $this->is_wc_feature_active( 'ship_to_billing_address_only' ) ? esc_html__( 'Billing and Shipping Details', 'elementor-pro' ) : esc_html__( 'Billing Details', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'billing_details_section_title', + [ + 'label' => esc_html__( 'Section Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => $this->is_wc_feature_active( 'ship_to_billing_address_only' ) ? esc_html__( 'Billing and Shipping Details', 'elementor-pro' ) : esc_html__( 'Billing Details', 'elementor-pro' ), + 'default' => $this->is_wc_feature_active( 'ship_to_billing_address_only' ) ? esc_html__( 'Billing and Shipping Details', 'elementor-pro' ) : esc_html__( 'Billing Details', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_responsive_control( + 'billing_details_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--billing-details-title-alignment: {{VALUE}};', + ], + ] + ); + + $repeater = new Repeater(); + + $repeater->start_controls_tabs( 'tabs', [ + 'condition' => [ + 'repeater_state' => '', + ], + ] ); + + $repeater->start_controls_tab( 'content_tab', [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + ] ); + + $repeater->add_control( + 'label', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + ] + ); + + $repeater->add_control( + 'placeholder', + [ + 'label' => esc_html__( 'Placeholder', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + ] + ); + + $repeater->end_controls_tab(); + + $repeater->start_controls_tab( 'advanced_tab', [ + 'label' => esc_html__( 'Advanced', 'elementor-pro' ), + ] ); + + $repeater->add_control( + 'default', + [ + 'label' => esc_html__( 'Default Value', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->end_controls_tab(); + + $repeater->end_controls_tabs(); + + $repeater->add_control( + 'repeater_state', + [ + 'label' => esc_html__( 'Repeater State - hidden', 'elementor-pro' ), + 'type' => Controls_Manager::HIDDEN, + ] + ); + + $repeater->add_control( + 'locale_notice', + [ + 'raw' => __( 'Note: This content cannot be changed due to local regulations.', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'repeater_state' => 'locale', + ], + ] + ); + + $repeater->add_control( + 'from_billing_notice', + [ + 'raw' => __( 'Note: This label and placeholder are taken from the Billing section. You can change it there.', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'repeater_state' => 'from_billing', + ], + ] + ); + + $this->add_control( + 'billing_details_form_fields', + [ + 'label' => esc_html__( 'Form Items', 'elementor-pro' ), + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'item_actions' => [ + 'add' => false, + 'duplicate' => false, + 'remove' => false, + 'sort' => false, + ], + 'default' => $this->get_billing_field_defaults(), + 'title_field' => '{{{ label }}}', + ] + ); + + $this->end_controls_section(); + + if ( $this->is_wc_feature_active( 'shipping' ) && ! $this->is_wc_feature_active( 'ship_to_billing_address_only' ) ) { + $this->add_shipping_controls(); + } + + $this->start_controls_section( + 'additional_information_section', + [ + 'label' => esc_html__( 'Additional Information', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'additional_information_active', + [ + 'label' => esc_html__( 'Additional Information', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + 'selectors' => [ + '{{WRAPPER}}' => '--additional-information-display: block;', + ], + ] + ); + + if ( $this->is_wc_feature_active( 'ship_to_billing_address_only' ) ) { + $this->add_control( + 'additional_information_section_title', + [ + 'label' => esc_html__( 'Section Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'Additional Information', 'elementor-pro' ), + 'default' => esc_html__( 'Additional Information', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'additional_information_active!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'additional_information_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--additional-fields-title-alignment: {{VALUE}};', + ], + 'condition' => [ + 'additional_information_active!' => '', + ], + ] + ); + } + + $repeater = new Repeater(); + + $repeater->start_controls_tabs( 'additional_information_form_fields_tabs' ); + + $repeater->start_controls_tab( 'additional_information_form_fields_content_tab', [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + ] ); + + $repeater->add_control( + 'label', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'placeholder', + [ + 'label' => esc_html__( 'Placeholder', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->end_controls_tab(); + + $repeater->start_controls_tab( 'additional_information_form_fields_advanced_tab', [ + 'label' => esc_html__( 'Advanced', 'elementor-pro' ), + ] ); + + $repeater->add_control( + 'default', + [ + 'label' => esc_html__( 'Default Value', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->end_controls_tab(); + + $repeater->end_controls_tabs(); + + $this->add_control( + 'additional_information_form_fields', + [ + 'label' => esc_html__( 'Items', 'elementor-pro' ), + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'item_actions' => [ + 'add' => false, + 'duplicate' => false, + 'remove' => false, + 'sort' => false, + ], + 'default' => [ + [ + 'field_key' => 'order_comments', + 'field_label' => esc_html__( 'Order Notes', 'elementor-pro' ), + 'label' => esc_html__( 'Order Notes', 'elementor-pro' ), + 'placeholder' => esc_html__( 'Notes about your order, e.g. special notes for delivery.', 'elementor-pro' ), + ], + ], + 'title_field' => '{{{ label }}}', + 'condition' => [ + 'additional_information_active!' => '', + ], + ] + ); + + $this->end_controls_section(); + + if ( $this->is_wc_feature_active( 'signup_and_login_from_checkout' ) ) { + $this->add_signup_and_login_from_checkout_controls(); + } + + $this->start_controls_section( + 'order_summary_section', + [ + 'label' => esc_html__( 'Your Order', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_section_title', + [ + 'label' => esc_html__( 'Section Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Your Order', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_responsive_control( + 'order_summary_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--order-review-title-alignment: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + + if ( $this->is_wc_feature_active( 'coupons' ) ) { + $this->add_coupon_controls(); + } + + $this->start_controls_section( + 'payment_section', + [ + 'label' => esc_html__( 'Payment', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'terms_conditions_heading', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Terms & Conditions', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'terms_conditions_message_text', + [ + 'label' => esc_html__( 'Message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'label_block' => true, + 'default' => esc_html__( 'I have read and agree to the website', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'terms_conditions_link_text', + [ + 'label' => esc_html__( 'Link Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'label_block' => true, + 'default' => esc_html__( 'terms and conditions', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'purchase_buttom_heading', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Purchase Button', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'purchase_button_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justify', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => '--place-order-title-alignment: flex-start; --purchase-button-width: fit-content;', + 'center' => '--place-order-title-alignment: center; --purchase-button-width: fit-content;', + 'end' => '--place-order-title-alignment: flex-end; --purchase-button-width: fit-content;', + 'justify' => '--place-order-title-alignment: stretch; --purchase-button-width: 100%;', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_checkout_tabs_style', + [ + 'label' => esc_html__( 'Sections', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'sections_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'section_normal_box_shadow', + 'selector' => $this->get_main_woocommerce_sections_selectors(), + ] + ); + + $this->add_control( + 'sections_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'sections_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + $this->get_main_woocommerce_sections_selectors() => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'sections_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'sections_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-color: {{VALUE}};', + ], + 'condition' => [ + 'sections_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'sections_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'sections_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + // move the 'Ship to a different address?' checkbox + '{{WRAPPER}} .woocommerce-shipping-fields' => '--shipping-heading-padding-start: {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'sections_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_checkout_tabs_typography', + [ + 'label' => esc_html__( 'Typography', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'sections_typography', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Titles', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'sections_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sections_titles_typography', + 'selector' => $this->get_main_woocommerce_sections_title_selectors(), + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'sections_titles_text_shadow', + 'selector' => $this->get_main_woocommerce_sections_title_selectors(), + ] + ); + + $this->add_responsive_control( + 'sections_title_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-title-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'sections_secondary_typography', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Secondary Titles', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sections_secondary_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-secondary-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sections_secondary_titles_typography', + 'selector' => '{{WRAPPER}} .e-checkout-secondary-title', + ] + ); + + $this->add_responsive_control( + 'sections_secondary_title_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-secondary-title-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'sections_descriptions_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Descriptions', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sections_descriptions_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-descriptions-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sections_descriptions_typography', + 'selector' => '{{WRAPPER}} .e-description', + ] + ); + + $this->add_responsive_control( + 'sections_descriptions_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-descriptions-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'sections_messages_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Messages', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sections_messages_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-messages-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sections_messages_typography', + 'selector' => '{{WRAPPER}} .woocommerce-checkout #payment .payment_box, {{WRAPPER}} .woocommerce-privacy-policy-text p, {{WRAPPER}} .e-checkout-message', + ] + ); + + $this->add_control( + 'sections_checkboxes_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Checkboxes', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sections_checkboxes_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-checkboxes-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sections_checkboxes_typography', + 'selector' => '{{WRAPPER}} .woocommerce-form__label-for-checkbox span', + ] + ); + + $this->add_control( + 'sections_radio_buttons_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Radio Buttons', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sections_radio_buttons_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-radio-buttons-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sections_radio_buttons_typography', + 'selector' => '{{WRAPPER}} .wc_payment_method label, {{WRAPPER}} #shipping_method li label', + ] + ); + + // Links + $this->add_control( + 'sections_links_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Links', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->start_controls_tabs( 'links_colors' ); + + $this->start_controls_tab( 'links_normal_colors', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $this->add_control( + 'links_normal_color', + [ + 'label' => esc_html__( 'Link Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--links-normal-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'links_hover_colors', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] ); + + $this->add_control( + 'links_hover_color', + [ + 'label' => esc_html__( 'Link Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--links-hover-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_checkout_tabs_forms', + [ + 'label' => esc_html__( 'Forms', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'forms_columns_gap', + [ + 'label' => esc_html__( 'Columns Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-columns-gap-padding: calc( {{SIZE}}{{UNIT}}/2 ); --forms-columns-gap-margin: calc( -{{SIZE}}{{UNIT}}/2 );', + ], + ] + ); + + $this->add_responsive_control( + 'forms_rows_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-rows-gap: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'forms_label_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Labels', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'forms_label_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-labels-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'forms_label_typography', + 'selector' => '{{WRAPPER}} .woocommerce-billing-fields .form-row label, {{WRAPPER}} .woocommerce-shipping-fields .form-row label, {{WRAPPER}} .woocommerce-additional-fields .form-row label, {{WRAPPER}} .e-woocommerce-login-anchor .form-row label, {{WRAPPER}} .e-coupon-anchor-description', + ] + ); + + $this->add_responsive_control( + 'forms_label_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-label-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'forms_field_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Fields', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'forms_field_typography', + 'selector' => '{{WRAPPER}} #customer_details .input-text, {{WRAPPER}} #customer_details .form-row textarea, {{WRAPPER}} #customer_details .form-row select, {{WRAPPER}} .e-woocommerce-login-anchor .input-text, {{WRAPPER}} #coupon_code, {{WRAPPER}} ::placeholder, {{WRAPPER}} .select2-container--default .select2-selection--single, .select2-results__option', + ] + ); + + $this->start_controls_tabs( 'forms_fields_styles' ); + + $this->start_controls_tab( 'forms_fields_normal_styles', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $this->add_control( + 'forms_fields_normal_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-normal-color: {{VALUE}};', + '.e-woo-select2-wrapper .select2-results__option' => 'color: {{VALUE}};', + // style select2 arrow + '{{WRAPPER}} .select2-container--default .select2-selection--single .select2-selection__arrow b' => 'border-color: {{VALUE}} transparent transparent transparent;', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_fields_normal_background', + 'selector' => '{{WRAPPER}} .woocommerce #customer_details .form-row .input-text, {{WRAPPER}} .woocommerce #customer_details .form-row textarea, {{WRAPPER}} .woocommerce form #customer_details select, {{WRAPPER}} .woocommerce .e-woocommerce-login-anchor .form-row .input-text, {{WRAPPER}} #coupon_code, {{WRAPPER}} .select2-container--default .select2-selection--single, {{WRAPPER}} .woocommerce-checkout #payment .payment_methods .payment_box', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_fields_normal_box_shadow', + 'selector' => '{{WRAPPER}} #customer_details .input-text, {{WRAPPER}} #customer_details .form-row textarea, {{WRAPPER}} .woocommerce form #customer_details select, {{WRAPPER}} .e-woocommerce-login-anchor .input-text, {{WRAPPER}} #coupon_code, {{WRAPPER}} .select2-container--default .select2-selection--single', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'forms_fields_focus_styles', [ + 'label' => esc_html__( 'Focus', 'elementor-pro' ), + ] ); + + $this->add_control( + 'forms_fields_focus_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-focus-color: {{VALUE}}', + '.e-woo-select2-wrapper .select2-results__option:focus' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_fields_focus_background', + 'selector' => '{{WRAPPER}} .woocommerce #customer_details .form-row .input-text:focus, {{WRAPPER}} .woocommerce #customer_details .form-row textarea:focus, {{WRAPPER}} #customer_details select:focus, {{WRAPPER}} .woocommerce .e-woocommerce-login-anchor .form-row .input-text:focus, {{WRAPPER}} #coupon_code:focus, {{WRAPPER}} .select2-container--default .select2-selection--single:focus', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_fields_focus_box_shadow', + 'selector' => '{{WRAPPER}} #customer_details .input-text:focus, {{WRAPPER}} #customer_details textarea:focus, {{WRAPPER}} #customer_details select:focus, {{WRAPPER}} .e-woocommerce-login-anchor .input-text:focus, {{WRAPPER}} #coupon_code:focus, {{WRAPPER}} .select2-container--default .select2-selection--single:focus', + ] + ); + + $this->add_control( + 'forms_fields_focus_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce #customer_details .form-row .input-text:focus, {{WRAPPER}} .woocommerce #customer_details .form-row textarea:focus, {{WRAPPER}} #customer_details select:focus, {{WRAPPER}} .woocommerce .e-woocommerce-login-anchor .form-row .input-text:focus, {{WRAPPER}} #coupon_code:focus, {{WRAPPER}} .select2-container--default .select2-selection--single:focus' => 'border-color: {{VALUE}}', + ], + 'condition' => [ + 'forms_fields_border_border!' => '', + ], + ] + ); + + $this->add_control( + 'forms_fields_focus_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-focus-transition-duration: {{SIZE}}ms', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'forms_fields_border', + 'selector' => '{{WRAPPER}} .woocommerce #customer_details .form-row .input-text, {{WRAPPER}} .woocommerce #customer_details .form-row textarea, {{WRAPPER}} .woocommerce form #customer_details select, {{WRAPPER}} .woocommerce .e-woocommerce-login-anchor .form-row .input-text, {{WRAPPER}} #coupon_code, {{WRAPPER}} .select2-container--default .select2-selection--single', + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'forms_fields_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'forms_fields_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + // style select2 + '{{WRAPPER}} .select2-container--default .select2-selection--single .select2-selection__rendered' => 'line-height: calc( ({{TOP}}{{UNIT}}*2) + 16px ); padding-left: {{LEFT}}{{UNIT}}; padding-right: {{RIGHT}}{{UNIT}};', + '{{WRAPPER}} .select2-container--default .select2-selection--single .select2-selection__arrow' => 'height: calc( ({{TOP}}{{UNIT}}*2) + 16px ); right: {{RIGHT}}{{UNIT}};', + '{{WRAPPER}} .select2-container--default .select2-selection--single' => 'height: auto;', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'forms_button_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Button', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'forms_button_typography', + 'selector' => '{{WRAPPER}} .woocommerce-button', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'forms_button_text_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-button', + ] + ); + + $this->start_controls_tabs( 'forms_buttons_styles' ); + + $this->start_controls_tab( 'forms_buttons_normal_styles', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $this->add_control( + 'forms_buttons_normal_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-normal-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_buttons_normal_background', + 'selector' => '{{WRAPPER}} .woocommerce-button', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_buttons_normal_box_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-button', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'forms_buttons_hover_styles', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] ); + + $this->add_control( + 'forms_buttons_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-hover-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_buttons_hover_background', + 'selector' => '{{WRAPPER}} .woocommerce-button:hover', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_buttons_focus_box_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-button:hover', + ] + ); + + $this->add_control( + 'forms_buttons_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-apply-coupon:hover, {{WRAPPER}} .woocommerce-form-login__submit:hover' => 'border-color: {{VALUE}}', + ], + 'condition' => [ + 'forms_buttons_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'forms_buttons_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-hover-transition-duration: {{SIZE}}ms', + ], + ] + ); + + $this->add_control( + 'forms_buttons_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'forms_buttons_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'forms_buttons_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-apply-coupon, {{WRAPPER}} .woocommerce-form-login__submit' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'forms_buttons_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'forms_buttons_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} ' => '--forms-buttons-border-color: {{VALUE}};', + ], + 'condition' => [ + 'forms_buttons_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'forms_buttons_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'forms_buttons_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}; width: auto;', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_checkout_tabs_order_summary', + [ + 'label' => esc_html__( 'Order Summary', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'order_summary_rows_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-rows-gap-top: calc( {{SIZE}}{{UNIT}}/2 ); --order-summary-rows-gap-bottom: calc( {{SIZE}}{{UNIT}}/2 );', + ], + ] + ); + + $this->add_control( + 'order_summary_items_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Items', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_items_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-items-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_summary_items_typography', + 'selector' => '{{WRAPPER}} .woocommerce-checkout-review-order-table .cart_item td', + ] + ); + + $this->add_control( + 'order_summary_variations_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Variations', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_variations_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-variations-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_summary_variations_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_TEXT, + ], + 'selector' => '{{WRAPPER}} .product-name .variation', + ] + ); + + $this->add_control( + 'order_summary_items_divider_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Dividers', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_items_divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-items-divider-color: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'order_summary_items_divider_weight', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-items-divider-weight: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'order_summary_totals_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Titles & Totals', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_totals_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-totals-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_summary_totals_typography', + 'selector' => '{{WRAPPER}} .woocommerce-checkout-review-order-table thead tr th, {{WRAPPER}} .woocommerce-checkout-review-order-table tfoot tr th, {{WRAPPER}} .woocommerce-checkout-review-order-table tfoot tr td', + ] + ); + + $this->add_control( + 'order_summary_dividers_total_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Divider Total', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_totals_divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-totals-divider-color: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'order_summary_totals_divider_weight', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-totals-divider-weight: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_checkout_tabs_purchase_button', + [ + 'label' => esc_html__( 'Purchase Button', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'purchase_button_typography', + 'selector' => '{{WRAPPER}} .woocommerce #payment #place_order', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'purchase_button_text_shadow', + 'selector' => '{{WRAPPER}} .woocommerce #payment #place_order', + ] + ); + + $this->start_controls_tabs( 'purchase_button_styles' ); + + $this->start_controls_tab( 'purchase_button_normal_styles', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $this->add_control( + 'purchase_button_normal_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--purchase-button-normal-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'purchase_button_normal_background', + 'selector' => '{{WRAPPER}} #payment #place_order', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'purchase_button_normal_box_shadow', + 'selector' => '{{WRAPPER}} #place_order', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'purchase_button_hover_styles', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] ); + + $this->add_control( + 'purchase_button_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--purchase-button-hover-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'purchase_button_hover_background', + 'selector' => '{{WRAPPER}} #payment #place_order:hover', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'purchase_button_hover_box_shadow', + 'selector' => '{{WRAPPER}} #place_order:hover', + ] + ); + + $this->add_control( + 'purchase_button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--purchase-button-hover-border-color: {{VALUE}}', + ], + 'condition' => [ + 'purchase_button_border_border!' => '', + ], + ] + ); + + $this->add_control( + 'purchase_button_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--purchase-button-hover-transition-duration: {{SIZE}}ms', + ], + ] + ); + + $this->add_control( + 'purchase_button_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + 'frontend_available' => true, + 'render_type' => 'template', + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'purchase_button_border', + 'selector' => '{{WRAPPER}} #place_order', + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'purchase_button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--purchase-button-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'purchase_button_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--purchase-button-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}; --purchase-button-width: fit-content;', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_checkout_tabs_customize', + [ + 'label' => esc_html__( 'Customize', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $customize_options = []; + + if ( $this->is_wc_feature_active( 'checkout_login_reminder' ) ) { + $customize_options += [ + 'customize_returning_customer' => esc_html__( 'Returning Customer', 'elementor-pro' ), + ]; + } + + $customize_options += [ + 'customize_billing_details' => esc_html__( 'Billing Details', 'elementor-pro' ), + 'customize_additional_info' => esc_html__( 'Additional Information', 'elementor-pro' ), + ]; + + if ( $this->is_wc_feature_active( 'shipping' ) ) { + $customize_options += [ + 'customize_shipping_address' => esc_html__( 'Shipping Address', 'elementor-pro' ), + ]; + } + + $customize_options += [ + 'customize_order_summary' => esc_html__( 'Order Summary', 'elementor-pro' ), + ]; + + if ( $this->is_wc_feature_active( 'coupons' ) ) { + $customize_options += [ + 'customize_coupon' => esc_html__( 'Coupon', 'elementor-pro' ), + ]; + } + + $customize_options += [ + 'customize_payment' => esc_html__( 'Payment', 'elementor-pro' ), + ]; + + $this->add_control( + 'section_checkout_show_customize_elements', + [ + 'label' => esc_html__( 'Select sections of the checkout to customize:', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'options' => $customize_options, + 'render_type' => 'ui', + 'label_block' => true, + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_checkout_tabs_customize_returning_customer', + [ + 'label' => esc_html__( 'Customize: Returning Customer', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'section_checkout_show_customize_elements' => 'customize_returning_customer', + ], + ] + ); + + $this->add_control( + 'returning_customers_section_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Section', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'customize_returning_customer_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-login-section' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'returning_customers_section_normal_box_shadow', + 'selector' => '{{WRAPPER}} .e-woocommerce-login-section', + ] + ); + + $this->add_control( + 'returning_customers_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-login-section' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'returning_customers_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-login-section' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'returning_customers_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'returning_customers_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-login-section' => '--sections-border-color: {{VALUE}};', + ], + 'condition' => [ + 'returning_customers_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'returning_customers_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-login-section' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'returning_customers_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-login-section' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'returning_customers_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-login-section' => '--sections-margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'returning_customers_secondary_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Secondary Title', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'returning_customers_secondary_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-form-login-toggle' => '--sections-secondary-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'returning_customers_content_typography', + 'selector' => '{{WRAPPER}} .woocommerce-form-login-toggle', + ] + ); + + $this->add_control( + 'returning_customers_description_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Description', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'returning_customers_description_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-login-nudge' => '--sections-descriptions-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'returning_customers_description_typography', + 'selector' => '{{WRAPPER}} .e-woocommerce-login-nudge.e-description', + ] + ); + + $this->add_control( + 'returning_customers_checkboxes_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Checkbox', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'returning_customers_checkboxes_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-login-section' => '--sections-checkboxes-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'returning_customers_checkboxes_typography', + 'selector' => '{{WRAPPER}} .e-woocommerce-login-section .woocommerce-form__label-for-checkbox span', + ] + ); + + $this->add_control( + 'returning_customers_link_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Link', 'elementor-pro' ), + ] + ); + + $this->start_controls_tabs( 'returning_customers_links' ); + + $this->start_controls_tab( 'returning_customers_normal_links', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $this->add_control( + 'returning_customers_normal_links_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-login-section' => '--links-normal-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'returning_customers_hover_links', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] ); + + $this->add_control( + 'returning_customers_hover_links_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-login-section' => '--links-hover-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_checkout_tabs_customize_billing_details', + [ + 'label' => esc_html__( 'Customize: Billing Details', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'section_checkout_show_customize_elements' => 'customize_billing_details', + ], + ] + ); + + $this->add_control( + 'customize_billing_details_section_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Section', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'customize_billing_details_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .col2-set .col-1' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'billing_details_section_normal_box_shadow', + 'selector' => '{{WRAPPER}} .e-checkout__column-start .col2-set .col-1', + ] + ); + + $this->add_control( + 'billing_details_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}} .col2-set .col-1' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'billing_details_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-checkout__column-start #customer_details .col-1' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'billing_details_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'billing_details_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .col2-set .col-1' => '--sections-border-color: {{VALUE}}', + ], + 'condition' => [ + 'billing_details_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'billing_details_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .col2-set .col-1' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'billing_details_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .col2-set .col-1' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'billing_details_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .col2-set .col-1' => '--sections-margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'billing_details_titles_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Title', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'billing_details_titles_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .col2-set .col-1' => '--sections-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'billing_details_titles_typography', + 'selector' => '{{WRAPPER}} .woocommerce-billing-fields h3', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'billing_details_titles_text_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-billing-fields h3', + ] + ); + + $this->add_control( + 'billing_details_checkboxes_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Checkbox', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'billing_details_checkboxes_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .col2-set .col-1' => '--sections-checkboxes-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'billing_details_checkboxes_typography', + 'selector' => '{{WRAPPER}} .col2-set .col-1 .woocommerce-form__label-for-checkbox span', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_checkout_tabs_customize_additional_info', + [ + 'label' => esc_html__( 'Customize: Additional Information', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'section_checkout_show_customize_elements' => 'customize_additional_info', + ], + ] + ); + + $this->add_control( + 'customize_additional_information_section_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Section', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'customize_additional_information_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-additional-fields' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'additional_information_section_normal_box_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-additional-fields', + ] + ); + + $this->add_control( + 'additional_information_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}} .woocommerce-additional-fields' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'additional_information_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-additional-fields' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'additional_information_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'additional_information_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-additional-fields' => '--sections-border-color: {{VALUE}};', + ], + 'condition' => [ + 'additional_information_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'additional_information_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-additional-fields' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'additional_information_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-additional-fields' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'additional_information_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-additional-fields' => '--sections-margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + '{{WRAPPER}}.e-checkout-layout-one-column .e-checkout__container' => 'grid-row-gap: {{BOTTOM}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'additional_information_titles_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Title', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'additional_information_titles_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-additional-fields' => '--sections-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'additional_information_titles_typography', + 'selector' => '{{WRAPPER}} .woocommerce-additional-fields h3', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'additional_information_titles_text_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-additional-fields h3', + ] + ); + + $this->end_controls_section(); + + if ( $this->is_wc_feature_active( 'shipping' ) ) { + $this->add_shipping_style_controls(); + } + + if ( $this->is_wc_feature_active( 'coupons' ) ) { + $this->add_coupons_style_controls(); + } + + $this->start_controls_section( + 'section_checkout_tabs_customize_order_summary', + [ + 'label' => esc_html__( 'Customize: Order Summary', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'section_checkout_show_customize_elements' => 'customize_order_summary', + ], + ] + ); + + $this->add_control( + 'customize_order_summary_section_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Section', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'customize_order_summary_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-checkout__order_review' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'order_summary_section_normal_box_shadow', + 'selector' => '{{WRAPPER}} .e-checkout__order_review', + ] + ); + + $this->add_control( + 'order_summary_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}} .e-checkout__order_review' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'order_summary_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-checkout__order_review' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'order_summary_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'order_summary_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-checkout__order_review' => '--sections-border-color: {{VALUE}}', + ], + 'condition' => [ + 'order_summary_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'order_summary_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-checkout__order_review' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'order_summary_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-checkout__order_review' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'order_summary_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-checkout__order_review' => '--sections-margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'order_summary_titles_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Title', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_titles_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-checkout__order_review' => '--sections-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_summary_titles_typography', + 'selector' => '{{WRAPPER}} h3#order_review_heading', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'order_summary_titles_text_shadow', + 'selector' => '{{WRAPPER}} h3#order_review_heading', + ] + ); + + $this->add_control( + 'order_summary_descriptions_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Message', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_descriptions_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-checkout__order_review' => '--sections-descriptions-color: {{VALUE}}; --sections-messages-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_summary_descriptions_typography', + 'selector' => '{{WRAPPER}} .woocommerce-no-shipping-available-html.e-description, {{WRAPPER}} .woocommerce-no-shipping-available-html.e-checkout-message', + ] + ); + + $this->add_control( + 'order_summary_radios_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Radio Button', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_radios_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-checkout__order_review' => '--sections-radio-buttons-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_summary_radio_typography', + 'selector' => '{{WRAPPER}} .woocommerce .e-checkout__order_review ul#shipping_method li label', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_checkout_tabs_customize_payment', + [ + 'label' => esc_html__( 'Customize: Payment', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'section_checkout_show_customize_elements' => 'customize_payment', + ], + ] + ); + + $this->add_control( + 'customize_payment_section_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Section', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'customize_payment_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout #payment' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'payment_section_normal_box_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-checkout #payment', + ] + ); + + $this->add_control( + 'payment_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout #payment' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'payment_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout #payment' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'payment_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'payment_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout #payment' => '--sections-border-color: {{VALUE}};', + ], + 'condition' => [ + 'payment_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'payment_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout #payment' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'payment_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout #payment' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'payment_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout #payment' => '--sections-margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'payment_info_box_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Info Box', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'payment_info_box_title_background', + 'selector' => '{{WRAPPER}} .woocommerce-checkout #payment .payment_methods .payment_box', + ] + ); + + $this->add_control( + 'payment_description_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Description', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'payment_description_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout-payment' => '--sections-descriptions-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'payment_description_typography', + 'selector' => '{{WRAPPER}} .woocommerce-checkout-payment .e-description', + ] + ); + + $this->add_control( + 'payment_messages_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Message', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'payment_messages_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout-payment' => '--sections-messages-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'payment_messages_typography', + 'selector' => '{{WRAPPER}} .woocommerce-checkout #payment .payment_box, {{WRAPPER}} .woocommerce-privacy-policy-text p', + ] + ); + + $this->add_control( + 'payment_checkboxes_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Checkbox', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'payment_checkboxes_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-terms-and-conditions-wrapper' => '--sections-checkboxes-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'payment_checkboxes_typography', + 'selector' => '{{WRAPPER}} .woocommerce-terms-and-conditions-wrapper .woocommerce-form__label-for-checkbox span', + ] + ); + + $this->add_control( + 'payment_radio_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Radio Button', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'payment_radio_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout-payment' => '--sections-radio-buttons-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'payment_radio_typography', + 'selector' => '{{WRAPPER}} .woocommerce-checkout-payment .wc_payment_method label', + ] + ); + + $this->add_control( + 'payment_links_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Links', 'elementor-pro' ), + ] + ); + + $this->start_controls_tabs( 'payment_colors' ); + + $this->start_controls_tab( 'payment_normal_colors', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $this->add_control( + 'payment_normal_color', + [ + 'label' => esc_html__( 'Link Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout-payment' => '--links-normal-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'payment_hover_colors', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] ); + + $this->add_control( + 'payment_hover_color', + [ + 'label' => esc_html__( 'Link Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-checkout-payment' => '--links-hover-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + } + + private function add_checkout_login_reminder_controls() { + $this->start_controls_section( + 'returning_customer_heading', + [ + 'label' => esc_html__( 'Returning Customer', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'returning_customer_section_title', + [ + 'label' => esc_html__( 'Section Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Returning customer?', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'returning_customer_link_text', + [ + 'label' => esc_html__( 'Link Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Click here to login', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_responsive_control( + 'returning_customer_title_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--login-title-alignment: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'login_button_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Login Button', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'login_button_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justify', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .e-login-wrap' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => '--login-button-alignment: start; --login-button-width: 35%;', + 'center' => '--login-button-alignment: center; --login-button-width: 35%;', + 'end' => '--login-button-alignment: end; --login-button-width: 35%;', + 'justify' => '--login-button-alignment: center; --login-button-width: 100%;', + ], + ] + ); + + $this->add_control( + 'login_button_alignment_note', + [ + 'raw' => esc_html__( 'Note: This control will only affect screen sizes Tablet and below', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + ] + ); + + $this->end_controls_section(); + } + + private function add_shipping_controls() { + $this->start_controls_section( + 'shipping_details_section', + [ + 'label' => esc_html__( 'Shipping Details', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'shipping_details_section_title', + [ + 'label' => esc_html__( 'Section Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Ship to a different address?', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater = new Repeater(); + + $repeater->add_control( + 'repeater_state', + [ + 'label' => esc_html__( 'Repeater State - hidden', 'elementor-pro' ), + 'type' => Controls_Manager::HIDDEN, + ] + ); + + $repeater->add_control( + 'label_placeholder_notification', + [ + 'raw' => __( 'Note: This label and placeholder are taken from the Billing section. You can change it there.', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'repeater_state' => 'from_billing', + ], + ] + ); + + $repeater->start_controls_tabs( 'tabs', [ + 'condition' => [ + 'repeater_state' => '', + ], + ] ); + + $repeater->start_controls_tab( 'content_tab', [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + ] ); + + $repeater->add_control( + 'label', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + ] + ); + + $repeater->add_control( + 'placeholder', + [ + 'label' => esc_html__( 'Placeholder', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + ] + ); + + $repeater->end_controls_tab(); + + $repeater->start_controls_tab( 'advanced_tab', [ + 'label' => esc_html__( 'Advanced', 'elementor-pro' ), + ] ); + + $repeater->add_control( + 'default', + [ + 'label' => esc_html__( 'Default Value', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->end_controls_tab(); + + $repeater->end_controls_tabs(); + + $repeater->add_control( + 'locale_notice', + [ + 'raw' => __( 'Note: This content cannot be changed due to local regulations.', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'repeater_state' => 'locale', + ], + ] + ); + + $repeater->add_control( + 'from_billing_notice', + [ + 'raw' => __( 'Note: This label and placeholder are taken from the Billing section. You can change it there.', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'repeater_state' => 'from_billing', + ], + ] + ); + + $this->add_control( + 'shipping_details_form_fields', + [ + 'label' => esc_html__( 'Form Items', 'elementor-pro' ), + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'item_actions' => [ + 'add' => false, + 'duplicate' => false, + 'remove' => false, + 'sort' => false, + ], + 'default' => $this->get_shipping_field_defaults(), + 'title_field' => '{{{ label }}}', + ] + ); + + $this->end_controls_section(); + } + + private function add_signup_and_login_from_checkout_controls() { + $this->start_controls_section( + 'create_account_section', + [ + 'label' => esc_html__( 'Create an Account', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'create_account_text', + [ + 'label' => esc_html__( 'Section Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Create an account?', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->end_controls_section(); + } + + private function add_coupon_controls() { + $this->start_controls_section( + 'coupon_section', + [ + 'label' => esc_html__( 'Coupon', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'coupon_section_display', + [ + 'label' => esc_html__( 'Coupon', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + ] + ); + + $this->add_control( + 'coupon_section_title_text', + [ + 'label' => esc_html__( 'Section Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Have a coupon?', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'coupon_section_display' => 'yes', + ], + ] + ); + + $this->add_control( + 'coupon_section_title_link_text', + [ + 'label' => esc_html__( 'Link Text', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'default' => esc_html__( 'Click here to enter your coupon code', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'coupon_section_display' => 'yes', + ], + ] + ); + + $this->add_responsive_control( + 'coupon_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--coupon-title-alignment: {{VALUE}};', + ], + 'condition' => [ + 'coupon_section_display' => 'yes', + ], + ] + ); + + $this->add_responsive_control( + 'coupon_button_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Apply Button', 'elementor-pro' ), + 'separator' => 'before', + 'condition' => [ + 'coupon_section_display' => 'yes', + ], + ] + ); + + $this->add_responsive_control( + 'coupon_button_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justify', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}} .coupon-container-grid' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => '--coupon-button-alignment: start;', + 'center' => '--coupon-button-alignment: center;', + 'end' => '--coupon-button-alignment: end;', + 'justify' => '--coupon-button-alignment: justify; --coupon-button-width: 100%;', + ], + 'condition' => [ + 'coupon_section_display' => 'yes', + ], + ] + ); + + $this->add_control( + 'coupon_button_alignment_note', + [ + 'raw' => esc_html__( 'Note: This control will only affect screen sizes Tablet and below', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'coupon_section_display' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + } + + private function add_shipping_style_controls() { + $this->start_controls_section( + 'section_checkout_tabs_customize_shipping_address', + [ + 'label' => esc_html__( 'Customize: Shipping Address', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'section_checkout_show_customize_elements' => 'customize_shipping_address', + ], + ] + ); + + $this->add_control( + 'shipping_address_section_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Section', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'customize_shipping_address_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-shipping-fields .shipping_address' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'shipping_address_section_normal_box_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-shipping-fields .shipping_address', + ] + ); + + $this->add_control( + 'shipping_address_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}} .woocommerce-shipping-fields .shipping_address' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'shipping_address_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-shipping-fields .shipping_address' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'shipping_address_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'shipping_address_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-shipping-fields .shipping_address' => '--sections-border-color: {{VALUE}}', + ], + 'condition' => [ + 'shipping_address_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'shipping_address_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-shipping-fields .shipping_address' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'shipping_address_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-shipping-fields .shipping_address' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'shipping_address_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .woocommerce-shipping-fields .shipping_address' => '--sections-margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'shipping_address_checkboxes_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Checkboxes', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'shipping_address_checkboxes_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .woocommerce-shipping-fields' => '--sections-checkboxes-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'shipping_address_checkboxes_typography', + 'selector' => '{{WRAPPER}} .woocommerce-shipping-fields .woocommerce-form__label-for-checkbox span', + ] + ); + + $this->end_controls_section(); + } + + private function add_coupons_style_controls() { + $this->start_controls_section( + 'section_checkout_tabs_customize_coupon', + [ + 'label' => esc_html__( 'Customize: Coupon', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'section_checkout_show_customize_elements' => 'customize_coupon', + ], + ] + ); + + $this->add_control( + 'customize_coupon_section_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Section', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'customize_coupon_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-coupon-box' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'coupon_section_normal_box_shadow', + 'selector' => '{{WRAPPER}} .e-coupon-box', + ] + ); + + $this->add_control( + 'coupon_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}} .e-coupon-box' => '--sections-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'coupon_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-coupon-box' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'coupon_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'coupon_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-coupon-box' => '--sections-border-color: {{VALUE}};', + ], + 'condition' => [ + 'coupon_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'coupon_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-coupon-box' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'coupon_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-coupon-box' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'coupon_margin', + [ + 'label' => esc_html__( 'Margin', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-coupon-box' => '--sections-margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'coupon_secondary_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Secondary Title', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'coupon_secondary_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-woocommerce-coupon-nudge' => '--sections-secondary-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'coupon_content_typography', + 'selector' => '{{WRAPPER}} .e-woocommerce-coupon-nudge.e-checkout-secondary-title', + ] + ); + + $this->add_control( + 'coupon_link_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Link', 'elementor-pro' ), + ] + ); + + $this->start_controls_tabs( 'coupon_links' ); + + $this->start_controls_tab( 'coupon_normal_links', [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] ); + + $this->add_control( + 'coupon_normal_links_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-coupon-box' => '--links-normal-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'coupon_hover_links', [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] ); + + $this->add_control( + 'coupon_hover_links_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-coupon-box' => '--links-hover-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + } + + /** + * Get Billing Field Defaults + * + * Get defaults used for the billing details repeater control. + * + * @since 3.5.0 + * + * @return array + */ + private function get_billing_field_defaults() { + $fields = [ + 'billing_first_name' => [ + 'label' => esc_html__( 'First Name', 'elementor-pro' ), + 'repeater_state' => '', + ], + 'billing_last_name' => [ + 'label' => esc_html__( 'Last Name', 'elementor-pro' ), + 'repeater_state' => '', + ], + 'billing_company' => [ + 'label' => esc_html__( 'Company Name', 'elementor-pro' ), + 'repeater_state' => '', + ], + 'billing_country' => [ + 'label' => esc_html__( 'Country / Region', 'elementor-pro' ), + 'repeater_state' => 'locale', + ], + 'billing_address_1' => [ + 'label' => esc_html__( 'Street Address', 'elementor-pro' ), + 'repeater_state' => 'locale', + ], + 'billing_postcode' => [ + 'label' => esc_html__( 'Post Code', 'elementor-pro' ), + 'repeater_state' => 'locale', + ], + 'billing_city' => [ + 'label' => esc_html__( 'Town / City', 'elementor-pro' ), + 'repeater_state' => 'locale', + ], + 'billing_state' => [ + 'label' => esc_html__( 'State', 'elementor-pro' ), + 'repeater_state' => 'locale', + ], + 'billing_phone' => [ + 'label' => esc_html__( 'Phone', 'elementor-pro' ), + 'repeater_state' => '', + ], + 'billing_email' => [ + 'label' => esc_html__( 'Email Address', 'elementor-pro' ), + 'repeater_state' => '', + ], + ]; + + return $this->reformat_address_field_defaults( $fields ); + } + + /** + * Get Shipping Field Defaults + * + * Get defaults used for the shipping details repeater control. + * + * @since 3.5.0 + * + * @return array + */ + private function get_shipping_field_defaults() { + $fields = [ + 'shipping_first_name' => [ + 'label' => esc_html__( 'First Name', 'elementor-pro' ), + 'repeater_state' => '', + ], + 'shipping_last_name' => [ + 'label' => esc_html__( 'Last Name', 'elementor-pro' ), + 'repeater_state' => '', + ], + 'shipping_company' => [ + 'label' => esc_html__( 'Company Name', 'elementor-pro' ), + 'repeater_state' => '', + ], + 'shipping_country' => [ + 'label' => esc_html__( 'Country / Region', 'elementor-pro' ), + 'repeater_state' => 'locale', + ], + 'shipping_address_1' => [ + 'label' => esc_html__( 'Street Address', 'elementor-pro' ), + 'repeater_state' => 'locale', + ], + 'shipping_postcode' => [ + 'label' => esc_html__( 'Post Code', 'elementor-pro' ), + 'repeater_state' => 'locale', + ], + 'shipping_city' => [ + 'label' => esc_html__( 'Town / City', 'elementor-pro' ), + 'repeater_state' => 'locale', + ], + 'shipping_state' => [ + 'label' => esc_html__( 'State', 'elementor-pro' ), + 'repeater_state' => 'locale', + ], + ]; + + return $this->reformat_address_field_defaults( $fields ); + } + + /** + * Reformat Address Field Defaults + * + * Used with the `get_..._field_defaults()` methods. + * Takes the address array and converts it into the format expected by the repeater controls. + * + * @since 3.5.0 + * + * @param $address + * @return array + */ + private function reformat_address_field_defaults( $address ) { + $defaults = []; + foreach ( $address as $key => $value ) { + $defaults[] = [ + 'field_key' => $key, + 'field_label' => $value['label'], + 'label' => $value['label'], + 'placeholder' => $value['label'], + 'repeater_state' => $value['repeater_state'], + ]; + } + + return $defaults; + } + + /** + * Get Main Woocommerce Sections Selectors + * + * Get all the 'Sections' selectors. There are numerous controls that need these selectors so it was easier + * to consolidate them into one function. Especially when updates need to be made. + * + * @since 3.5.0 + * + * @return string + */ + private function get_main_woocommerce_sections_selectors() { + $selector = '{{WRAPPER}} .e-woocommerce-login-section, {{WRAPPER}} .woocommerce-checkout #customer_details .col-1, {{WRAPPER}} .woocommerce-additional-fields, {{WRAPPER}} .e-checkout__order_review, {{WRAPPER}} .e-coupon-box, {{WRAPPER}} .woocommerce-checkout #payment'; + if ( $this->is_wc_feature_active( 'shipping' ) ) { + $selector .= ', {{WRAPPER}} .woocommerce-shipping-fields .shipping_address'; + } + return $selector; + } + + /** + * Get Main Woocommerce Sections Title Selectors + * + * Get all the 'Title' selectors. There are numerous controls that need these selectors so it was easier to + * consolidate them into one function. Especially when updates need to be made. + * + * @since 3.5.0 + * + * @return string + */ + private function get_main_woocommerce_sections_title_selectors() { + return '{{WRAPPER}} h3#order_review_heading, {{WRAPPER}} .woocommerce-billing-fields h3, {{WRAPPER}} .woocommerce-additional-fields h3'; + } + + /** + * Init Gettext Modifications + * + * Sets the `$gettext_modifications` property used with the `filter_gettext()` in the extended Base_Widget. + * + * @since 3.5.0 + */ + protected function init_gettext_modifications() { + $instance = $this->get_settings_for_display(); + + $this->gettext_modifications = [ + 'Billing details' => isset( $instance['billing_details_section_title'] ) ? $instance['billing_details_section_title'] : '', + 'Billing & Shipping' => isset( $instance['billing_details_section_title'] ) ? $instance['billing_details_section_title'] : '', + 'Ship to a different address?' => isset( $instance['shipping_details_section_title'] ) ? $instance['shipping_details_section_title'] : '', + 'Additional information' => isset( $instance['additional_information_section_title'] ) ? $instance['additional_information_section_title'] : '', + 'Your order' => isset( $instance['order_summary_section_title'] ) ? $instance['order_summary_section_title'] : '', + 'Have a coupon?' => isset( $instance['coupon_section_title_text'] ) ? $instance['coupon_section_title_text'] : '', + 'Click here to enter your coupon code' => isset( $instance['coupon_section_title_link_text'] ) ? $instance['coupon_section_title_link_text'] : '', + 'Returning customer?' => isset( $instance['returning_customer_section_title'] ) ? $instance['returning_customer_section_title'] : '', + 'Click here to login' => isset( $instance['returning_customer_link_text'] ) ? $instance['returning_customer_link_text'] : '', + 'Create an account?' => isset( $instance['create_account_text'] ) ? $instance['create_account_text'] : '', + ]; + } + + /** + * WooCommerce Terms and Conditions Checkbox Text. + * + * WooCommerce filter is used to apply widget settings to Checkout Terms & Conditions text and link text. + * + * @since 3.5.0 + * + * @param string $text + * @return string + */ + public function woocommerce_terms_and_conditions_checkbox_text( $text ) { + $instance = $this->get_settings_for_display(); + + if ( ! isset( $instance['terms_conditions_message_text'] ) || ! isset( $instance['terms_conditions_link_text'] ) ) { + return $text; + } + + $message = $instance['terms_conditions_message_text']; + $link = $instance['terms_conditions_link_text']; + + $terms_page_id = wc_terms_and_conditions_page_id(); + if ( $terms_page_id ) { + $message .= ' ' . $link . ''; + } + + return $message; + } + + /** + * Modify Form Field. + * + * WooCommerce filter is used to apply widget settings to the Checkout forms address fields + * from the Billing and Shipping Details widget sections, e.g. label, placeholder, default. + * + * @since 3.5.0 + * + * @param array $args + * @param string $key + * @param string $value + * @return array + */ + public function modify_form_field( $args, $key, $value ) { + $reformatted_form_fields = $this->get_reformatted_form_fields(); + + // Check if we need to modify the args of this form field. + if ( isset( $reformatted_form_fields[ $key ] ) ) { + $apply_fields = [ + 'label', + 'placeholder', + 'default', + ]; + + foreach ( $apply_fields as $field ) { + if ( ! empty( $reformatted_form_fields[ $key ][ $field ] ) ) { + $args[ $field ] = $reformatted_form_fields[ $key ][ $field ]; + } + } + } + + return $args; + } + + /** + * Get Reformatted Form Fields. + * + * Combines the 3 relevant repeater settings arrays into a one level deep associative array + * with the keys that match those that WooCommerce uses for its form fields. + * + * The result is cached so the conversion only ever happens once. + * + * @since 3.5.0 + * + * @return array + */ + private function get_reformatted_form_fields() { + if ( ! isset( $this->reformatted_form_fields ) ) { + $instance = $this->get_settings_for_display(); + + // Reformat form repeater field into one usable array. + $repeater_fields = [ + 'billing_details_form_fields', + 'shipping_details_form_fields', + 'additional_information_form_fields', + ]; + + $this->reformatted_form_fields = []; + + // Apply other modifications to inputs. + foreach ( $repeater_fields as $repeater_field ) { + if ( isset( $instance[ $repeater_field ] ) ) { + foreach ( $instance[ $repeater_field ] as $item ) { + if ( ! isset( $item['field_key'] ) ) { + continue; + } + $this->reformatted_form_fields[ $item['field_key'] ] = $item; + } + } + } + } + + return $this->reformatted_form_fields; + } + + /** + * Render Woocommerce Checkout Login Form + * + * A custom function to render a login form on the Checkout widget. The default WC Login form + * was removed in this file's render() method with: + * remove_action( 'woocommerce_before_checkout_form', 'woocommerce_checkout_login_form' ); + * + * And then we are adding this form into the widget at the + * 'woocommerce_checkout_before_customer_details' hook. + * + * We are doing this in order to match the placement of the Login form to the provided design. + * WC places these forms ABOVE the checkout form section where as we needed to place them inside the + * checkout form section. So we removed the default login form and added our own form. + * + * @since 3.5.0 + */ + private function render_woocommerce_checkout_login_form() { + $settings = $this->get_settings_for_display(); + $button_classes = [ 'woocommerce-button', 'button', 'woocommerce-form-login__submit', 'e-woocommerce-form-login-submit' ]; + if ( $settings['forms_buttons_hover_animation'] ) { + $button_classes[] = 'elementor-animation-' . $settings['forms_buttons_hover_animation']; + } + $this->add_render_attribute( + 'button_login', [ + 'class' => $button_classes, + 'name' => 'login', + 'type' => 'submit', + ] + ); + ?> + + get_settings_for_display(); + $button_classes = [ 'woocommerce-button', 'button', 'e-apply-coupon' ]; + if ( $settings['forms_buttons_hover_animation'] ) { + $button_classes[] = 'elementor-animation-' . $settings['forms_buttons_hover_animation']; + } + $this->add_render_attribute( + 'button_coupon', [ + 'class' => $button_classes, + 'name' => 'apply_coupon', + 'type' => 'submit', + ] + ); + ?> +
        +

        + +
        + editor->is_edit_mode() ); + } + + /** + * Should Render Coupon + * + * Decide if the coupon form should be rendered. + * The coupon form should be rendered if: + * 1) The WooCommerce setting is enabled + * 2) And the Coupon Display toggle hasn't been set to 'no' + * 3) AND: a payment is needed, OR the Editor is open + * + * @since 3.5.0 + * + * @return boolean + */ + private function should_render_coupon() { + $settings = $this->get_settings_for_display(); + $coupon_display_control = true; + + if ( '' === $settings['coupon_section_display'] ) { + $coupon_display_control = false; + } + + return ( WC()->cart->needs_payment() || Plugin::elementor()->editor->is_edit_mode() ) && wc_coupons_enabled() && $coupon_display_control; + } + + /** + * WooCommerce Checkout Before Customer Details + * + * Callback function for the woocommerce_checkout_before_customer_details hook that outputs elements + * + * This eliminates the need for template overrides. + * + * @since 3.5.0 + */ + public function woocommerce_checkout_before_customer_details() { + ?> +
        + +
        + + should_render_login() ) { + $this->render_woocommerce_checkout_login_form(); + } + } + + /** + * Woocommerce Checkout After Customer Details + * + * Output containing elements. Callback function for the woocommerce_checkout_after_customer_details hook. + * + * This eliminates the need for template overrides. + * + * @since 3.5.0 + */ + public function woocommerce_checkout_after_customer_details() { + ?> + +
        + +
        + +
        + + +
        + + + +
        + +
        + should_render_coupon() ) { + $this->render_woocommerce_checkout_coupon_form(); + } + ?> +
        + + + + +
        + +
        + +
        + editor->is_edit_mode(); + + // Simulate a logged out user so that all WooCommerce sections will render in the Editor + if ( $is_editor ) { + $store_current_user = wp_get_current_user()->ID; + wp_set_current_user( 0 ); + } + + // Add actions & filters before displaying our Widget. + $this->add_render_hooks(); + + // Display our Widget. + echo do_shortcode( '[woocommerce_checkout]' ); + + // Remove actions & filters after displaying our Widget. + $this->remove_render_hooks(); + + // Return to existing logged-in user after widget is rendered. + if ( $is_editor ) { + wp_set_current_user( $store_current_user ); + } + } + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/elements.php b/modules/woocommerce/widgets/elements.php new file mode 100644 index 0000000..ddf6220 --- /dev/null +++ b/modules/woocommerce/widgets/elements.php @@ -0,0 +1,163 @@ +start_controls_section( + 'section_product', + [ + 'label' => esc_html__( 'Element', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'element', + [ + 'label' => esc_html__( 'Page', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => '— ' . esc_html__( 'Select', 'elementor-pro' ) . ' —', + 'woocommerce_cart' => esc_html__( 'Cart Page', 'elementor-pro' ), + 'product_page' => esc_html__( 'Single Product Page', 'elementor-pro' ), + 'woocommerce_checkout' => esc_html__( 'Checkout Page', 'elementor-pro' ), + 'woocommerce_order_tracking' => esc_html__( 'Order Tracking Form', 'elementor-pro' ), + 'woocommerce_my_account' => esc_html__( 'My Account', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'product_id', + [ + 'label' => esc_html__( 'Product', 'elementor-pro' ), + 'type' => QueryModule::QUERY_CONTROL_ID, + 'options' => [], + 'label_block' => true, + 'autocomplete' => [ + 'object' => QueryModule::QUERY_OBJECT_POST, + 'query' => [ + 'post_type' => [ 'product' ], + ], + ], + 'condition' => [ + 'element' => [ 'product_page' ], + ], + ] + ); + + $this->end_controls_section(); + } + + private function get_shortcode() { + $settings = $this->get_settings(); + + switch ( $settings['element'] ) { + case '': + return ''; + break; + + case 'product_page': + if ( ! empty( $settings['product_id'] ) ) { + $product_data = get_post( $settings['product_id'] ); + $product = ! empty( $product_data ) && in_array( $product_data->post_type, [ 'product', 'product_variation' ] ) ? wc_setup_product_data( $product_data ) : false; + } + + if ( empty( $product ) && current_user_can( 'manage_options' ) ) { + return esc_html__( 'Please set a valid product', 'elementor-pro' ); + } + + $this->add_render_attribute( 'shortcode', 'id', $settings['product_id'] ); + break; + + case 'woocommerce_cart': + case 'woocommerce_checkout': + case 'woocommerce_order_tracking': + break; + } + + $shortcode = sprintf( + '[%s %s]', + esc_html( $settings['element'] ), + $this->get_render_attribute_string( 'shortcode' ) + ); + + return $shortcode; + } + + protected function render() { + $shortcode = $this->get_shortcode(); + + if ( empty( $shortcode ) ) { + return; + } + + Module::instance()->add_products_post_class_filter(); + + $html = do_shortcode( $shortcode ); + + if ( 'woocommerce_checkout' === $this->get_settings( 'element' ) && '
        ' === $html ) { + $html = '
        ' . esc_html__( 'Your cart is currently empty.', 'elementor-pro' ) . '
        '; + } + + // PHPCS - Woocommerce output + echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + + Module::instance()->remove_products_post_class_filter(); + } + + public function render_plain_content() { + // PHPCS - Already escaped in get_shortcode + echo $this->get_shortcode(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/menu-cart.php b/modules/woocommerce/widgets/menu-cart.php new file mode 100644 index 0000000..fac3669 --- /dev/null +++ b/modules/woocommerce/widgets/menu-cart.php @@ -0,0 +1,2236 @@ +start_controls_section( + 'section_menu_icon_content', + [ + 'label' => esc_html__( 'Menu Icon', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'icon', + [ + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'cart-light' => esc_html__( 'Cart', 'elementor-pro' ) . ' ' . esc_html__( 'Light', 'elementor-pro' ), + 'cart-medium' => esc_html__( 'Cart', 'elementor-pro' ) . ' ' . esc_html__( 'Medium', 'elementor-pro' ), + 'cart-solid' => esc_html__( 'Cart', 'elementor-pro' ) . ' ' . esc_html__( 'Solid', 'elementor-pro' ), + 'basket-light' => esc_html__( 'Basket', 'elementor-pro' ) . ' ' . esc_html__( 'Light', 'elementor-pro' ), + 'basket-medium' => esc_html__( 'Basket', 'elementor-pro' ) . ' ' . esc_html__( 'Medium', 'elementor-pro' ), + 'basket-solid' => esc_html__( 'Basket', 'elementor-pro' ) . ' ' . esc_html__( 'Solid', 'elementor-pro' ), + 'bag-light' => esc_html__( 'Bag', 'elementor-pro' ) . ' ' . esc_html__( 'Light', 'elementor-pro' ), + 'bag-medium' => esc_html__( 'Bag', 'elementor-pro' ) . ' ' . esc_html__( 'Medium', 'elementor-pro' ), + 'bag-solid' => esc_html__( 'Bag', 'elementor-pro' ) . ' ' . esc_html__( 'Solid', 'elementor-pro' ), + 'custom' => esc_html__( 'Custom', 'elementor-pro' ), + ], + 'default' => 'cart-medium', + 'prefix_class' => 'toggle-icon--', // Prefix class not used anymore, but kept for BC reasons. + 'render_type' => 'template', + ] + ); + + $this->add_control( + 'menu_icon_svg', + [ + 'label' => esc_html__( 'Custom Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon_active', + 'default' => [ + 'value' => 'fas fa-shopping-cart', + 'library' => 'fa-solid', + ], + 'skin_settings' => [ + 'inline' => [ + 'none' => [ + 'label' => 'None', + ], + ], + ], + 'recommended' => [ + 'fa-solid' => [ + 'shopping-bag', + 'shopping-basket', + 'shopping-cart', + 'cart-arrow-down', + 'cart-plus', + ], + ], + 'skin' => 'inline', + 'label_block' => false, + 'condition' => [ + 'icon' => 'custom', + ], + ] + ); + + $this->add_control( + 'items_indicator', + [ + 'label' => esc_html__( 'Items Indicator', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'bubble' => esc_html__( 'Bubble', 'elementor-pro' ), + 'plain' => esc_html__( 'Plain', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-menu-cart--items-indicator-', + 'default' => 'bubble', + ] + ); + + $this->add_control( + 'hide_empty_indicator', + [ + 'label' => esc_html__( 'Hide Empty', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'return_value' => 'hide', + 'prefix_class' => 'elementor-menu-cart--empty-indicator-', + 'condition' => [ + 'items_indicator!' => 'none', + ], + ] + ); + + $this->add_control( + 'show_subtotal', + [ + 'label' => esc_html__( 'Subtotal', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'return_value' => 'yes', + 'default' => 'yes', + 'prefix_class' => 'elementor-menu-cart--show-subtotal-', + ] + ); + + $this->add_responsive_control( + 'alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--main-alignment: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_cart', + [ + 'label' => esc_html__( 'Cart', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'cart_type', + [ + 'label' => esc_html__( 'Cart Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'side-cart' => esc_html__( 'Side Cart', 'elementor-pro' ), + 'mini-cart' => esc_html__( 'Mini Cart', 'elementor-pro' ), + ], + 'default' => 'side-cart', + 'prefix_class' => 'elementor-menu-cart--cart-type-', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'open_cart', + [ + 'label' => esc_html__( 'Open Cart', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'click' => esc_html__( 'On Click', 'elementor-pro' ), + 'mouseover' => esc_html__( 'On Hover', 'elementor-pro' ), + ], + 'default' => 'click', + 'frontend_available' => true, + 'render_type' => 'template', + ] + ); + + $this->add_responsive_control( + 'side_cart_alignment', + [ + 'label' => esc_html__( 'Cart Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'end' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'condition' => [ + 'cart_type' => 'side-cart', + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => '--side-cart-alignment-transform: translateX(-100%); --side-cart-alignment-right: auto; --side-cart-alignment-left: 0;', + 'end' => '--side-cart-alignment-transform: translateX(100%); --side-cart-alignment-left: auto; --side-cart-alignment-right: 0;', + ], + ] + ); + + $this->add_responsive_control( + 'mini_cart_alignment', + [ + 'label' => esc_html__( 'Cart Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-h-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'condition' => [ + 'cart_type' => 'mini-cart', + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-menu-cart--cart-type-mini-cart .elementor-menu-cart__container' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => 'left: 0; right: auto; transform: none;', + 'center' => 'left: 50%; right: auto; transform: translateX(-50%);', + 'end' => 'right: 0; left: auto; transform: none;', + ], + ] + ); + + $this->add_responsive_control( + 'mini_cart_spacing', + [ + 'label' => esc_html__( 'Distance', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => -300, + 'max' => 300, + ], + 'em' => [ + 'min' => -30, + 'max' => 30, + ], + 'rem' => [ + 'min' => -30, + 'max' => 30, + ], + '%' => [ + 'min' => -100, + 'max' => 100, + ], + ], + 'condition' => [ + 'cart_type' => 'mini-cart', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--mini-cart-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'heading_close_cart_button', + [ + 'label' => esc_html__( 'Close Cart', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'close_cart_button_show', + [ + 'label' => esc_html__( 'Close Icon', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'return_value' => 'yes', + 'default' => 'yes', + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-cart__close-button, {{WRAPPER}} .elementor-menu-cart__close-button-custom' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + '' => 'display: none;', + ], + ] + ); + + $this->add_control( + 'close_cart_icon_svg', + [ + 'label' => esc_html__( 'Custom Icon', 'elementor-pro' ), + 'type' => Controls_Manager::ICONS, + 'fa4compatibility' => 'icon_active', + 'skin_settings' => [ + 'inline' => [ + 'none' => [ + 'label' => 'Default', + 'icon' => 'fas fa-times', + ], + 'icon' => [ + 'icon' => 'eicon-star', + ], + ], + ], + 'recommended' => [ + 'fa-regular' => [ + 'times-circle', + ], + 'fa-solid' => [ + 'times', + 'times-circle', + ], + ], + 'skin' => 'inline', + 'label_block' => false, + 'condition' => [ + 'close_cart_button_show!' => '', + ], + ] + ); + + $this->add_control( + 'close_cart_button_alignment', + [ + 'label' => esc_html__( 'Icon Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'end' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'condition' => [ + 'close_cart_button_show!' => '', + ], + 'selectors_dictionary' => [ + 'start' => 'margin-right: auto', + 'end' => 'margin-left: auto', + ], + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-cart__close-button, {{WRAPPER}} .elementor-menu-cart__close-button-custom' => '{{VALUE}};', + ], + ] + ); + + $this->add_control( + 'heading_remove_item_button', + [ + 'label' => esc_html__( 'Remove Item', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'show_remove_icon', + [ + 'label' => esc_html__( 'Remove Item Icon', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'return_value' => 'yes', + 'default' => 'yes', + 'prefix_class' => 'elementor-menu-cart--show-remove-button-', + ] + ); + + $this->add_control( + 'remove_item_button_position', + [ + 'label' => esc_html__( 'Icon Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'middle' => [ + 'title' => esc_html__( 'Middle', 'elementor-pro' ), + 'icon' => 'eicon-v-align-middle', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'default' => '', + 'prefix_class' => 'remove-item-position--', + 'condition' => [ + 'show_remove_icon!' => '', + ], + ] + ); + + $this->add_control( + 'heading_price_quantity', + [ + 'label' => esc_html__( 'Price and Quantity', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'price_quantity_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'top' => '--price-quantity-position--grid-template-rows: auto 75%; --price-quantity-position--align-self: start;', + 'bottom' => '', + ], + ] + ); + + $this->add_control( + 'show_divider', + [ + 'label' => esc_html__( 'Cart Dividers', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'separator' => 'before', + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'return_value' => 'yes', + 'default' => 'yes', + 'selectors' => [ + '{{WRAPPER}}' => '--divider-style: {{VALUE}}; --subtotal-divider-style: {{VALUE}};', + ], + 'selectors_dictionary' => [ + '' => 'none', + 'yes' => 'solid', + ], + ] + ); + + $this->add_control( + 'heading_buttons', + [ + 'label' => esc_html__( 'Buttons', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'view_cart_button_show', + [ + 'label' => esc_html__( 'View Cart', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'return_value' => 'yes', + 'default' => 'yes', + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + '' => '--view-cart-button-display: none; --cart-footer-layout: 1fr;', + ], + ] + ); + + $this->add_control( + 'view_cart_button_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justify', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'condition' => [ + 'view_cart_button_show!' => '', + 'checkout_button_show' => '', + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => '--cart-footer-buttons-alignment-display: block; --cart-footer-buttons-alignment-text-align: left; --cart-footer-buttons-alignment-button-width: auto;', + 'center' => '--cart-footer-buttons-alignment-display: block; --cart-footer-buttons-alignment-text-align: center; --cart-footer-buttons-alignment-button-width: auto;', + 'end' => '--cart-footer-buttons-alignment-display: block; --cart-footer-buttons-alignment-text-align: right; --cart-footer-buttons-alignment-button-width: auto;', + 'justify' => '--cart-footer-layout: 1fr;', + ], + ] + ); + + $this->add_control( + 'checkout_button_show', + [ + 'label' => esc_html__( 'Checkout', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'return_value' => 'yes', + 'default' => 'yes', + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + '' => '--checkout-button-display: none; --cart-footer-layout: 1fr;', + ], + ] + ); + + $this->add_control( + 'checkout_button_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justify', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'condition' => [ + 'checkout_button_show!' => '', + 'view_cart_button_show' => '', + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => '--cart-footer-buttons-alignment-display: block; --cart-footer-buttons-alignment-text-align: left; --cart-footer-buttons-alignment-button-width: auto;', + 'center' => '--cart-footer-buttons-alignment-display: block; --cart-footer-buttons-alignment-text-align: center; --cart-footer-buttons-alignment-button-width: auto;', + 'end' => '--cart-footer-buttons-alignment-display: block; --cart-footer-buttons-alignment-text-align: right; --cart-footer-buttons-alignment-button-width: auto;', + 'justify' => '--cart-footer-layout: 1fr;', + ], + ] + ); + + $this->add_control( + 'checkout_button_display', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::HIDDEN, + 'condition' => [ + 'checkout_button_show' => '', + 'view_cart_button_show' => '', + ], + 'default' => '--cart-footer-buttons-alignment-display: none;', + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + ] + ); + + $this->add_control( + 'buttons_position', + [ + 'label' => esc_html__( 'Vertical Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'top' => [ + 'title' => esc_html__( 'Top', 'elementor-pro' ), + 'icon' => 'eicon-v-align-top', + ], + 'bottom' => [ + 'title' => esc_html__( 'Bottom', 'elementor-pro' ), + 'icon' => 'eicon-v-align-bottom', + ], + ], + 'default' => '', + 'condition' => [ + 'cart_type' => 'side-cart', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'view_cart_button_show', + 'operator' => '!=', + 'value' => '', + ], + [ + 'name' => 'checkout_button_show', + 'operator' => '!=', + 'value' => '', + ], + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'bottom' => '--cart-buttons-position-margin: auto;', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_additional_options', + [ + 'label' => esc_html__( 'Additional Options', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'heading_additional_options', + [ + 'label' => esc_html__( 'Cart', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'automatically_open_cart', + [ + 'label' => esc_html__( 'Automatically Open Cart', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'description' => esc_html__( 'Open the cart every time an item is added.', 'elementor-pro' ), + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'return_value' => 'yes', + 'default' => 'no', + 'frontend_available' => true, + ] + ); + + $this->add_control( + 'automatically_update_cart', + [ + 'label' => esc_html__( 'Automatically Update Cart', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'return_value' => 'yes', + 'default' => 'yes', + 'description' => esc_html__( 'Updates to the cart (e.g., a removed item) via Ajax. The cart will update without refreshing the whole page.', 'elementor-pro' ), + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'yes' => '--elementor-remove-from-cart-button: none; --remove-from-cart-button: block;', + '' => '--elementor-remove-from-cart-button: block; --remove-from-cart-button: none;', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_toggle_style', + [ + 'label' => esc_html__( 'Menu Icon', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->start_controls_tabs( 'toggle_button_colors' ); + + $this->start_controls_tab( 'toggle_button_normal_colors', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'toggle_button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-button-text-color: {{VALUE}};', + ], + 'condition' => [ + 'show_subtotal!' => '', + ], + ] + ); + + $this->add_control( + 'toggle_button_icon_color', + [ + 'label' => esc_html__( 'Icon Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-button-icon-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'toggle_button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-button-background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'toggle_button_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-button-border-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'toggle_button_normal_box_shadow', + 'selector' => '{{WRAPPER}} .elementor-menu-cart__toggle .elementor-button', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'toggle_button_hover_colors', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'toggle_button_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-button-hover-text-color: {{VALUE}};', + ], + 'condition' => [ + 'show_subtotal!' => '', + ], + ] + ); + + $this->add_control( + 'toggle_button_hover_icon_color', + [ + 'label' => esc_html__( 'Icon Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-button-icon-hover-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'toggle_button_hover_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-button-hover-background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'toggle_button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-button-hover-border-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'toggle_button_hover_box_shadow', + 'selector' => '{{WRAPPER}} .elementor-menu-cart__toggle .elementor-button:hover', + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'toggle_button_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'separator' => 'before', + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-button-border-width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'toggle_button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-button-border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'toggle_button_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-menu-cart__toggle .elementor-button', + 'separator' => 'before', + 'condition' => [ + 'show_subtotal!' => '', + ], + ] + ); + + $this->add_control( + 'heading_icon_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Icon', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'toggle_icon_size', + [ + 'label' => esc_html__( 'Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-icon-size: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'toggle_icon_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .elementor-menu-cart__toggle .elementor-button-text' => 'margin-right: {{SIZE}}{{UNIT}};', + 'body.rtl {{WRAPPER}} .elementor-menu-cart__toggle .elementor-button-text' => 'margin-left: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'show_subtotal!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'toggle_button_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--toggle-icon-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'items_indicator_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Items Indicator', 'elementor-pro' ), + 'separator' => 'before', + 'condition' => [ + 'items_indicator!' => 'none', + ], + ] + ); + $this->add_control( + 'items_indicator_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--items-indicator-text-color: {{VALUE}};', + ], + 'condition' => [ + 'items_indicator!' => 'none', + ], + ] + ); + + $this->add_control( + 'items_indicator_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--items-indicator-background-color: {{VALUE}};', + ], + 'condition' => [ + 'items_indicator' => 'bubble', + ], + ] + ); + + $this->add_responsive_control( + 'items_indicator_distance', + [ + 'label' => esc_html__( 'Distance', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'em' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}} .elementor-menu-cart__toggle .elementor-button-icon .elementor-button-icon-qty[data-counter]' => 'right: -{{SIZE}}{{UNIT}}; top: -{{SIZE}}{{UNIT}};', + 'body.rtl {{WRAPPER}} .elementor-menu-cart__toggle .elementor-button-icon .elementor-button-icon-qty[data-counter]' => 'right: {{SIZE}}{{UNIT}}; top: -{{SIZE}}{{UNIT}}; left: auto;', + ], + 'condition' => [ + 'items_indicator' => 'bubble', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_cart_style', + [ + 'label' => esc_html__( 'Cart', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--cart-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'none' => esc_html__( 'None', 'elementor-pro' ), + 'solid' => esc_html__( 'Solid', 'elementor-pro' ), + 'double' => esc_html__( 'Double', 'elementor-pro' ), + 'dotted' => esc_html__( 'Dotted', 'elementor-pro' ), + 'dashed' => esc_html__( 'Dashed', 'elementor-pro' ), + 'groove' => esc_html__( 'Groove', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}}' => '--cart-border-style: {{VALUE}};', + ], + 'default' => 'none', + ] + ); + + $this->add_responsive_control( + 'border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-cart__main' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--cart-border-color: {{VALUE}};', + ], + 'condition' => [ + 'border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--cart-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'cart_box_shadow', + 'selector' => '{{WRAPPER}} .elementor-menu-cart__main', + ] + ); + + $this->add_responsive_control( + 'cart_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--cart-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'heading_close', + [ + 'label' => esc_html__( 'Close Cart', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'condition' => [ + 'close_cart_button_show!' => '', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'close_cart_icon_size', + [ + 'label' => esc_html__( 'Icon Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--cart-close-icon-size: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'close_cart_button_show!' => '', + ], + ] + ); + + $this->start_controls_tabs( 'cart_icon_style' ); + + $this->start_controls_tab( + 'icon_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + 'condition' => [ + 'close_cart_button_show!' => '', + ], + ] + ); + + $this->add_control( + 'close_cart_icon_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--cart-close-button-color: {{VALUE}};', + ], + 'condition' => [ + 'close_cart_button_show!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'icon_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + 'condition' => [ + 'close_cart_button_show!' => '', + ], + ] + ); + + $this->add_control( + 'close_cart_icon_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--cart-close-button-hover-color: {{VALUE}};', + ], + 'condition' => [ + 'close_cart_button_show!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'heading_remove_item_button_style', + [ + 'label' => esc_html__( 'Remove Item', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + 'condition' => [ + 'show_remove_icon!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'remove_item_button_size', + [ + 'label' => esc_html__( 'Icon Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--remove-item-button-size: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'show_remove_icon!' => '', + ], + ] + ); + + $this->start_controls_tabs( + 'cart_remove_item_button_style', + [ + 'condition' => [ + 'show_remove_icon!' => '', + ], + ] + ); + + $this->start_controls_tab( + 'remove_item_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + 'condition' => [ + 'show_remove_icon!' => '', + ], + ] + ); + + $this->add_control( + 'remove_item_button_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--remove-item-button-color: {{VALUE}}', + ], + 'condition' => [ + 'show_remove_icon!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'remove_item_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + 'condition' => [ + 'show_remove_icon!' => '', + ], + ] + ); + + $this->add_control( + 'remove_item_button_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--remove-item-button-hover-color: {{VALUE}};', + ], + 'condition' => [ + 'show_remove_icon!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'heading_subtotal_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Subtotal', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'subtotal_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--menu-cart-subtotal-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'subtotal_typography', + 'selector' => '{{WRAPPER}} .elementor-menu-cart__subtotal', + ] + ); + + $this->add_responsive_control( + 'subtotal_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--menu-cart-subtotal-text-align: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'subtotal_divider_style', + [ + 'label' => esc_html__( 'Divider Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'solid' => esc_html__( 'Solid', 'elementor-pro' ), + 'double' => esc_html__( 'Double', 'elementor-pro' ), + 'dotted' => esc_html__( 'Dotted', 'elementor-pro' ), + 'dashed' => esc_html__( 'Dashed', 'elementor-pro' ), + 'groove' => esc_html__( 'Groove', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}} .widget_shopping_cart_content' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + '' => '--subtotal-divider-left-width: 0; --subtotal-divider-right-width: 0;', + 'solid' => '--subtotal-divider-style: solid;', + 'double' => '--subtotal-divider-style: double;', + 'dotted' => '--subtotal-divider-style: dotted;', + 'dashed' => '--subtotal-divider-style: dashed;', + 'groove' => '--subtotal-divider-style: groove;', + ], + ] + ); + + $this->add_responsive_control( + 'subtotal_divider_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .widget_shopping_cart_content' => '--subtotal-divider-top-width: {{TOP}}{{UNIT}}; --subtotal-divider-right-width: {{RIGHT}}{{UNIT}}; --subtotal-divider-bottom-width: {{BOTTOM}}{{UNIT}}; --subtotal-divider-left-width: {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'subtotal_divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .widget_shopping_cart_content' => '--subtotal-divider-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_product_tabs_style', + [ + 'label' => esc_html__( 'Products', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'heading_product_title_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Product Title', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'product_title_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-menu-cart__product-name a', + ] + ); + + $this->start_controls_tabs( 'product_title_colors' ); + + $this->start_controls_tab( 'product_title_normal_colors', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'product_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-cart__product-name a' => 'color: {{VALUE}};', + + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'product_title_hover_colors', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'product_title_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-cart__product-name a:hover' => 'color: {{VALUE}};', + + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'heading_product_variations_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Variations', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'product_variations_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--product-variations-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'product_variations_typography', + 'selector' => '{{WRAPPER}} .elementor-menu-cart__product .variation', + ] + ); + + $this->add_control( + 'heading_product_price_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Product Price', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'product_price_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--product-price-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'product_price_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-menu-cart__product-price', + ] + ); + + $this->add_control( + 'heading_quantity_title_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Quantity', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'product_quantity_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-cart__product-price .product-quantity' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'product_quantity_typography', + 'selector' => '{{WRAPPER}} .elementor-menu-cart__product-price .product-quantity', + ] + ); + + $this->add_control( + 'heading_product_divider_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Divider', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'divider_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'solid' => esc_html__( 'Solid', 'elementor-pro' ), + 'double' => esc_html__( 'Double', 'elementor-pro' ), + 'dotted' => esc_html__( 'Dotted', 'elementor-pro' ), + 'dashed' => esc_html__( 'Dashed', 'elementor-pro' ), + 'groove' => esc_html__( 'Groove', 'elementor-pro' ), + ], + 'selectors' => [ + '{{WRAPPER}}' => '--divider-style: {{VALUE}}; --subtotal-divider-style: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--divider-color: {{VALUE}}; --subtotal-divider-color: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'divider_width', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 10, + ], + 'em' => [ + 'max' => 1, + ], + 'rem' => [ + 'max' => 1, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--divider-width: {{SIZE}}{{UNIT}}; --subtotal-divider-top-width: {{SIZE}}{{UNIT}}; --subtotal-divider-right-width: {{SIZE}}{{UNIT}}; --subtotal-divider-bottom-width: {{SIZE}}{{UNIT}}; --subtotal-divider-left-width: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'divider_gap', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--product-divider-gap: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_buttons', + [ + 'label' => esc_html__( 'Buttons', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => 'view_cart_button_show', + 'operator' => '!=', + 'value' => '', + ], + [ + 'name' => 'checkout_button_show', + 'operator' => '!=', + 'value' => '', + ], + ], + ], + ] + ); + + $this->add_responsive_control( + 'buttons_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'inline' => esc_html__( 'Inline', 'elementor-pro' ), + 'stacked' => esc_html__( 'Stacked', 'elementor-pro' ), + ], + 'default' => 'inline', + 'devices' => [ 'desktop', 'tablet', 'mobile' ], + 'condition' => [ + 'view_cart_button_show!' => '', + 'checkout_button_show!' => '', + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'inline' => '--cart-footer-layout: 1fr 1fr; --products-max-height-sidecart: calc(100vh - 240px); --products-max-height-minicart: calc(100vh - 385px)', + 'stacked' => '--cart-footer-layout: 1fr; --products-max-height-sidecart: calc(100vh - 300px); --products-max-height-minicart: calc(100vh - 450px)', + ], + ] + ); + + $this->add_responsive_control( + 'space_between_buttons', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--space-between-buttons: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'view_cart_button_show!' => '', + 'checkout_button_show!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'product_buttons_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-menu-cart__footer-buttons .elementor-button', + ] + ); + + $this->add_control( + 'button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--cart-footer-buttons-border-radius: {{SIZE}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'heading_view_cart_button_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'View Cart', 'elementor-pro' ), + 'condition' => [ + 'view_cart_button_show!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'view_cart_buttons_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-menu-cart__footer-buttons a.elementor-button--view-cart', + 'separator' => 'before', + 'condition' => [ + 'view_cart_button_show!' => '', + ], + ] + ); + + $this->start_controls_tabs( + 'view_cart_button_text_colors', + [ + 'condition' => [ + 'view_cart_button_show!' => '', + ], + ] + ); + + $this->start_controls_tab( + 'heading_view_cart_button_normal_style', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + 'condition' => [ + 'view_cart_button_show!' => '', + ], + ] + ); + + $this->add_control( + 'view_cart_button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--view-cart-button-text-color: {{VALUE}};', + ], + 'condition' => [ + 'view_cart_button_show!' => '', + ], + ] + ); + + $this->add_control( + 'view_cart_button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--view-cart-button-background-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'heading_view_cart_button_hover_style', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + 'condition' => [ + 'view_cart_button_show!' => '', + ], + ] + ); + + $this->add_control( + 'view_cart_button_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--view-cart-button-hover-text-color: {{VALUE}};', + ], + 'condition' => [ + 'view_cart_button_show!' => '', + ], + ] + ); + + $this->add_control( + 'view_cart_button_hover_background', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--view-cart-button-hover-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'view_cart_button_border_hover_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-cart__footer-buttons .elementor-button--view-cart:hover' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + 'view_cart_border_border!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'view_cart_border', + 'selector' => '{{WRAPPER}} .elementor-button--view-cart', + 'separator' => 'before', + 'condition' => [ + 'view_cart_button_show!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'view_cart_button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-cart__footer-buttons a.elementor-button--view-cart' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'view_cart_button_show!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'view_cart_button_box_shadow', + 'selector' => '{{WRAPPER}} .elementor-button--view-cart', + 'condition' => [ + 'view_cart_button_show!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'view_cart_button_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--view-cart-button-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'view_cart_button_show!' => '', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'heading_checkout_button_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Checkout', 'elementor-pro' ), + 'condition' => [ + 'checkout_button_show!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'cart_checkout_button_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .elementor-menu-cart__footer-buttons a.elementor-button--checkout', + 'separator' => 'before', + 'condition' => [ + 'checkout_button_show!' => '', + ], + ] + ); + + $this->start_controls_tabs( + 'cart_checkout_button_text_colors', + [ + 'condition' => [ + 'checkout_button_show!' => '', + ], + ] + ); + + $this->start_controls_tab( + 'heading_cart_checkout_button_normal_style', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + 'condition' => [ + 'checkout_button_show!' => '', + ], + ] + ); + + $this->add_control( + 'checkout_button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--checkout-button-text-color: {{VALUE}};', + ], + 'condition' => [ + 'checkout_button_show!' => '', + ], + ] + ); + + $this->add_control( + 'checkout_button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--checkout-button-background-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'heading_cart_checkout_button_hover_style', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + 'condition' => [ + 'checkout_button_show!' => '', + ], + ] + ); + + $this->add_control( + 'checkout_button_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--checkout-button-hover-text-color: {{VALUE}};', + ], + 'condition' => [ + 'checkout_button_show!' => '', + ], + ] + ); + + $this->add_control( + 'checkout_button_hover_background', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--checkout-button-hover-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'checkout_button_border_hover_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-cart__footer-buttons .elementor-button--checkout:hover' => 'border-color: {{VALUE}};', + ], + 'condition' => [ + 'checkout_border_border!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'checkout_border', + 'selector' => '{{WRAPPER}} .elementor-button--checkout', + 'separator' => 'before', + 'condition' => [ + 'checkout_button_show!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'view_checkout_button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .elementor-menu-cart__footer-buttons a.elementor-button--checkout' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'checkout_button_show!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'view_checkout_button_box_shadow', + 'selector' => '{{WRAPPER}} .elementor-button--checkout', + 'condition' => [ + 'checkout_button_show!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'view_checkout_button_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--checkout-button-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'checkout_button_show!' => '', + ], + 'separator' => 'after', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_style_messages', + [ + 'label' => esc_html__( 'Messages', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'cart_empty_message_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}} .woocommerce-mini-cart__empty-message', + ] + ); + + $this->add_control( + 'empty_message_color', + [ + 'label' => esc_html__( 'Empty Cart Message Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--empty-message-color: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'empty_message_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--empty-message-alignment: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + } + + /** + * Check if user did not explicitly disabled the use of our mini-cart template and set the option accordingly. + * The option value is later used by Module::woocommerce_locate_template(). + */ + private function maybe_use_mini_cart_template() { + $option_value = get_option( 'elementor_' . Module::OPTION_NAME_USE_MINI_CART, '' ); + if ( empty( $option_value ) || 'initial' === $option_value ) { + update_option( 'elementor_' . Module::OPTION_NAME_USE_MINI_CART, 'yes' ); + } + } + + protected function render() { + $settings = $this->get_settings_for_display(); + if ( ! wp_script_is( 'wc-cart-fragments' ) ) { + wp_enqueue_script( 'wc-cart-fragments' ); + } + + $this->maybe_use_mini_cart_template(); + Module::render_menu_cart( $settings ); + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/my-account.php b/modules/woocommerce/widgets/my-account.php new file mode 100644 index 0000000..8dccaa5 --- /dev/null +++ b/modules/woocommerce/widgets/my-account.php @@ -0,0 +1,2164 @@ +start_controls_section( + 'section_menu_icon_content', + [ + 'label' => esc_html__( 'Tabs', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'tabs_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'vertical' => esc_html__( 'Vertical', 'elementor-pro' ), + 'horizontal' => esc_html__( 'Horizontal', 'elementor-pro' ), + ], + 'default' => 'vertical', + 'render_type' => 'template', + 'prefix_class' => 'e-my-account-tabs-', + ] + ); + + $this->add_responsive_control( + 'tabs_content_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--tab-content-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'tabs_position', + [ + 'label' => esc_html__( 'Tabs Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => "eicon-align-$start-h", + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-align-center-h', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => "eicon-align-$end-h", + ], + 'stretch' => [ + 'title' => esc_html__( 'Stretch', 'elementor-pro' ), + 'icon' => 'eicon-align-stretch-h', + ], + ], + 'condition' => [ + 'tabs_layout' => 'horizontal', + ], + 'selectors' => [ + '{{WRAPPER}}' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'start' => '--tabs-container-justify-content: flex-start; --tab-width: auto', + 'center' => '--tabs-container-justify-content: center; --tab-width: auto', + 'end' => '--tabs-container-justify-content: flex-end; --tab-width: auto', + 'stretch' => '--tabs-container-justify-content: space-between; --tab-width: 100%', + ], + ] + ); + + $repeater = new Repeater(); + + $repeater->add_control( + 'tab_name', + [ + 'label' => esc_html__( 'Tab Name', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $repeater->add_control( + 'order_display_description', + [ + 'raw' => esc_html__( 'Note: By default, only your last order is displayed while editing the orders section. You can see other orders on your live site or in the WooCommerce orders section', 'elementor-pro' ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-descriptor', + 'condition' => [ + 'field_key' => 'orders', + ], + ] + ); + + $this->add_control( + 'tabs', + [ + 'label' => '', + 'type' => Controls_Manager::REPEATER, + 'fields' => $repeater->get_controls(), + 'item_actions' => [ + 'add' => false, + 'duplicate' => false, + 'remove' => false, + 'sort' => false, + ], + 'default' => [ + [ + 'field_key' => 'dashboard', + 'field_label' => esc_html__( 'Dashboard', 'elementor-pro' ), + 'tab_name' => esc_html__( 'Dashboard', 'elementor-pro' ), + ], + [ + 'field_key' => 'orders', + 'field_label' => esc_html__( 'Orders', 'elementor-pro' ), + 'tab_name' => esc_html__( 'Orders', 'elementor-pro' ), + ], + [ + 'field_key' => 'downloads', + 'field_label' => esc_html__( 'Downloads', 'elementor-pro' ), + 'tab_name' => esc_html__( 'Downloads', 'elementor-pro' ), + ], + [ + 'field_key' => 'edit-address', + 'field_label' => esc_html__( 'Addresses', 'elementor-pro' ), + 'tab_name' => esc_html__( 'Addresses', 'elementor-pro' ), + ], + [ + 'field_key' => 'edit-account', + 'field_label' => esc_html__( 'Account Details', 'elementor-pro' ), + 'tab_name' => esc_html__( 'Account Details', 'elementor-pro' ), + ], + [ + 'field_key' => 'customer-logout', + 'field_label' => esc_html__( 'Logout', 'elementor-pro' ), + 'tab_name' => esc_html__( 'Logout', 'elementor-pro' ), + ], + ], + 'title_field' => '{{{ tab_name }}}', + ] + ); + + $this->add_responsive_control( + 'tabs_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-alignment: {{VALUE}};', + ], + 'conditions' => [ + 'relation' => 'and', + 'terms' => [ + [ + 'name' => 'tabs_position', + 'operator' => '!==', + 'value' => 'start', + ], + [ + 'name' => 'tabs_position', + 'operator' => '!==', + 'value' => 'center', + ], + [ + 'name' => 'tabs_position', + 'operator' => '!==', + 'value' => 'end', + ], + ], + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_additional_options', + [ + 'label' => esc_html__( 'Additional Options', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'customize_dashboard_check', + [ + 'label' => esc_html__( 'Customize Dashboard', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), + 'label_off' => esc_html__( 'No', 'elementor-pro' ), + 'frontend_available' => true, + 'render_type' => 'template', + ] + ); + + $this->add_control( + 'customize_dashboard_description', + [ + 'raw' => sprintf( + /* translators: 1: Saved templates link opening tag. 2: Link closing tag. */ + esc_html__( 'Replaces the default WooCommerce customer dashboard screen with a custom template. (Don\'t have one? Head over to %1$sSaved Templates%2$s.)', 'elementor-pro' ), + sprintf( '', admin_url( 'edit.php?post_type=elementor_library&tabs_group=library#add_new' ) ), + '' + ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-control-field-description elementor-descriptor elementor-descriptor-subtle', + 'condition' => [ + 'customize_dashboard_check' => 'yes', + ], + ] + ); + + $this->add_control( + 'customize_dashboard_select_heading', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Choose template', 'elementor-pro' ), + 'condition' => [ + 'customize_dashboard_check' => 'yes', + ], + ] + ); + + $document_types = Plugin::elementor()->documents->get_document_types( [ + 'show_in_library' => true, + ] ); + + $this->add_control( + 'customize_dashboard_select', + [ + 'type' => QueryControlModule::QUERY_CONTROL_ID, + 'label_block' => true, + 'show_label' => false, + 'autocomplete' => [ + 'object' => QueryControlModule::QUERY_OBJECT_LIBRARY_TEMPLATE, + 'query' => [ + 'meta_query' => [ + [ + 'key' => Document::TYPE_META_KEY, + 'value' => array_keys( $document_types ), + 'compare' => 'IN', + ], + ], + ], + ], + 'condition' => [ + 'customize_dashboard_check' => 'yes', + ], + 'render_type' => 'template', + ] + ); + + $this->add_control( + 'edit_button', + [ + 'raw' => sprintf( '%s', esc_html__( 'Edit Template', 'elementor-pro' ) ), + 'type' => Controls_Manager::RAW_HTML, + 'content_classes' => 'elementor-edit-template-wrapper', + 'condition' => [ + 'customize_dashboard_check' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'tabs_style', + [ + 'label' => esc_html__( 'Tabs', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'tabs_typography', + 'selector' => '{{WRAPPER}} .e-my-account-tab .woocommerce .woocommerce-MyAccount-navigation ul li a', + ] + ); + + $this->start_controls_tabs( 'tabs_section' ); + + $this->start_controls_tab( 'tabs_normal', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'tabs_normal_background', + 'selector' => '{{WRAPPER}} .e-my-account-tab .woocommerce .woocommerce-MyAccount-navigation ul li:not(.is-active) a', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'tabs_normal_box_shadow', + 'selector' => '{{WRAPPER}} .e-my-account-tab .woocommerce .woocommerce-MyAccount-navigation ul li:not(.is-active) a', + ] + ); + + $this->add_control( + 'tabs_normal_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-normal-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'tabs_hover', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'tabs_hover_background', + 'selector' => '{{WRAPPER}} .e-my-account-tab .woocommerce .woocommerce-MyAccount-navigation ul li a:hover', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'tabs_hover_box_shadow', + 'selector' => '{{WRAPPER}} .e-my-account-tab .woocommerce .woocommerce-MyAccount-navigation ul li a:hover', + ] + ); + + $this->add_control( + 'tabs_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-hover-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'tabs_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-hover-border-color: {{VALUE}}', + ], + 'condition' => [ + 'tabs_border_type!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'tabs_active', [ 'label' => esc_html__( 'Active', 'elementor-pro' ) ] ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'tabs_active_background', + 'selector' => '{{WRAPPER}} .e-my-account-tab .woocommerce .woocommerce-MyAccount-navigation ul li.is-active a', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'tabs_active_box_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-MyAccount-navigation ul li.is-active a', + ] + ); + + $this->add_control( + 'tabs_active_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-active-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'tabs_active_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-active-border-color: {{VALUE}}', + ], + 'condition' => [ + 'tabs_border_type!' => '', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'tabs_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'tabs_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-my-account-tab .woocommerce .woocommerce-MyAccount-navigation ul li a' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'tabs_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'tabs_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-border-color: {{VALUE}};', + ], + 'condition' => [ + 'tabs_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'tabs_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'tabs_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'tabs_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'tabs_divider_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Dividers', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'tabs_divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-divider-color: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'tabs_divider_weight', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--tabs-divider-weight: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'sections_title', + [ + 'label' => esc_html__( 'Sections', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'my_account_sections_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'my_account_sections_box_shadow', + 'selector' => '{{WRAPPER}} .e-my-account-tab__dashboard:not(.e-my-account-tab__dashboard--custom) .woocommerce-MyAccount-content-wrapper, {{WRAPPER}} .e-my-account-tab__orders .woocommerce-MyAccount-content-wrapper, {{WRAPPER}} .e-my-account-tab__downloads .woocommerce-MyAccount-content-wrapper, {{WRAPPER}} address, {{WRAPPER}} .e-my-account-tab__edit-account .woocommerce-MyAccount-content-wrapper, {{WRAPPER}} .e-my-account-tab__view-order .order_details, {{WRAPPER}} .woocommerce-form-login, {{WRAPPER}} .woocommerce-form-register, {{WRAPPER}} .woocommerce-ResetPassword, {{WRAPPER}} .e-my-account-tab__payment-methods .woocommerce-MyAccount-content-wrapper', + ] + ); + + $this->add_control( + 'sections_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-type: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'sections_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-top-width: {{TOP}}{{UNIT}}; --sections-border-right-width: {{RIGHT}}{{UNIT}}; --sections-border-bottom-width: {{BOTTOM}}{{UNIT}}; --sections-border-left-width: {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'sections_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'sections_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-color: {{VALUE}};', + ], + 'condition' => [ + 'sections_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'sections_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'sections_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}; --edit-link-margin-top: {{TOP}}{{UNIT}}; --edit-link-margin-start: {{LEFT}}{{UNIT}};', + '{{WRAPPER}} .e-my-account-tab__edit-address .woocommerce-Address address' => 'padding-top: calc( {{TOP}}{{UNIT}} + 40px );', + '{{WRAPPER}} .woocommerce-pagination' => 'padding-bottom: {{BOTTOM}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'typography_title', + [ + 'label' => esc_html__( 'Typography', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'typography_titles', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Section Titles', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'typography_section_titles_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--typography-section-titles-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'section_titles_typography', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) h2, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) h3', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'section_titles_typography_text_shadow', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) h2, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) h3', + ] + ); + + $this->add_responsive_control( + 'section_title_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--section-title-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'typography_secondary_titles', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'General Text', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'general_text_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--general-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'general_text_typography', + 'selector' => '{{WRAPPER}} .woocommerce-MyAccount-content > div > p, {{WRAPPER}} address, {{WRAPPER}} .woocommerce-EditAccountForm fieldset legend, {{WRAPPER}} .woocommerce-ResetPassword p:nth-child(1), {{WRAPPER}} .woocommerce-OrderUpdate', + ] + ); + + $this->add_control( + 'typography_login_messages_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Login Messages', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'login_messages_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--login-messages-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'login_messages_typography', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .register p:not([class]), {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce em', + ] + ); + + $this->add_control( + 'checkboxes_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Checkboxes', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'checkboxes_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--checkboxes-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'checkboxes_typography', + 'selector' => '{{WRAPPER}} .woocommerce-form__label-for-checkbox span', + ] + ); + + $this->add_control( + 'payment_methods_radio_buttons_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Radio Buttons', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'payment_methods_radio_buttons_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--payment-methods-radio-buttons-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'payment_methods_radio_buttons_typography', + 'selector' => '{{WRAPPER}} .woocommerce-PaymentMethod .input-radio + label', + ] + ); + + $this->add_control( + 'links_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Links', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->start_controls_tabs( 'links_colors' ); + + $this->start_controls_tab( 'links_normal_colors', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'links_normal_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--links-normal-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'links_hover_colors', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'links_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--links-hover-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'forms_section', + [ + 'label' => esc_html__( 'Forms', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'forms_columns_gap', + [ + 'label' => esc_html__( 'Columns Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-columns-gap-padding-right: calc( {{SIZE}}{{UNIT}}/2 ); --forms-columns-gap-padding-left: calc( {{SIZE}}{{UNIT}}/2 ); --forms-columns-gap-margin-left: calc( -{{SIZE}}{{UNIT}}/2 ); --forms-columns-gap-margin-right: calc( -{{SIZE}}{{UNIT}}/2 );', + ], + ] + ); + + $this->add_responsive_control( + 'forms_rows_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-rows-gap: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'forms_label_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Labels', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'forms_label_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-labels-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'forms_label_typography', + 'selector' => '{{WRAPPER}} .woocommerce-form-row label, {{WRAPPER}} .woocommerce-address-fields label', + ] + ); + + $this->add_responsive_control( + 'forms_label_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-label-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'forms_field_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Fields', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'forms_field_typography', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .form-row .input-text, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .form-row select, {{WRAPPER}} ::placeholder, {{WRAPPER}} .select2-container--default .select2-selection--single, .select2-results__option, {{WRAPPER}} .e-my-account-tab__payment-methods input[type=text]', + + ] + ); + + $this->start_controls_tabs( 'forms_fields_styles' ); + + $this->start_controls_tab( 'forms_fields_normal_styles', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'forms_fields_normal_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-normal-color: {{VALUE}};', + '.e-woo-select2-wrapper .select2-results__option' => 'color: {{VALUE}};', + // style select2 arrow + '{{WRAPPER}} .select2-container--default .select2-selection--single .select2-selection__arrow b' => 'border-color: {{VALUE}} transparent transparent transparent;', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_fields_normal_background', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .form-row .input-text, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .form-row select, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .select2-container--default .select2-selection--single, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .select2-container--default, .select2-results__option, {{WRAPPER}} .e-my-account-tab__payment-methods input[type=text]', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_fields_normal_box_shadow', + 'selector' => '{{WRAPPER}} .input-text, {{WRAPPER}} select, {{WRAPPER}} .select2-container--default .select2-selection--single, {{WRAPPER}} .e-my-account-tab__payment-methods input[type=text]', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'forms_fields_focus_styles', [ 'label' => esc_html__( 'Focus', 'elementor-pro' ) ] ); + + $this->add_control( + 'forms_fields_focus_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-focus-color: {{VALUE}}', + '.e-woo-select2-wrapper .select2-results__option:focus' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_fields_focus_background', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .form-row .input-text:focus, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .form-row select:focus, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .select2-container--default.select2-container--focus .select2-selection--single, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .select2-container--default.select2-container--focus, {{WRAPPER}} .e-my-account-tab__payment-methods input[type=text]:focus', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_fields_focus_box_shadow', + 'selector' => '{{WRAPPER}} .input-text:focus, {{WRAPPER}} select:focus, {{WRAPPER}} .select2-container--default .select2-selection--single:focus, {{WRAPPER}} .e-my-account-tab__payment-methods input[type=text]:focus', + ] + ); + + $this->add_control( + 'forms_fields_focus_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .form-row .input-text:focus, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .form-row select:focus, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .select2-container--default.select2-container--focus, .select2-results__option:focus, {{WRAPPER}} .e-my-account-tab__payment-methods input[type=text]:focus' => 'border-color: {{VALUE}}', + ], + 'condition' => [ + 'forms_fields_border_border!' => '', + ], + ] + ); + + $this->add_control( + 'forms_fields_focus_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-focus-transition-duration: {{SIZE}}ms', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'forms_fields_border', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .form-row .input-text, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .form-row select, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .select2-container--default, {{WRAPPER}} .e-my-account-tab__payment-methods input[type=text]', + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'forms_fields_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'forms_fields_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-fields-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + // style select2 + '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .select2-container--default .select2-selection--single .select2-selection__rendered' => 'line-height: calc( ({{TOP}}{{UNIT}}*2) + 16px ); padding-left: {{LEFT}}{{UNIT}}; padding-right: {{RIGHT}}{{UNIT}};', + '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .select2-container--default .select2-selection--single .select2-selection__arrow' => 'height: calc( ({{TOP}}{{UNIT}}*2) + 16px ); right: {{RIGHT}}{{UNIT}};', + '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .select2-container--default .select2-selection--single' => 'height: auto;', + ], + ] + ); + + $this->add_control( + 'forms_button_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Buttons', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'forms_button_typography', + 'selector' => '{{WRAPPER}} button.button, {{WRAPPER}} #add_payment_method #payment #place_order', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'forms_button_text_shadow', + 'selector' => '{{WRAPPER}} button.button, {{WRAPPER}} #add_payment_method #payment #place_order', + ] + ); + + $this->start_controls_tabs( 'forms_buttons_styles' ); + + $this->start_controls_tab( 'forms_buttons_normal_styles', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'forms_buttons_normal_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-normal-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_buttons_background', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-EditAccountForm .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-address-fields .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .login .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .register .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .woocommerce-ResetPassword .button, {{WRAPPER}} #add_payment_method #payment #place_order', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_buttons_normal_box_shadow', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-EditAccountForm .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-address-fields .button, {{WRAPPER}} button.button, {{WRAPPER}} #add_payment_method #payment #place_order', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'forms_buttons_hover_styles', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'forms_buttons_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-hover-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'forms_buttons_hover_background', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-EditAccountForm .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-address-fields .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .login .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .register .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .woocommerce-ResetPassword .button:hover, {{WRAPPER}} #add_payment_method #payment #place_order:hover', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'forms_buttons_focus_box_shadow', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-EditAccountForm .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-address-fields .button:hover, {{WRAPPER}} button.button:hover, {{WRAPPER}} #add_payment_method #payment #place_order:hover', + ] + ); + + $this->add_control( + 'forms_buttons_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-EditAccountForm .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-address-fields .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .login .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .register .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .woocommerce-ResetPassword .button:hover, {{WRAPPER}} #add_payment_method #payment #place_order:hover' => 'border-color: {{VALUE}}', + ], + 'condition' => [ + 'forms_buttons_border_border!' => '', + ], + ] + ); + + $this->add_control( + 'forms_buttons_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-hover-transition-duration: {{SIZE}}ms', + ], + ] + ); + + $this->add_control( + 'forms_buttons_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + 'frontend_available' => true, + 'render_type' => 'template', + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'forms_buttons_border', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-EditAccountForm .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-address-fields .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .login .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .register .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .woocommerce-ResetPassword .button, {{WRAPPER}} #add_payment_method #payment #place_order', + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'forms_buttons_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'forms_buttons_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--forms-buttons-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'tables_section', + [ + 'label' => esc_html__( 'Order Details', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'tables_rows_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-rows-gap-top: calc( {{SIZE}}{{UNIT}}/2 ); --order-summary-rows-gap-bottom: calc( {{SIZE}}{{UNIT}}/2 );', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'tables_titles', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Titles & Totals', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'tables_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tables-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'tables_titles_typography', + 'selector' => '{{WRAPPER}} .order_details thead th, {{WRAPPER}} .order_details tfoot td, {{WRAPPER}} .order_details tfoot th, {{WRAPPER}} .nobr', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'tables_titles_text_shadow', + 'selector' => '{{WRAPPER}} .order_details thead th, {{WRAPPER}} .order_details tfoot td, {{WRAPPER}} .order_details tfoot th, {{WRAPPER}} .nobr', + ] + ); + + $this->add_control( + 'tables_items_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Items', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'tables_items_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tables-items-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'tables_items_typography', + 'selector' => '{{WRAPPER}} .e-my-account-tab__orders tbody td, {{WRAPPER}} .e-my-account-tab__downloads tbody td, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .product-quantity, {{WRAPPER}} .woocommerce-table--order-downloads tbody td, {{WRAPPER}} .woocommerce-table--order-details td a, {{WRAPPER}} td.product-total, {{WRAPPER}} td.payment-method-method, {{WRAPPER}} td.payment-method-expires', + ] + ); + + $this->add_control( + 'variations_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Variations', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'variations_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--variations-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'variations_typography', + 'selector' => '{{WRAPPER}} .wc-item-meta', + ] + ); + + $this->add_control( + 'sections_links_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Product Link', 'elementor-pro' ), + ] + ); + + $this->start_controls_tabs( 'tables_links_colors' ); + + $this->start_controls_tab( 'tables_links_normal_colors', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'tables_links_normal_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tables-links-normal-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'tables_links_hover_colors', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'tables_links_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tables-links-hover-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'tables_divider_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Dividers', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'tables_divider_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--tables-divider-border-type: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'tables_divider_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--tables-divider-border-width: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'tables_divider_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'tables_divider_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tables-divider-border-color: {{VALUE}};', + ], + 'condition' => [ + 'tables_divider_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'tables_button_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Buttons', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'tables_button_typography', + 'selector' => '{{WRAPPER}} .shop_table .button, {{WRAPPER}} .order-again .button, {{WRAPPER}} .woocommerce-pagination .button, {{WRAPPER}} .e-my-account-tab__payment-methods .woocommerce-MyAccount-content-wrapper .button', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'tables_button_text_shadow', + 'selector' => '{{WRAPPER}} .shop_table .button, {{WRAPPER}} .order-again .button, {{WRAPPER}} .woocommerce-pagination .button, {{WRAPPER}} .e-my-account-tab__payment-methods .woocommerce-MyAccount-content-wrapper .button', + ] + ); + + $this->start_controls_tabs( 'tables_button_styles' ); + + $this->start_controls_tab( 'tables_button_styles_normal', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'tables_button_normal_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tables-button-normal-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'tables_button_normal_background', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .shop_table .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .order-again .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .woocommerce-pagination .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom).e-my-account-tab__payment-methods .woocommerce-MyAccount-content-wrapper .button', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'tables_button_normal_box_shadow', + 'selector' => '{{WRAPPER}} .shop_table .button, {{WRAPPER}} .order-again .button, {{WRAPPER}} .woocommerce-pagination .button, {{WRAPPER}} .e-my-account-tab__payment-methods .woocommerce-MyAccount-content-wrapper .button', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'tables_button_styles_hover', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'tables_button_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .shop_table .button:hover, {{WRAPPER}} .woocommerce-pagination .button:hover, {{WRAPPER}} .order-again .button:hover, {{WRAPPER}} .e-my-account-tab__payment-methods .woocommerce .woocommerce-MyAccount-content-wrapper .button:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'tables_button_hover_background', + 'selector' => '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .shop_table .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .order-again .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .woocommerce-pagination .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom).e-my-account-tab__payment-methods .woocommerce-MyAccount-content-wrapper .button:hover', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'tables_button_hover_box_shadow', + 'selector' => '{{WRAPPER}} .shop_table .button:hover, {{WRAPPER}} .order-again .button:hover, {{WRAPPER}} .woocommerce-pagination .button:hover, {{WRAPPER}} .e-my-account-tab__payment-methods .woocommerce-MyAccount-content-wrapper .button:hover', + ] + ); + + $this->add_control( + 'tables_button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .shop_table .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .order-again .button:hover, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce-pagination .button:hover, {{WRAPPER}} .e-my-account-tab__payment-methods:not(.e-my-account-tab__dashboard--custom) .woocommerce-MyAccount-content-wrapper .button:hover' => 'border-color: {{VALUE}}', + ], + 'condition' => [ + 'tables_button_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'tables_button_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--tables-button-hover-transition-duration: {{SIZE}}ms', + ], + ] + ); + + $this->add_control( + 'tables_button_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + 'frontend_available' => true, + 'render_type' => 'template', + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'tables_button_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--tables-buttons-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'tables_button_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .shop_table .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .order-again .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom) .woocommerce .woocommerce-pagination .button, {{WRAPPER}} .e-my-account-tab:not(.e-my-account-tab__dashboard--custom).e-my-account-tab__payment-methods .woocommerce-MyAccount-content-wrapper .button' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'tables_button_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'tables_button_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} ' => '--tables-buttons-border-color: {{VALUE}};', + ], + 'condition' => [ + 'tables_button_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'tables_button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--tables-button-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'tables_button_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--tables-button-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + } + + public function modify_menu_items( $items, $endpoints ) { + $settings = $this->get_settings_for_display(); + + if ( ! empty( $settings['tabs'] ) ) { + foreach ( $settings['tabs'] as $tab ) { + if ( isset( $tab['tab_name'] ) && isset( $items[ $tab['field_key'] ] ) ) { + $items[ $tab['field_key'] ] = $tab['tab_name']; + } + } + } + + return $items; + } + + /** + * WooCommerce Get My Account Page Permalink + * + * Modify the permalinks of the My Account menu items. By default the permalinks will go to the + * set WooCommerce My Account Page, even if the widget is on a different page. This function will override + * the permalinks to use the widget page URL as the base URL instead. + * + * This is a callback function for the woocommerce_get_myaccount_page_permalink filter. + * + * @since 3.5.0 + * + * @return string + */ + public function woocommerce_get_myaccount_page_permalink( $bool ) { + return get_permalink(); + } + + /** + * WooCommerce Logout Default Redirect URL + * + * Modify the permalink of the My Account Logout menu item. We add this so that we can add custom + * parameters to the URL, which we can later access to log the user out and redirect back to the widget + * page. Without this WooCommerce would have always just redirect back to the set My Account Page + * after log out. + * + * This is a callback function for the woocommerce_logout_default_redirect_url filter. + * + * @since 3.5.0 + * + * @return string + */ + public function woocommerce_logout_default_redirect_url( $redirect ) { + return $redirect . '?elementor_wc_logout=true&elementor_my_account_redirect=' . esc_url( get_permalink() ); + } + + protected function render() { + // Add actions & filters before displaying our Widget. + add_action( 'woocommerce_account_navigation', [ $this, 'woocommerce_account_navigation' ], 1 ); + add_filter( 'woocommerce_account_menu_items', [ $this, 'modify_menu_items' ], 10, 2 ); + add_action( 'woocommerce_account_content', [ $this, 'before_account_content' ], 2 ); + add_action( 'woocommerce_account_content', [ $this, 'after_account_content' ], 95 ); + add_filter( 'woocommerce_get_myaccount_page_permalink', [ $this, 'woocommerce_get_myaccount_page_permalink' ], 10, 1 ); + add_filter( 'woocommerce_logout_default_redirect_url', [ $this, 'woocommerce_logout_default_redirect_url' ], 10, 1 ); + add_filter( 'woocommerce_is_account_page', '__return_true' ); + + if ( $this->has_custom_template() && 'dashboard' === $this->get_current_endpoint() ) { + remove_action( 'woocommerce_account_content', 'woocommerce_account_content', 10 ); + add_action( 'woocommerce_account_content', [ $this, 'display_custom_template' ], 10 ); + } + + // Display our Widget. + if ( ! Plugin::elementor()->editor->is_edit_mode() ) { + $this->render_html_front_end(); + } else { + $this->render_html_editor(); + } + + // Remove actions & filters after displaying our Widget. + remove_action( 'woocommerce_account_navigation', [ $this, 'woocommerce_account_navigation' ], 2 ); + remove_action( 'woocommerce_account_menu_items', [ $this, 'modify_menu_items' ], 10 ); + remove_action( 'woocommerce_account_content', [ $this, 'before_account_content' ], 5 ); + remove_action( 'woocommerce_account_content', [ $this, 'after_account_content' ], 99 ); + remove_filter( 'woocommerce_get_myaccount_page_permalink', [ $this, 'woocommerce_get_myaccount_page_permalink' ], 10, 1 ); + remove_filter( 'woocommerce_logout_default_redirect_url', [ $this, 'woocommerce_logout_default_redirect_url' ], 10, 1 ); + remove_filter( 'woocommerce_is_account_page', '__return_true' ); + + if ( $this->has_custom_template() && 'dashboard' === $this->get_current_endpoint() ) { + remove_action( 'woocommerce_account_content', [ $this, 'display_custom_template' ], 10 ); + add_action( 'woocommerce_account_content', 'woocommerce_account_content', 10 ); + } + } + + /** + * Get Account Pages + * + * Get all the pages that would render on the My Account page. + * We will use this array to be able to render all these pages' content when the editor loads. + * We will then switch between the pages via JS as all the content is already on the page. + * + * @since 3.5.0 + * + * @return array + */ + private function get_account_pages() { + $pages = [ + 'dashboard' => '', + 'orders' => '', + 'downloads' => '', + 'edit-address' => '', + ]; + + // Check if payment gateways support add new payment methods. + $support_payment_methods = false; + foreach ( WC()->payment_gateways->get_available_payment_gateways() as $gateway ) { + if ( $gateway->supports( 'add_payment_method' ) || $gateway->supports( 'tokenization' ) ) { + $support_payment_methods = true; + break; + } + } + + if ( $support_payment_methods ) { + $pages['payment-methods'] = ''; + $pages['add-payment-method'] = ''; + } + + // Edit account. + $pages['edit-account'] = ''; + + // Get the latest order (if there is one) for view-order (order preview) page. + $recent_order = wc_get_orders( [ + 'limit' => 1, + 'orderby' => 'date', + 'order' => 'DESC', + ] ); + + if ( ! empty( $recent_order ) ) { + $pages['view-order'] = $recent_order[0]->get_id(); + } + + return $pages; + } + + /** + * Get Current Endpoint + * + * Used to determine which page Account Page the user is on currently. + * This is used so we can add a unique wrapper class around the page's content. + * + * @since 3.5.0 + * + * @return string + */ + private function get_current_endpoint() { + global $wp_query; + $current = ''; + + $pages = $this->get_account_pages(); + + foreach ( $pages as $page => $val ) { + if ( isset( $wp_query->query[ $page ] ) ) { + $current = $page; + break; + } + } + + if ( '' === $current && isset( $wp_query->query_vars['page'] ) ) { + $current = 'dashboard'; // Dashboard is not an endpoint so it needs a custom check. + } + + return $current; + } + + /** + * Render HTML Front End + * + * This function will output the content on the front-end. + * + * @since 3.5.0 + */ + private function render_html_front_end() { + $current_endpoint = $this->get_current_endpoint(); + $custom_dashboard_class = ''; + if ( 'dashboard' === $current_endpoint && $this->has_custom_template() && is_user_logged_in() ) { + $custom_dashboard_class = 'e-my-account-tab__dashboard--custom'; + } + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo ' + get_settings_for_display(); + // Add .e-my-account-tab__dashboard as the default class when the editor loads. + // This class will be replaced with JS when tabs are switched. + + $custom_dashboard_class = ''; + if ( $this->has_custom_template() && is_user_logged_in() ) { + $custom_dashboard_class = 'e-my-account-tab__dashboard--custom'; + } + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo ' + get_settings_for_display(); + + if ( 'horizontal' === $settings['tabs_layout'] ) { + ?> + + get_dashboard_template_id() ); + + return 0 < $template_id; + } + + /** + * Get Account Content Wrapper + * + * This function will determine the wrapper class around the main content. + * There are different wrappers depending on the following scenarios: + * 1. Are there orders/downloads or not. + * 2. A custom template been selected for the dashboard intro or not + * + * @since 3.5.0 + * + * @return string + */ + private function get_account_content_wrapper( $args ) { + $user_id = get_current_user_id(); + $num_orders = wc_get_customer_order_count( $user_id ); + $num_downloads = count( wc_get_customer_available_downloads( $user_id ) ); + $class = 'woocommerce-MyAccount-content-wrapper'; + $current_endpoint = $this->get_current_endpoint(); + + /* we need to render a different css class if there are no orders/downloads to display + * as the no orders/downloads screen should not have the default padding and border + * around it but show the 'no orders/downloads' notification only + */ + if ( 'frontend' === $args['context'] ) { // Front-end display + global $wp_query; + if ( ( 0 === $num_orders && isset( $wp_query->query_vars['orders'] ) ) || ( 0 === $num_downloads && isset( $wp_query->query_vars['downloads'] ) ) ) { + $class .= '-no-data'; + } + } else { // Editor display + if ( ( 0 === $num_orders && 'orders' === $args['page'] ) || ( 0 === $num_downloads && 'downloads' === $args['page'] ) ) { + $class .= '-no-data'; + } + } + + return $class; + } + + /** + * Before Account Content + * + * Output containing elements. Callback function for the woocommerce_account_content hook. + * + * This eliminates the need for template overrides. + * + * @since 3.5.0 + */ + public function before_account_content() { + $wrapper_class = $this->get_account_content_wrapper( [ 'context' => 'frontend' ] ); + + echo '
        '; + } + + /** + * Get Dashboard Template ID + * + * Get the template_id for the dashboard intro section if a custom template should be displayed + * + * @since 3.7.0 + * + * @return int + */ + public function get_dashboard_template_id() { + $settings = $this->get_settings_for_display(); + if ( 'yes' === $settings['customize_dashboard_check'] ) { + $template_id = intval( $settings['customize_dashboard_select'] ); + } else { + $template_id = 0; + } + + return $template_id; + } + + /** + * Display a custom template inside the My Account dashboard section + * + * @since 3.7.0 + */ + public function display_custom_template() { + $template_id = intval( $this->get_dashboard_template_id() ); + + if ( 0 < $template_id ) { + echo do_shortcode( '[elementor-template id="' . $template_id . '"]' ); + + do_action( 'woocommerce_account_dashboard' ); + do_action( 'woocommerce_before_my_account' ); + do_action( 'woocommerce_after_my_account' ); + } + } + + /** + * After Account Content + * + * Output containing elements. Callback function for the woocommerce_account_content hook. + * + * This eliminates the need for template overrides. + * + * @since 3.5.0 + */ + public function after_account_content() { + echo '
        '; + } + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/notices.php b/modules/woocommerce/widgets/notices.php new file mode 100644 index 0000000..400aabc --- /dev/null +++ b/modules/woocommerce/widgets/notices.php @@ -0,0 +1,130 @@ +start_controls_section( + 'section', + [ + 'label' => esc_html__( 'WooCommerce Notices', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'where_to_appear_notice', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => esc_html__( 'Drop this widget anywhere on the page or template where you want notices to appear.', 'elementor-pro' ), + 'content_classes' => 'elementor-descriptor', + ] + ); + + $this->add_control( + 'site_settings_notice', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => sprintf( + /* translators: 1: Link opening tag, 2: Link closing tag. */ + esc_html__( 'To change the design of your notices, go to your %1$sWooCommerce Settings%2$s', 'elementor-pro' ), + '', + '' + ), + 'content_classes' => 'elementor-descriptor elementor-descriptor-subtle', + ] + ); + + $this->add_control( + 'one_per_page_notice', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'heading' => esc_html__( 'Note:', 'elementor-pro' ), + 'content' => esc_html__( 'You can only add the Notices widget once per page.', 'elementor-pro' ), + ] + ); + + $this->end_controls_section(); + } + + private function hide_woocommerce_notices() { + ?> + + editor->is_edit_mode() || Plugin::elementor()->preview->is_preview_mode() ) { + $this->render_demo_notice(); + } else { + $this->hide_woocommerce_notices(); + ?> +
        + +
        + +
        + [ + '0' => [ + 'notice' => esc_html__( 'This is an example of a WooCommerce notice. (You won\'t see this while previewing your site.)', 'elementor-pro' ), + 'data' => [], + ], + ], + ] ); + ?> +
        + get_product(); + + if ( ! $product ) { + return; + } + + add_action( 'woocommerce_before_add_to_cart_quantity', [ $this, 'before_add_to_cart_quantity' ], 95 ); + add_action( 'woocommerce_before_add_to_cart_button', [ $this, 'before_add_to_cart_quantity' ], 5 ); + add_action( 'woocommerce_after_add_to_cart_button', [ $this, 'after_add_to_cart_button' ], 5 ); + ?> + +
        + is_loop_item() ) { + $this->render_loop_add_to_cart(); + } else { + woocommerce_template_single_add_to_cart(); + } ?> +
        + + get_loop_quantity_args(); + $button_args = [ 'quantity' => $quantity_args['min_value'] ]; + ?> +
        +
        + before_add_to_cart_quantity(); + + $this->render_loop_quantity_input( $quantity_args ); + woocommerce_template_loop_add_to_cart( $button_args ); + + $this->after_add_to_cart_button(); + ?> +
        +
        + get_type() + && 'yes' === $this->get_settings_for_display( 'show_quantity' ) + ) { + woocommerce_quantity_input( $quantity_args ); + } + } + + private function get_loop_quantity_args() { + global $product; + + $quantity_args = [ + 'min_value' => apply_filters( 'woocommerce_quantity_input_min', $product->get_min_purchase_quantity(), $product ), + 'max_value' => apply_filters( 'woocommerce_quantity_input_max', $product->get_max_purchase_quantity(), $product ), + 'input_value' => $product->get_min_purchase_quantity(), + 'classes' => [ 'input-text', 'qty', 'text' ], + ]; + + if ( 'no' === get_option( 'woocommerce_enable_ajax_add_to_cart' ) ) { + $quantity_args['min_value'] = $product->get_min_purchase_quantity(); + $quantity_args['input_value'] = $product->get_min_purchase_quantity(); + $quantity_args['classes'][] = 'disabled'; + } + + return $quantity_args; + } + + private function is_loop_item() { + return 'loop-item' === Plugin::elementor()->documents->get_current()->get_type(); + } + + private function is_loop_item_template_edit() { + return ( Plugin::elementor()->editor->is_edit_mode() && $this->is_loop_item() ); + } + + public function should_add_container() { + global $product; + + if ( ! in_array( $this->get_settings_for_display( 'layout' ), [ 'auto', 'stacked' ], true ) ) { + return false; + } + + switch ( current_action() ) { + case 'woocommerce_before_add_to_cart_quantity': + return in_array( $product->get_type(), [ 'simple', 'variable' ], true ); + case 'woocommerce_before_add_to_cart_button': + return in_array( $product->get_type(), [ 'grouped', 'external' ], true ); + case 'woocommerce_after_add_to_cart_button': + default: + return true; + } + } + + /** + * Before Add to Cart Quantity + * + * Added wrapper tag around the quantity input and "Add to Cart" button + * used to more solidly accommodate the layout when additional elements + * are added by 3rd party plugins. + * + * @since 3.6.0 + */ + public function before_add_to_cart_quantity() { + if ( ! $this->should_add_container() ) { + return; + } + ?> +
        + should_add_container() ) { + return; + } + ?> +
        + start_controls_section( + 'section_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => esc_html__( 'Inline', 'elementor-pro' ), + 'stacked' => esc_html__( 'Stacked', 'elementor-pro' ), + 'auto' => esc_html__( 'Auto', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-add-to-cart--layout-', + 'render_type' => 'template', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_atc_button_style', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'prefix_class' => 'elementor-add-to-cart%s--align-', + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'button_typography', + 'selector' => '{{WRAPPER}} .cart button, {{WRAPPER}} .cart .button', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'button_border', + 'selector' => '{{WRAPPER}} .cart button, {{WRAPPER}} .cart .button', + 'exclude' => [ 'color' ], + ] + ); + + $this->add_control( + 'button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .cart button, {{WRAPPER}} .cart .button' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'button_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .cart button, {{WRAPPER}} .cart .button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->start_controls_tabs( 'button_style_tabs' ); + + $this->start_controls_tab( 'button_style_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .cart button, {{WRAPPER}} .cart .button' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'button_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .cart button, {{WRAPPER}} .cart .button' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'button_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .cart button, {{WRAPPER}} .cart .button' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'button_style_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_text_color_hover', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .cart button:hover, {{WRAPPER}} .cart .button:hover' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'button_bg_color_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .cart button:hover, {{WRAPPER}} .cart .button:hover' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'button_border_color_hover', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .cart button:hover, {{WRAPPER}} .cart .button:hover' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'button_transition', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (s)', + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 0.2, + ], + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3, + 'step' => 0.1, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .cart button, {{WRAPPER}} .cart .button' => 'transition: all {{SIZE}}s', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'heading_view_cart_style', + [ + 'label' => esc_html__( 'View Cart', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'view_cart_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .added_to_cart' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'view_cart_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}} .added_to_cart', + ] + ); + + $this->add_responsive_control( + 'view_cart_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--view-cart-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_atc_quantity_style', + [ + 'label' => esc_html__( 'Quantity', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'show_quantity', + [ + 'label' => esc_html__( 'Quantity', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'return_value' => 'yes', + 'default' => 'yes', + 'prefix_class' => 'e-add-to-cart--show-quantity-', + 'render_type' => 'template', + ] + ); + + $this->add_responsive_control( + 'spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--button-spacing: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'show_quantity!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'quantity_typography', + 'selector' => '{{WRAPPER}} .quantity .qty', + 'condition' => [ + 'show_quantity!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'quantity_border', + 'selector' => '{{WRAPPER}} .quantity .qty', + 'exclude' => [ 'color' ], + 'condition' => [ + 'show_quantity!' => '', + ], + ] + ); + + $this->add_control( + 'quantity_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .quantity .qty' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'show_quantity!' => '', + ], + ] + ); + + $this->add_control( + 'quantity_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .quantity .qty' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'show_quantity!' => '', + ], + ] + ); + + $this->start_controls_tabs( 'quantity_style_tabs', + [ + 'condition' => [ + 'show_quantity!' => '', + ], + ] + ); + + $this->start_controls_tab( 'quantity_style_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'quantity_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .quantity .qty' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'quantity_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .quantity .qty' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'quantity_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .quantity .qty' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'quantity_style_focus', + [ + 'label' => esc_html__( 'Focus', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'quantity_text_color_focus', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .quantity .qty:focus' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'quantity_bg_color_focus', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .quantity .qty:focus' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'quantity_border_color_focus', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .quantity .qty:focus' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'quantity_transition', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (s)', + 'type' => Controls_Manager::SLIDER, + 'default' => [ + 'size' => 0.2, + ], + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3, + 'step' => 0.1, + ], + ], + 'selectors' => [ + '{{WRAPPER}} .quantity .qty' => 'transition: all {{SIZE}}s', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_atc_variations_style', + [ + 'label' => esc_html__( 'Variations', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'variations_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} form.cart .variations' => 'width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'variations_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} form.cart .variations' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'variations_space_between', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} form.cart table.variations tr th, .woocommerce {{WRAPPER}} form.cart table.variations tr td' => 'padding-top: calc( {{SIZE}}{{UNIT}}/2 ); padding-bottom: calc( {{SIZE}}{{UNIT}}/2 );', + ], + ] + ); + + $this->add_control( + 'heading_variations_label_style', + [ + 'label' => esc_html__( 'Label', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'variations_label_color_focus', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} form.cart table.variations label' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'variations_label_typography', + 'selector' => '.woocommerce {{WRAPPER}} form.cart table.variations label', + ] + ); + + $this->add_control( + 'heading_variations_select_style', + [ + 'label' => esc_html__( 'Select field', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'variations_select_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} form.cart table.variations td.value select' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'variations_select_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} form.cart table.variations td.value select, .woocommerce {{WRAPPER}} form.cart table.variations td.value:before' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'variations_select_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} form.cart table.variations td.value select, .woocommerce {{WRAPPER}} form.cart table.variations td.value:before' => 'border: 1px solid {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'variations_select_typography', + 'selector' => '.woocommerce {{WRAPPER}} form.cart table.variations td.value select, .woocommerce div.product.elementor{{WRAPPER}} form.cart table.variations td.value:before', + ] + ); + + $this->add_control( + 'variations_select_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} form.cart table.variations td.value select, .woocommerce {{WRAPPER}} form.cart table.variations td.value:before' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/product-additional-information.php b/modules/woocommerce/widgets/product-additional-information.php new file mode 100644 index 0000000..7119e0c --- /dev/null +++ b/modules/woocommerce/widgets/product-additional-information.php @@ -0,0 +1,110 @@ +start_controls_section( 'section_additional_info_style', [ + 'label' => esc_html__( 'General', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] ); + + $this->add_control( + 'show_heading', + [ + 'label' => esc_html__( 'Heading', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'render_type' => 'ui', + 'return_value' => 'yes', + 'default' => 'yes', + 'prefix_class' => 'elementor-show-heading-', + ] + ); + + $this->add_control( + 'heading_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} h2' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'show_heading!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'heading_typography', + 'selector' => '.woocommerce {{WRAPPER}} h2', + 'condition' => [ + 'show_heading!' => '', + ], + ] + ); + + $this->add_control( + 'content_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .shop_attributes' => 'color: {{VALUE}}', + ], + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'content_typography', + 'selector' => '.woocommerce {{WRAPPER}} .shop_attributes', + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + global $product; + $product = $this->get_product(); + + if ( ! $product ) { + return; + } + + wc_get_template( 'single-product/tabs/additional-information.php' ); + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/product-content.php b/modules/woocommerce/widgets/product-content.php new file mode 100644 index 0000000..26c5fc0 --- /dev/null +++ b/modules/woocommerce/widgets/product-content.php @@ -0,0 +1,31 @@ +start_controls_section( + 'section_product_tabs_style', + [ + 'label' => esc_html__( 'Tabs', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->start_controls_tabs( 'tabs_style' ); + + $this->start_controls_tab( 'normal_tabs_style', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'tab_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-tabs ul.wc-tabs li a' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'tab_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'alpha' => false, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-tabs ul.wc-tabs li' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'tabs_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-tabs .woocommerce-Tabs-panel' => 'border-color: {{VALUE}}', + '.woocommerce {{WRAPPER}} .woocommerce-tabs ul.wc-tabs li' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'active_tabs_style', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'active_tab_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-tabs ul.wc-tabs li.active a' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'active_tab_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'alpha' => false, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-tabs .woocommerce-Tabs-panel, .woocommerce {{WRAPPER}} .woocommerce-tabs ul.wc-tabs li.active' => 'background-color: {{VALUE}}', + '.woocommerce {{WRAPPER}} .woocommerce-tabs ul.wc-tabs li.active' => 'border-bottom-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'active_tabs_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-tabs .woocommerce-Tabs-panel' => 'border-color: {{VALUE}}', + '.woocommerce {{WRAPPER}} .woocommerce-tabs ul.wc-tabs li.active' => 'border-color: {{VALUE}} {{VALUE}} {{active_tab_bg_color.VALUE}} {{VALUE}}', + '.woocommerce {{WRAPPER}} .woocommerce-tabs ul.wc-tabs li:not(.active)' => 'border-bottom-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'tab_typography', + 'selector' => '.woocommerce {{WRAPPER}} .woocommerce-tabs ul.wc-tabs li a', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'tab_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-tabs ul.wc-tabs li' => 'border-radius: {{SIZE}}{{UNIT}} {{SIZE}}{{UNIT}} 0 0', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_product_panel_style', + [ + 'label' => esc_html__( 'Panel', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-Tabs-panel' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'content_typography', + 'selector' => '.woocommerce {{WRAPPER}} .woocommerce-tabs .woocommerce-Tabs-panel', + ] + ); + + $this->add_control( + 'heading_panel_heading_style', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Heading', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'heading_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-Tabs-panel h2' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'content_heading_typography', + 'selector' => '.woocommerce {{WRAPPER}} .woocommerce-tabs .woocommerce-Tabs-panel h2', + ] + ); + + $this->add_control( + 'panel_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-tabs .woocommerce-Tabs-panel' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}; margin-top: -{{TOP}}{{UNIT}}', + ], + 'separator' => 'before', + ] + ); + + $this->add_control( + 'panel_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-tabs .woocommerce-Tabs-panel' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + '.woocommerce {{WRAPPER}} .woocommerce-tabs ul.wc-tabs' => 'margin-left: {{TOP}}{{UNIT}}; margin-right: {{RIGHT}}{{UNIT}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'panel_box_shadow', + 'selector' => '.woocommerce {{WRAPPER}} .woocommerce-tabs .woocommerce-Tabs-panel', + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + global $product; + + $product = $this->get_product(); + + if ( ! $product ) { + return; + } + + setup_postdata( $product->get_id() ); + + wc_get_template( 'single-product/tabs/tabs.php' ); + + // On render widget from Editor - trigger the init manually. + if ( wp_doing_ajax() ) { + ?> + + start_controls_section( + 'section_product_gallery_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'sale_flash', + [ + 'label' => esc_html__( 'Sale Flash', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'render_type' => 'template', + 'return_value' => 'yes', + 'default' => 'yes', + 'prefix_class' => '', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'image_border', + 'selector' => '.woocommerce {{WRAPPER}} .woocommerce-product-gallery__trigger + .woocommerce-product-gallery__wrapper, + .woocommerce {{WRAPPER}} .flex-viewport, .woocommerce {{WRAPPER}} .flex-control-thumbs img', + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'image_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-product-gallery__trigger + .woocommerce-product-gallery__wrapper, + .woocommerce {{WRAPPER}} .flex-viewport' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} .flex-viewport:not(:last-child)' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'heading_thumbs_style', + [ + 'label' => esc_html__( 'Thumbnails', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'thumbs_border', + 'selector' => '.woocommerce {{WRAPPER}} .flex-control-thumbs img', + ] + ); + + $this->add_responsive_control( + 'thumbs_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} .flex-control-thumbs img' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'spacing_thumbs', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} .flex-control-thumbs li' => 'padding-right: calc({{SIZE}}{{UNIT}} / 2); padding-left: calc({{SIZE}}{{UNIT}} / 2); padding-bottom: {{SIZE}}{{UNIT}}', + '.woocommerce {{WRAPPER}} .flex-control-thumbs' => 'margin-right: calc(-{{SIZE}}{{UNIT}} / 2); margin-left: calc(-{{SIZE}}{{UNIT}} / 2)', + ], + ] + ); + + $this->end_controls_section(); + } + + public function render() { + global $product; + + $product = $this->get_product(); + + if ( ! $product ) { + return; + } + + $settings = $this->get_settings_for_display(); + + $is_library_preview = Utils::_unstable_get_super_global_value( $_GET, 'elementor_library' ) + && Utils::_unstable_get_super_global_value( $_GET, 'preview_id' ); + + if ( $is_library_preview ) { + // We need to enqueue these scripts manually on the Library preview. + $this->load_assets_dependencies(); + } + + if ( 'yes' === $settings['sale_flash'] ) { + wc_get_template( 'loop/sale-flash.php' ); + } + wc_get_template( 'single-product/product-image.php' ); + + // On render widget from Editor - trigger the init manually. + if ( Plugin::elementor()->editor->is_edit_mode() ) { + ?> + + start_controls_section( + 'section_product_meta_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'view', + [ + 'label' => esc_html__( 'View', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'inline', + 'options' => [ + 'table' => esc_html__( 'Table', 'elementor-pro' ), + 'stacked' => esc_html__( 'Stacked', 'elementor-pro' ), + 'inline' => esc_html__( 'Inline', 'elementor-pro' ), + ], + 'prefix_class' => 'elementor-woo-meta--view-', + ] + ); + + $this->add_responsive_control( + 'space_between', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}:not(.elementor-woo-meta--view-inline) .product_meta .detail-container:not(:last-child)' => 'padding-bottom: calc({{SIZE}}{{UNIT}}/2)', + '{{WRAPPER}}:not(.elementor-woo-meta--view-inline) .product_meta .detail-container:not(:first-child)' => 'margin-top: calc({{SIZE}}{{UNIT}}/2)', + '{{WRAPPER}}.elementor-woo-meta--view-inline .product_meta .detail-container' => 'margin-right: calc({{SIZE}}{{UNIT}}/2); margin-left: calc({{SIZE}}{{UNIT}}/2)', + '{{WRAPPER}}.elementor-woo-meta--view-inline .product_meta' => 'margin-right: calc(-{{SIZE}}{{UNIT}}/2); margin-left: calc(-{{SIZE}}{{UNIT}}/2)', + 'body:not(.rtl) {{WRAPPER}}.elementor-woo-meta--view-inline .detail-container:after' => 'right: calc( (-{{SIZE}}{{UNIT}}/2) + (-{{divider_weight.SIZE}}px/2) )', + 'body:not.rtl {{WRAPPER}}.elementor-woo-meta--view-inline .detail-container:after' => 'left: calc( (-{{SIZE}}{{UNIT}}/2) - ({{divider_weight.SIZE}}px/2) )', + ], + ] + ); + + $this->add_control( + 'divider', + [ + 'label' => esc_html__( 'Divider', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Off', 'elementor-pro' ), + 'label_on' => esc_html__( 'On', 'elementor-pro' ), + 'selectors' => [ + '{{WRAPPER}} .product_meta .detail-container:not(:last-child):after' => 'content: ""', + ], + 'return_value' => 'yes', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'divider_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + 'solid' => esc_html__( 'Solid', 'elementor-pro' ), + 'double' => esc_html__( 'Double', 'elementor-pro' ), + 'dotted' => esc_html__( 'Dotted', 'elementor-pro' ), + 'dashed' => esc_html__( 'Dashed', 'elementor-pro' ), + ], + 'default' => 'solid', + 'condition' => [ + 'divider' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}}:not(.elementor-woo-meta--view-inline) .product_meta .detail-container:not(:last-child):after' => 'border-top-style: {{VALUE}}', + '{{WRAPPER}}.elementor-woo-meta--view-inline .product_meta .detail-container:not(:last-child):after' => 'border-left-style: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'divider_weight', + [ + 'label' => esc_html__( 'Weight', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 1, + ], + 'range' => [ + 'px' => [ + 'min' => 1, + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'condition' => [ + 'divider' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}}:not(.elementor-woo-meta--view-inline) .product_meta .detail-container:not(:last-child):after' => 'border-top-width: {{SIZE}}{{UNIT}}; margin-bottom: calc(-{{SIZE}}{{UNIT}}/2)', + '{{WRAPPER}}.elementor-woo-meta--view-inline .product_meta .detail-container:not(:last-child):after' => 'border-left-width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'divider_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'condition' => [ + 'divider' => 'yes', + 'view!' => 'inline', + ], + 'selectors' => [ + '{{WRAPPER}} .product_meta .detail-container:not(:last-child):after' => 'width: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'divider_height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'custom' ], + 'default' => [ + 'unit' => '%', + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'condition' => [ + 'divider' => 'yes', + 'view' => 'inline', + ], + 'selectors' => [ + '{{WRAPPER}} .product_meta .detail-container:not(:last-child):after' => 'height: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'divider_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ddd', + 'global' => [ + 'default' => Global_Colors::COLOR_TEXT, + ], + 'condition' => [ + 'divider' => 'yes', + ], + 'selectors' => [ + '{{WRAPPER}} .product_meta .detail-container:not(:last-child):after' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'heading_text_style', + [ + 'label' => esc_html__( 'Text', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'text_typography', + 'selector' => '{{WRAPPER}}', + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'heading_link_style', + [ + 'label' => esc_html__( 'Link', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'link_typography', + 'selector' => '{{WRAPPER}} a', + ] + ); + + $this->add_control( + 'link_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} a' => 'color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_product_meta_captions', + [ + 'label' => esc_html__( 'Captions', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'heading_category_caption', + [ + 'label' => esc_html__( 'Category', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'category_caption_single', + [ + 'label' => esc_html__( 'Singular', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'Category', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'category_caption_plural', + [ + 'label' => esc_html__( 'Plural', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'Categories', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'heading_tag_caption', + [ + 'label' => esc_html__( 'Tag', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'tag_caption_single', + [ + 'label' => esc_html__( 'Singular', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'Tag', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'tag_caption_plural', + [ + 'label' => esc_html__( 'Plural', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'Tags', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + ] + ); + + $this->add_control( + 'heading_sku_caption', + [ + 'label' => esc_html__( 'SKU', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sku_caption', + [ + 'label' => esc_html__( 'SKU', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'SKU', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->add_control( + 'sku_missing_caption', + [ + 'label' => esc_html__( 'Missing', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'placeholder' => esc_html__( 'N/A', 'elementor-pro' ), + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + ] + ); + + $this->end_controls_section(); + } + + private function get_plural_or_single( $single, $plural, $count ) { + return 1 === $count ? $single : $plural; + } + + protected function render() { + global $product; + + $product = $this->get_product(); + + if ( ! $product ) { + return; + } + + $sku = esc_html( $product->get_sku() ); + + $settings = $this->get_settings_for_display(); + $sku_caption = ! empty( $settings['sku_caption'] ) ? esc_html( $settings['sku_caption'] ) : esc_html__( 'SKU', 'elementor-pro' ); + $sku_missing = ! empty( $settings['sku_missing_caption'] ) ? esc_html( $settings['sku_missing_caption'] ) : esc_html__( 'N/A', 'elementor-pro' ); + $category_caption_single = ! empty( $settings['category_caption_single'] ) ? $settings['category_caption_single'] : esc_html__( 'Category', 'elementor-pro' ); + $category_caption_plural = ! empty( $settings['category_caption_plural'] ) ? $settings['category_caption_plural'] : esc_html__( 'Categories', 'elementor-pro' ); + $tag_caption_single = ! empty( $settings['tag_caption_single'] ) ? $settings['tag_caption_single'] : esc_html__( 'Tag', 'elementor-pro' ); + $tag_caption_plural = ! empty( $settings['tag_caption_plural'] ) ? $settings['tag_caption_plural'] : esc_html__( 'Tags', 'elementor-pro' ); + ?> +
        + + + + is_type( 'variable' ) ) ) : ?> + + + + + + + + + + + + + get_category_ids() ) ) : ?> + get_plural_or_single( $category_caption_single, $category_caption_plural, count( $product->get_category_ids() ) ) ); ?> get_id(), 'product_cat', '', ', ' ); ?> + + + get_tag_ids() ) ) : ?> + get_plural_or_single( $tag_caption_single, $tag_caption_plural, count( $product->get_tag_ids() ) ) ); ?> get_id(), 'product_tag', '', ', ' ); ?> + + + + +
        + start_controls_section( + 'section_price_style', + [ + 'label' => esc_html__( 'Price', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'text_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'price_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} .price' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '.woocommerce {{WRAPPER}} .price', + ] + ); + + $this->add_control( + 'sale_heading', + [ + 'label' => esc_html__( 'Sale Price', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'sale_price_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .price ins' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'sale_price_typography', + 'selector' => '.woocommerce {{WRAPPER}} .price ins', + ] + ); + + $this->add_control( + 'price_block', + [ + 'label' => esc_html__( 'Stacked', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'return_value' => 'yes', + 'prefix_class' => 'elementor-product-price-block-', + ] + ); + + $this->add_responsive_control( + 'sale_price_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + 'body:not(.rtl) {{WRAPPER}}:not(.elementor-product-price-block-yes) del' => 'margin-right: {{SIZE}}{{UNIT}}', + 'body.rtl {{WRAPPER}}:not(.elementor-product-price-block-yes) del' => 'margin-left: {{SIZE}}{{UNIT}}', + '{{WRAPPER}}.elementor-product-price-block-yes del' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + global $product; + $product = $this->get_product(); + + if ( ! $product ) { + return; + } + + wc_get_template( '/single-product/price.php' ); + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/product-rating.php b/modules/woocommerce/widgets/product-rating.php new file mode 100644 index 0000000..9922c6a --- /dev/null +++ b/modules/woocommerce/widgets/product-rating.php @@ -0,0 +1,193 @@ +start_controls_section( + 'section_product_rating_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'star_color', + [ + 'label' => esc_html__( 'Star Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .star-rating' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'empty_star_color', + [ + 'label' => esc_html__( 'Empty Star Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .star-rating::before' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'link_color', + [ + 'label' => esc_html__( 'Link Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-review-link' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'text_typography', + 'selector' => '.woocommerce {{WRAPPER}} .woocommerce-review-link', + ] + ); + + $this->add_control( + 'star_size', + [ + 'label' => esc_html__( 'Star Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'unit' => 'em', + ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '.woocommerce {{WRAPPER}} .star-rating' => 'font-size: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'space_between', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'unit' => 'em', + ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '.woocommerce:not(.rtl) {{WRAPPER}} .star-rating' => 'margin-right: {{SIZE}}{{UNIT}}', + '.woocommerce.rtl {{WRAPPER}} .star-rating' => 'margin-left: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'prefix_class' => 'elementor-product-rating--align-', + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + if ( ! post_type_supports( 'product', 'comments' ) ) { + return; + } + + global $product; + $product = $this->get_product(); + + if ( ! $product ) { + return; + } + + wc_get_template( 'single-product/rating.php' ); + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/product-related.php b/modules/woocommerce/widgets/product-related.php new file mode 100644 index 0000000..1f197b2 --- /dev/null +++ b/modules/woocommerce/widgets/product-related.php @@ -0,0 +1,257 @@ +start_controls_section( + 'section_related_products_content', + [ + 'label' => esc_html__( 'Related Products', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'posts_per_page', + [ + 'label' => esc_html__( 'Products Per Page', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => 4, + 'range' => [ + 'px' => [ + 'max' => 20, + ], + ], + ] + ); + + $this->add_columns_responsive_control(); + + $this->add_control( + 'orderby', + [ + 'label' => esc_html__( 'Order By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'date', + 'options' => [ + 'date' => esc_html__( 'Date', 'elementor-pro' ), + 'title' => esc_html__( 'Title', 'elementor-pro' ), + 'price' => esc_html__( 'Price', 'elementor-pro' ), + 'popularity' => esc_html__( 'Popularity', 'elementor-pro' ), + 'rating' => esc_html__( 'Rating', 'elementor-pro' ), + 'rand' => esc_html__( 'Random', 'elementor-pro' ), + 'menu_order' => esc_html__( 'Menu Order', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'order', + [ + 'label' => esc_html__( 'Order', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'desc', + 'options' => [ + 'asc' => esc_html__( 'ASC', 'elementor-pro' ), + 'desc' => esc_html__( 'DESC', 'elementor-pro' ), + ], + ] + ); + + $this->end_controls_section(); + + parent::register_controls(); + + $this->start_injection( [ + 'at' => 'before', + 'of' => 'section_design_box', + ] ); + + $this->start_controls_section( + 'section_heading_style', + [ + 'label' => esc_html__( 'Heading', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'show_heading', + [ + 'label' => esc_html__( 'Heading', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'default' => 'yes', + 'return_value' => 'yes', + 'prefix_class' => 'show-heading-', + ] + ); + + $this->add_control( + 'heading_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '.woocommerce {{WRAPPER}}.elementor-wc-products .products > h2' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'show_heading!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'heading_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '.woocommerce {{WRAPPER}}.elementor-wc-products .products > h2', + 'condition' => [ + 'show_heading!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'heading_text_align', + [ + 'label' => esc_html__( 'Text Align', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '.woocommerce {{WRAPPER}}.elementor-wc-products .products > h2' => 'text-align: {{VALUE}}', + ], + 'condition' => [ + 'show_heading!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'heading_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '.woocommerce {{WRAPPER}}.elementor-wc-products .products > h2' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'show_heading!' => '', + ], + ] + ); + + $this->end_controls_section(); + + $this->end_injection(); + } + + protected function render() { + global $product; + + $product = $this->get_product(); + + if ( ! $product ) { + return; + } + + $settings = $this->get_settings_for_display(); + + // Add a wrapper class to the Add to Cart & View Items elements if the automically_align_buttons switch has been selected. + if ( 'yes' === $settings['automatically_align_buttons'] ) { + add_filter( 'woocommerce_loop_add_to_cart_link', [ $this, 'add_to_cart_wrapper' ], 10, 1 ); + } + + $args = [ + 'posts_per_page' => 4, + 'columns' => 4, + 'orderby' => $settings['orderby'], + 'order' => $settings['order'], + ]; + + if ( ! empty( $settings['posts_per_page'] ) ) { + $args['posts_per_page'] = $settings['posts_per_page']; + } + + if ( ! empty( $settings['columns'] ) ) { + $args['columns'] = $settings['columns']; + } + + $args = array_map( 'sanitize_text_field', $args ); + + // Get visible related products then sort them at random. + $args['related_products'] = array_filter( array_map( 'wc_get_product', wc_get_related_products( $product->get_id(), $args['posts_per_page'], $product->get_upsell_ids() ) ), 'wc_products_array_filter_visible' ); + + // Handle orderby. + $args['related_products'] = wc_products_array_orderby( $args['related_products'], $args['orderby'], $args['order'] ); + + ob_start(); + + wc_get_template( 'single-product/related.php', $args ); + + $related_products_html = ob_get_clean(); + + if ( $related_products_html ) { + $related_products_html = str_replace( '
          start_controls_section( + 'section_product_description_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'text_align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + 'justify' => [ + 'title' => esc_html__( 'Justified', 'elementor-pro' ), + 'icon' => 'eicon-text-align-justify', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .woocommerce-product-details__short-description' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'text_typography', + 'selector' => '.woocommerce {{WRAPPER}} .woocommerce-product-details__short-description', + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + global $product; + $product = $this->get_product(); + + if ( ! $product ) { + return; + } + + wc_get_template( 'single-product/short-description.php' ); + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/product-stock.php b/modules/woocommerce/widgets/product-stock.php new file mode 100644 index 0000000..950de3b --- /dev/null +++ b/modules/woocommerce/widgets/product-stock.php @@ -0,0 +1,88 @@ +start_controls_section( + 'section_product_stock_style', + [ + 'label' => esc_html__( 'Style', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '.woocommerce {{WRAPPER}} .stock' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'text_typography', + 'selector' => '.woocommerce {{WRAPPER}} .stock', + ] + ); + + $this->end_controls_section(); + } + + protected function render() { + global $product; + $product = $this->get_product(); + + if ( ! $product ) { + return; + } + + // PHPCS - the method wc_get_stock_html is safe. + echo wc_get_stock_html( $product ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/product-title.php b/modules/woocommerce/widgets/product-title.php new file mode 100644 index 0000000..869b7a6 --- /dev/null +++ b/modules/woocommerce/widgets/product-title.php @@ -0,0 +1,97 @@ + 'heading', + 'is_core_dependency' => true, + ], + ]; + } + + protected function register_controls() { + parent::register_controls(); + + $this->update_control( + 'title', + [ + 'dynamic' => [ + 'default' => Plugin::elementor()->dynamic_tags->tag_data_to_tag_text( null, 'woocommerce-product-title-tag' ), + ], + ], + [ + 'recursive' => true, + ] + ); + + $this->update_control( + 'header_size', + [ + 'default' => 'h1', + ] + ); + } + + protected function get_html_wrapper_class() { + return parent::get_html_wrapper_class() . ' elementor-page-title elementor-widget-' . parent::get_name(); + } + + protected function render() { + $this->add_render_attribute( 'title', 'class', [ 'product_title', 'entry-title' ] ); + parent::render(); + } + + /** + * Render Woocommerce Product Title output in the editor. + * + * Written as a Backbone JavaScript template and used to generate the live preview. + * + * @since 2.9.0 + * @access protected + */ + protected function content_template() { + ?> + <# view.addRenderAttribute( 'title', 'class', [ 'product_title', 'entry-title' ] ); #> + start_controls_section( + 'section_upsell_content', + [ + 'label' => esc_html__( 'Upsells', 'elementor-pro' ), + ] + ); + + $this->add_columns_responsive_control(); + + $this->add_control( + 'orderby', + [ + 'label' => esc_html__( 'Order By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'date', + 'options' => [ + 'date' => esc_html__( 'Date', 'elementor-pro' ), + 'title' => esc_html__( 'Title', 'elementor-pro' ), + 'price' => esc_html__( 'Price', 'elementor-pro' ), + 'popularity' => esc_html__( 'Popularity', 'elementor-pro' ), + 'rating' => esc_html__( 'Rating', 'elementor-pro' ), + 'rand' => esc_html__( 'Random', 'elementor-pro' ), + 'menu_order' => esc_html__( 'Menu Order', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'order', + [ + 'label' => esc_html__( 'Order', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'desc', + 'options' => [ + 'asc' => esc_html__( 'ASC', 'elementor-pro' ), + 'desc' => esc_html__( 'DESC', 'elementor-pro' ), + ], + ] + ); + + $this->end_controls_section(); + + parent::register_controls(); + + $this->start_injection( [ + 'at' => 'before', + 'of' => 'section_design_box', + ] ); + + $this->start_controls_section( + 'section_heading_style', + [ + 'label' => esc_html__( 'Heading', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'show_heading', + [ + 'label' => esc_html__( 'Heading', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'default' => 'yes', + 'return_value' => 'yes', + 'prefix_class' => 'show-heading-', + ] + ); + + $this->add_control( + 'heading_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products .products > h2' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'show_heading!' => '', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'heading_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}}.elementor-wc-products .products > h2', + 'condition' => [ + 'show_heading!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'heading_text_align', + [ + 'label' => esc_html__( 'Text Align', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products .products > h2' => 'text-align: {{VALUE}}', + ], + 'condition' => [ + 'show_heading!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'heading_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products .products > h2' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'show_heading!' => '', + ], + ] + ); + + $this->end_controls_section(); + + $this->end_injection(); + } + + protected function render() { + $settings = $this->get_settings_for_display(); + + // Add a wrapper class to the Add to Cart & View Items elements if the automically_align_buttons switch has been selected. + if ( 'yes' === $settings['automatically_align_buttons'] ) { + add_filter( 'woocommerce_loop_add_to_cart_link', [ $this, 'add_to_cart_wrapper' ], 10, 1 ); + } + + $limit = '-1'; + $columns = 4; + $orderby = 'rand'; + $order = 'desc'; + + if ( ! empty( $settings['columns'] ) ) { + $columns = $settings['columns']; + } + + if ( ! empty( $settings['orderby'] ) ) { + $orderby = $settings['orderby']; + } + + if ( ! empty( $settings['order'] ) ) { + $order = $settings['order']; + } + + ob_start(); + + woocommerce_upsell_display( + sanitize_text_field( $limit ), + sanitize_text_field( $columns ), + sanitize_text_field( $orderby ), + sanitize_text_field( $order ) + ); + + $upsells_html = ob_get_clean(); + + if ( $upsells_html ) { + $upsells_html = str_replace( '
            start_controls_section( + 'section_products_style', + [ + 'label' => esc_html__( 'Products', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'wc_style_warning', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'The style of this widget is often affected by your theme and plugins. If you experience any such issue, try to switch to a basic theme and deactivate related plugins.', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'products_class', + [ + 'type' => Controls_Manager::HIDDEN, + 'default' => 'wc-products', + 'prefix_class' => 'elementor-products-grid elementor-', + ] + ); + + $this->add_responsive_control( + 'column_gap', + [ + 'label' => esc_html__( 'Columns Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 20, + ], + 'tablet_default' => [ + 'size' => 20, + ], + 'mobile_default' => [ + 'size' => 20, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products' => 'grid-column-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'row_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'size' => 40, + ], + 'tablet_default' => [ + 'size' => 40, + ], + 'mobile_default' => [ + 'size' => 40, + ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products' => 'grid-row-gap: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'align', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'prefix_class' => 'elementor-product-loop-item--align-', + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product' => 'text-align: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'heading_image_style', + [ + 'label' => esc_html__( 'Image', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'image_border', + 'selector' => '{{WRAPPER}}.elementor-wc-products .attachment-woocommerce_thumbnail', + ] + ); + + $this->add_responsive_control( + 'image_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products .attachment-woocommerce_thumbnail' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'image_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products .attachment-woocommerce_thumbnail' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'heading_title_style', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .woocommerce-loop-product__title' => 'color: {{VALUE}}', + '{{WRAPPER}}.elementor-wc-products ul.products li.product .woocommerce-loop-category__title' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'title_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}}.elementor-wc-products ul.products li.product .woocommerce-loop-product__title, ' . + '{{WRAPPER}}.elementor-wc-products ul.products li.product .woocommerce-loop-category__title', + + ] + ); + + $this->add_responsive_control( + 'title_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .woocommerce-loop-product__title' => 'margin-bottom: {{SIZE}}{{UNIT}}', + '{{WRAPPER}}.elementor-wc-products ul.products li.product .woocommerce-loop-category__title' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'heading_rating_style', + [ + 'label' => esc_html__( 'Rating', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'star_color', + [ + 'label' => esc_html__( 'Star Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .star-rating' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'empty_star_color', + [ + 'label' => esc_html__( 'Empty Star Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .star-rating::before' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'star_size', + [ + 'label' => esc_html__( 'Star Size', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'default' => [ + 'unit' => 'em', + ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .star-rating' => 'font-size: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'rating_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .star-rating' => 'margin-bottom: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'heading_price_style', + [ + 'label' => esc_html__( 'Price', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'price_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .price' => 'color: {{VALUE}}', + '{{WRAPPER}}.elementor-wc-products ul.products li.product .price ins' => 'color: {{VALUE}}', + '{{WRAPPER}}.elementor-wc-products ul.products li.product .price ins .amount' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'price_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}}.elementor-wc-products ul.products li.product .price', + ] + ); + + $this->add_control( + 'heading_old_price_style', + [ + 'label' => esc_html__( 'Regular Price', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'old_price_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .price del' => 'color: {{VALUE}}', + '{{WRAPPER}}.elementor-wc-products ul.products li.product .price del .amount' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'old_price_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + 'selector' => '{{WRAPPER}}.elementor-wc-products ul.products li.product .price del .amount ', + 'selector' => '{{WRAPPER}}.elementor-wc-products ul.products li.product .price del ', + ] + ); + + $this->add_control( + 'heading_button_style', + [ + 'label' => esc_html__( 'Button', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->start_controls_tabs( 'tabs_button_style' ); + + $this->start_controls_tab( + 'tab_button_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'default' => '', + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .button' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .button' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .button' => 'border-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'button_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}}.elementor-wc-products ul.products li.product .button', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( + 'tab_button_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'button_hover_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .button:hover' => 'color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_hover_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .button:hover' => 'background-color: {{VALUE}};', + ], + ] + ); + + $this->add_control( + 'button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .button:hover' => 'border-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_group_control( + Group_Control_Border::get_type(), [ + 'name' => 'button_border', + 'exclude' => [ 'color' ], + 'selector' => '{{WRAPPER}}.elementor-wc-products ul.products li.product .button', + 'separator' => 'before', + ] + ); + + $this->add_control( + 'button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .button' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'button_text_padding', + [ + 'label' => esc_html__( 'Text Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'button_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product .button' => 'margin-top: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'automatically_align_buttons', + [ + 'label' => __( 'Automatically align buttons', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => __( 'Yes', 'elementor-pro' ), + 'label_off' => __( 'No', 'elementor-pro' ), + 'default' => '', + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product' => '--button-align-display: flex; --button-align-direction: column; --button-align-justify: space-between;', + ], + 'render_type' => 'template', + ] + ); + + $this->add_control( + 'heading_view_cart_style', + [ + 'label' => esc_html__( 'View Cart', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + + $this->add_control( + 'view_cart_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products .added_to_cart' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'view_cart_typography', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_ACCENT, + ], + 'selector' => '{{WRAPPER}}.elementor-wc-products .added_to_cart', + ] + ); + + $this->add_responsive_control( + 'view_cart_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products .added_to_cart' => 'margin-inline-start: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_design_box', + [ + 'label' => esc_html__( 'Box', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'box_border_width', + [ + 'label' => esc_html__( 'Border Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product' => 'border-style: solid; border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'box_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 200, + ], + 'em' => [ + 'max' => 20, + ], + 'rem' => [ + 'max' => 20, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_responsive_control( + 'box_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 50, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}', + ], + ] + ); + + $this->start_controls_tabs( 'box_style_tabs' ); + + $this->start_controls_tab( 'classic_style_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_shadow', + 'selector' => '{{WRAPPER}}.elementor-wc-products ul.products li.product', + ] + ); + + $this->add_control( + 'box_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'box_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'classic_style_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'box_shadow_hover', + 'selector' => '{{WRAPPER}}.elementor-wc-products ul.products li.product:hover', + ] + ); + + $this->add_control( + 'box_bg_color_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product:hover' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'box_border_color_hover', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product:hover' => 'border-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_pagination_style', + [ + 'label' => esc_html__( 'Pagination', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'paginate' => 'yes', + ], + ] + ); + + $this->add_control( + 'pagination_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} nav.woocommerce-pagination' => 'margin-top: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_control( + 'show_pagination_border', + [ + 'label' => esc_html__( 'Border', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'default' => 'yes', + 'return_value' => 'yes', + 'prefix_class' => 'elementor-show-pagination-border-', + ] + ); + + $this->add_control( + 'pagination_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} nav.woocommerce-pagination ul' => 'border-color: {{VALUE}}', + '{{WRAPPER}} nav.woocommerce-pagination ul li' => 'border-right-color: {{VALUE}}; border-left-color: {{VALUE}}', + ], + 'condition' => [ + 'show_pagination_border' => 'yes', + ], + ] + ); + + $this->add_control( + 'pagination_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 20, + ], + 'em' => [ + 'max' => 2, + ], + 'rem' => [ + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}} nav.woocommerce-pagination ul li a, {{WRAPPER}} nav.woocommerce-pagination ul li span' => 'padding: {{SIZE}}{{UNIT}}', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'pagination_typography', + 'selector' => '{{WRAPPER}} nav.woocommerce-pagination', + ] + ); + + $this->start_controls_tabs( 'pagination_style_tabs' ); + + $this->start_controls_tab( 'pagination_style_normal', + [ + 'label' => esc_html__( 'Normal', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'pagination_link_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} nav.woocommerce-pagination ul li a' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'pagination_link_bg_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} nav.woocommerce-pagination ul li a' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'pagination_style_hover', + [ + 'label' => esc_html__( 'Hover', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'pagination_link_color_hover', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} nav.woocommerce-pagination ul li a:hover' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'pagination_link_bg_color_hover', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} nav.woocommerce-pagination ul li a:hover' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'pagination_style_active', + [ + 'label' => esc_html__( 'Active', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'pagination_link_color_active', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} nav.woocommerce-pagination ul li span.current' => 'color: {{VALUE}}', + ], + ] + ); + + $this->add_control( + 'pagination_link_bg_color_active', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} nav.woocommerce-pagination ul li span.current' => 'background-color: {{VALUE}}', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->end_controls_section(); + + $this->start_controls_section( + 'sale_flash_style', + [ + 'label' => esc_html__( 'Sale Flash', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'show_onsale_flash', + [ + 'label' => esc_html__( 'Sale Flash', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'separator' => 'before', + 'default' => 'yes', + 'return_value' => 'yes', + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product span.onsale' => 'display: block', + ], + ] + ); + + $this->add_control( + 'onsale_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product span.onsale' => 'color: {{VALUE}}', + ], + 'condition' => [ + 'show_onsale_flash' => 'yes', + ], + ] + ); + + $this->add_control( + 'onsale_text_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product span.onsale' => 'background-color: {{VALUE}}', + ], + 'condition' => [ + 'show_onsale_flash' => 'yes', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'onsale_typography', + 'selector' => '{{WRAPPER}}.elementor-wc-products ul.products li.product span.onsale', + 'condition' => [ + 'show_onsale_flash' => 'yes', + ], + ] + ); + + $this->add_control( + 'onsale_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product span.onsale' => 'border-radius: {{SIZE}}{{UNIT}}', + ], + 'condition' => [ + 'show_onsale_flash' => 'yes', + ], + ] + ); + + $this->add_control( + 'onsale_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product span.onsale' => 'min-width: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'show_onsale_flash' => 'yes', + ], + ] + ); + + $this->add_control( + 'onsale_height', + [ + 'label' => esc_html__( 'Height', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product span.onsale' => 'min-height: {{SIZE}}{{UNIT}}; line-height: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'show_onsale_flash' => 'yes', + ], + ] + ); + + $this->add_control( + 'onsale_horizontal_position', + [ + 'label' => esc_html__( 'Position', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ + 'title' => esc_html__( 'Left', 'elementor-pro' ), + 'icon' => 'eicon-h-align-left', + ], + 'right' => [ + 'title' => esc_html__( 'Right', 'elementor-pro' ), + 'icon' => 'eicon-h-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product span.onsale' => '{{VALUE}}', + ], + 'selectors_dictionary' => [ + 'left' => 'right: auto; left: 0', + 'right' => 'left: auto; right: 0', + ], + 'condition' => [ + 'show_onsale_flash' => 'yes', + ], + ] + ); + + $this->add_control( + 'onsale_distance', + [ + 'label' => esc_html__( 'Distance', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'min' => -20, + 'max' => 20, + ], + 'em' => [ + 'min' => -2, + 'max' => 2, + ], + 'rem' => [ + 'min' => -2, + 'max' => 2, + ], + ], + 'selectors' => [ + '{{WRAPPER}}.elementor-wc-products ul.products li.product span.onsale' => 'margin: {{SIZE}}{{UNIT}};', + ], + 'condition' => [ + 'show_onsale_flash' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + } + + /** + * Add To Cart Wrapper + * + * Add a div wrapper around the Add to Cart & View Cart buttons on the product cards inside the product grid. + * The wrapper is used to vertically align the Add to Cart Button and the View Cart link to the bottom of the card. + * This wrapper is added when the 'Automatically align buttons' toggle is selected. + * Using the 'woocommerce_loop_add_to_cart_link' hook. + * + * @since 3.7.0 + * + * @param string $string + * @return string $string + */ + public function add_to_cart_wrapper( $string ) { + return '
            ' . $string . '
            '; + } +} diff --git a/modules/woocommerce/widgets/products-deprecated.php b/modules/woocommerce/widgets/products-deprecated.php new file mode 100644 index 0000000..3ce48e3 --- /dev/null +++ b/modules/woocommerce/widgets/products-deprecated.php @@ -0,0 +1,278 @@ +query; + } + + protected function register_skins() { + $this->add_skin( new Skins\Skin_Classic( $this ) ); + } + + protected function register_controls() { + $this->deprecated_notice( Plugin::get_title(), '2.0.10', '', esc_html__( 'Products', 'elementor-pro' ) ); + + $this->start_controls_section( + 'section_layout', + [ + 'label' => esc_html__( 'Layout', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_control( + 'posts_per_page', + [ + 'label' => esc_html__( 'Products Count', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => '4', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'section_filter', + [ + 'label' => esc_html__( 'Query', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_group_control( + Group_Control_Posts::get_type(), + [ + 'name' => 'posts', + 'post_type' => 'product', + ] + ); + + $this->add_control( + 'advanced', + [ + 'label' => esc_html__( 'Advanced', 'elementor-pro' ), + 'type' => Controls_Manager::HEADING, + ] + ); + + $this->add_control( + 'filter_by', + [ + 'label' => esc_html__( 'Filter By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => [ + '' => esc_html__( 'None', 'elementor-pro' ), + 'featured' => esc_html__( 'Featured', 'elementor-pro' ), + 'sale' => esc_html__( 'Sale', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'orderby', + [ + 'label' => esc_html__( 'Order By', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'date', + 'options' => [ + 'date' => esc_html__( 'Date', 'elementor-pro' ), + 'title' => esc_html__( 'Title', 'elementor-pro' ), + 'price' => esc_html__( 'Price', 'elementor-pro' ), + 'popularity' => esc_html__( 'Popularity', 'elementor-pro' ), + 'rating' => esc_html__( 'Rating', 'elementor-pro' ), + 'rand' => esc_html__( 'Random', 'elementor-pro' ), + 'menu_order' => esc_html__( 'Menu Order', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'order', + [ + 'label' => esc_html__( 'Order', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'default' => 'desc', + 'options' => [ + 'asc' => esc_html__( 'ASC', 'elementor-pro' ), + 'desc' => esc_html__( 'DESC', 'elementor-pro' ), + ], + ] + ); + + $this->add_control( + 'exclude', + [ + 'label' => esc_html__( 'Exclude', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT2, + 'multiple' => true, + 'options' => [ + 'current_post' => esc_html__( 'Current Post', 'elementor-pro' ), + 'manual_selection' => esc_html__( 'Manual Selection', 'elementor-pro' ), + ], + 'label_block' => true, + ] + ); + + $this->add_control( + 'exclude_ids', + [ + 'label' => esc_html__( 'Search & Select', 'elementor-pro' ), + 'type' => Module::QUERY_CONTROL_ID, + 'autocomplete' => [ + 'object' => Module::QUERY_OBJECT_POST, + ], + 'options' => [], + 'label_block' => true, + 'multiple' => true, + 'condition' => [ + 'exclude' => 'manual_selection', + ], + ] + ); + + $this->add_control( + 'avoid_duplicates', + [ + 'label' => esc_html__( 'Avoid Duplicates', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'description' => esc_html__( 'Set to Yes to avoid duplicate posts from showing up on the page. This only affects the frontend.', 'elementor-pro' ), + ] + ); + + $this->end_controls_section(); + + parent::register_controls(); + } + + public function query_posts() { + $settings = $this->get_settings(); + /** @var Module $query_module */ + $query_module = Module::instance(); + $query_args = $query_module->get_query_args( 'posts', $settings ); + + // Default ordering args + $ordering_args = WC()->query->get_catalog_ordering_args( $settings['orderby'], $settings['order'] ); + + $query_args['orderby'] = $ordering_args['orderby']; + $query_args['order'] = $ordering_args['order']; + + if ( ! empty( $ordering_args['meta_key'] ) ) { + $query_args['meta_key'] = $ordering_args['meta_key']; + } + + if ( 'sale' === $settings['filter_by'] ) { + // From WooCommerce `sale_products` shortcode + $query_args['post__in'] = array_merge( [ 0 ], wc_get_product_ids_on_sale() ); + } + + if ( version_compare( WC()->version, '3.0.0', '>=' ) ) { + $query_args = $this->get_wc_visibility_parse_query( $query_args ); + } else { + $query_args = $this->get_wc_legacy_visibility_parse_query( $query_args ); + } + + $this->query = new \WP_Query( $query_args ); + } + + private function get_wc_visibility_parse_query( $query_args ) { + $settings = $this->get_settings(); + $product_visibility_term_ids = wc_get_product_visibility_term_ids(); + + if ( 'featured' === $settings['filter_by'] ) { + $query_args['tax_query'][] = [ + 'taxonomy' => 'product_visibility', + 'field' => 'term_taxonomy_id', + 'terms' => $product_visibility_term_ids['featured'], + ]; + } + + return $query_args; + } + + private function get_wc_legacy_visibility_parse_query( $query_args ) { + $settings = $this->get_settings(); + + $query_args['meta_query'] = [ + [ + 'key' => '_visibility', + 'value' => [ 'catalog', 'visible' ], + 'compare' => 'IN', + ], + ]; + + if ( 'featured' === $settings['filter_by'] ) { + // From WooCommerce `featured_products` shortcode + $query_args['meta_query'][] = [ + 'key' => '_featured', + 'value' => 'yes', + ]; + } + + return $query_args; + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/products.php b/modules/woocommerce/widgets/products.php new file mode 100644 index 0000000..505b4ef --- /dev/null +++ b/modules/woocommerce/widgets/products.php @@ -0,0 +1,387 @@ +start_controls_section( + 'section_query', + [ + 'label' => esc_html__( 'Query', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_CONTENT, + ] + ); + + $this->add_query_controls( Products_Renderer::QUERY_CONTROL_NAME ); + + $this->end_controls_section(); + } + + protected function register_controls() { + $this->start_controls_section( + 'section_content', + [ + 'label' => esc_html__( 'Content', 'elementor-pro' ), + ] + ); + + $this->add_columns_responsive_control(); + + $this->add_control( + 'rows', + [ + 'label' => esc_html__( 'Rows', 'elementor-pro' ), + 'type' => Controls_Manager::NUMBER, + 'default' => Products_Renderer::DEFAULT_COLUMNS_AND_ROWS, + 'render_type' => 'template', + 'range' => [ + 'px' => [ + 'max' => 20, + ], + ], + ] + ); + + $this->add_control( + 'paginate', + [ + 'label' => esc_html__( 'Pagination', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'condition' => [ + Products_Renderer::QUERY_CONTROL_NAME . '_post_type!' => [ + 'related_products', + 'upsells', + 'cross_sells', + ], + ], + ] + ); + + $this->add_control( + 'allow_order', + [ + 'label' => esc_html__( 'Allow Order', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'condition' => [ + 'paginate' => 'yes', + ], + ] + ); + + $this->add_control( + 'wc_notice_frontpage', + [ + // TODO: Remove define() with the release of Elementor 3.22 + 'type' => defined( 'Controls_Manager::ALERT' ) ? Controls_Manager::ALERT : 'alert', + 'alert_type' => 'info', + 'content' => esc_html__( 'Ordering is not available if this widget is placed in your front page. Visible on frontend only.', 'elementor-pro' ), + 'condition' => [ + 'paginate' => 'yes', + 'allow_order' => 'yes', + ], + ] + ); + + $this->add_control( + 'show_result_count', + [ + 'label' => esc_html__( 'Show Result Count', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'default' => '', + 'condition' => [ + 'paginate' => 'yes', + ], + ] + ); + + $this->end_controls_section(); + + $this->register_query_section(); + + $this->start_controls_section( + 'section_products_title', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => Products_Renderer::QUERY_CONTROL_NAME . '_post_type', + 'operator' => '=', + 'value' => 'related_products', + ], + [ + 'name' => Products_Renderer::QUERY_CONTROL_NAME . '_post_type', + 'operator' => '=', + 'value' => 'upsells', + ], + [ + 'name' => Products_Renderer::QUERY_CONTROL_NAME . '_post_type', + 'operator' => '=', + 'value' => 'cross_sells', + ], + ], + ], + ] + ); + + $this->add_control( + 'products_title_show', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => '', + 'return_value' => 'show', + 'prefix_class' => 'products-heading-', + ] + ); + + $query_type_strings = [ + 'related_products' => esc_html__( 'Related Products', 'elementor-pro' ), + 'upsells' => esc_html__( 'You may also like...', 'elementor-pro' ), + 'cross_sells' => esc_html__( 'You may be interested in...', 'elementor-pro' ), + ]; + + foreach ( $query_type_strings as $query_type => $string ) { + $this->add_control( + 'products_' . $query_type . '_title_text', + [ + 'label' => esc_html__( 'Section Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'label_block' => true, + 'placeholder' => $string, + 'default' => $string, + 'dynamic' => [ + 'active' => true, + ], + 'condition' => [ + 'products_title_show!' => '', + Products_Renderer::QUERY_CONTROL_NAME . '_post_type' => $query_type, + ], + ] + ); + } + + $this->add_responsive_control( + 'products_title_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--products-title-alignment: {{VALUE}};', + ], + 'condition' => [ + 'products_title_show!' => '', + ], + ] + ); + + $this->end_controls_section(); + + parent::register_controls(); + + $this->start_injection( [ + 'type' => 'section', + 'at' => 'start', + 'of' => 'section_design_box', + ] ); + + $this->start_controls_section( + 'products_title_style', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + 'condition' => [ + 'products_title_show!' => '', + ], + 'conditions' => [ + 'relation' => 'or', + 'terms' => [ + [ + 'name' => Products_Renderer::QUERY_CONTROL_NAME . '_post_type', + 'operator' => '=', + 'value' => 'related_products', + ], + [ + 'name' => Products_Renderer::QUERY_CONTROL_NAME . '_post_type', + 'operator' => '=', + 'value' => 'upsells', + ], + [ + 'name' => Products_Renderer::QUERY_CONTROL_NAME . '_post_type', + 'operator' => '=', + 'value' => 'cross_sells', + ], + ], + ], + ] + ); + + $this->add_control( + 'products_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'global' => [ + 'default' => Global_Colors::COLOR_PRIMARY, + ], + 'selectors' => [ + '{{WRAPPER}}' => '--products-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'products_title_typography', + 'selector' => '{{WRAPPER}}.products-heading-show .related-products > h2, {{WRAPPER}}.products-heading-show .upsells > h2, {{WRAPPER}}.products-heading-show .cross-sells > h2', + 'global' => [ + 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, + ], + ] + ); + + $this->add_responsive_control( + 'products_title_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--products-title-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->end_injection(); + } + + public static function get_shortcode_object( $settings ) { + if ( 'current_query' === $settings[ Products_Renderer::QUERY_CONTROL_NAME . '_post_type' ] ) { + return new Current_Query_Renderer( $settings, 'current_query' ); + } + return new Products_Renderer( $settings, 'products' ); + } + + protected function render() { + + if ( WC()->session ) { + wc_print_notices(); + } + + $settings = $this->get_settings_for_display(); + $post_type_setting = $settings[ Products_Renderer::QUERY_CONTROL_NAME . '_post_type' ]; + + // Add a wrapper class to the Add to Cart & View Items elements if the automically_align_buttons switch has been selected. + if ( 'yes' === $settings['automatically_align_buttons'] ) { + add_filter( 'woocommerce_loop_add_to_cart_link', [ $this, 'add_to_cart_wrapper' ], 10, 1 ); + } + + if ( 'related_products' === $post_type_setting ) { + $content = Module::get_products_related_content( $settings ); + } elseif ( 'upsells' === $post_type_setting ) { + $content = Module::get_upsells_content( $settings ); + } elseif ( 'cross_sells' === $post_type_setting ) { + $content = Module::get_cross_sells_content( $settings ); + } else { + // For Products_Renderer. + if ( ! isset( $GLOBALS['post'] ) ) { + $GLOBALS['post'] = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + } + + $shortcode = static::get_shortcode_object( $settings ); + $content = $shortcode->get_content(); + } + + if ( $content ) { + $content = str_replace( '
              get_settings_for_display( 'nothing_found_message' ) ) { + echo '
              ' . esc_html( $this->get_settings_for_display( 'nothing_found_message' ) ) . '
              '; + } + + if ( 'yes' === $settings['automatically_align_buttons'] ) { + remove_filter( 'woocommerce_loop_add_to_cart_link', [ $this, 'add_to_cart_wrapper' ] ); + } + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/purchase-summary.php b/modules/woocommerce/widgets/purchase-summary.php new file mode 100644 index 0000000..7602619 --- /dev/null +++ b/modules/woocommerce/widgets/purchase-summary.php @@ -0,0 +1,1719 @@ +start_controls_section( + 'confirmation_message', + [ + 'label' => esc_html__( 'Confirmation Message', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'confirmation_message_active', + [ + 'label' => esc_html__( 'Confirmation Message', 'elementor-pro' ), + 'type' => Controls_Manager::SWITCHER, + 'label_on' => esc_html__( 'Show', 'elementor-pro' ), + 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), + 'default' => 'yes', + 'selectors' => [ + '{{WRAPPER}}' => '--confirmation-message-display: block;', + ], + ] + ); + + $this->add_control( + 'confirmation_message_text', + [ + 'label' => esc_html__( 'Message', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'default' => esc_html__( 'Thank You. Your order has been received.', 'elementor-pro' ), + 'label_block' => true, + 'condition' => [ + 'confirmation_message_active!' => '', + ], + ] + ); + + $this->add_responsive_control( + 'confirmation_message_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'condition' => [ + 'confirmation_message_active!' => '', + ], + 'selectors' => [ + '{{WRAPPER}}' => '--confirmation-message-alignment: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'payment_details', + [ + 'label' => esc_html__( 'Payment Details', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'payment_details_number', + [ + 'label' => esc_html__( 'Number', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Order Number:', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'payment_details_date', + [ + 'label' => esc_html__( 'Date:', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Order Date:', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'payment_details_email', + [ + 'label' => esc_html__( 'Email', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Order Email:', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'payment_details_total', + [ + 'label' => esc_html__( 'Total', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Order Total:', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'payment_details_payment', + [ + 'label' => esc_html__( 'Payment', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Payment Method:', 'elementor-pro' ), + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'bank_details', + [ + 'label' => esc_html__( 'Bank Details', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'bank_details_text', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Our Bank Details', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'bank_details_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--bank-details-alignment: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'downloads', + [ + 'label' => esc_html__( 'Downloads', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'downloads_text', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Downloads', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'downloads_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--downloads-alignment: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'order_summary', + [ + 'label' => esc_html__( 'Purchase Summary', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_summary_text', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Order Details', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'order_summary_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--order-summary-alignment: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'billing_details', + [ + 'label' => esc_html__( 'Billing Details', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'billing_details_text', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Billing Details', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'billing_details_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--billing-details-alignment: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'shipping_details', + [ + 'label' => esc_html__( 'Shipping Address', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'shipping_details_text', + [ + 'label' => esc_html__( 'Title', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'dynamic' => [ + 'active' => true, + ], + 'ai' => [ + 'active' => false, + ], + 'default' => esc_html__( 'Shipping Details', 'elementor-pro' ), + ] + ); + + $this->add_responsive_control( + 'shipping_details_alignment', + [ + 'label' => esc_html__( 'Alignment', 'elementor-pro' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'start' => [ + 'title' => esc_html__( 'Start', 'elementor-pro' ), + 'icon' => 'eicon-text-align-left', + ], + 'center' => [ + 'title' => esc_html__( 'Center', 'elementor-pro' ), + 'icon' => 'eicon-text-align-center', + ], + 'end' => [ + 'title' => esc_html__( 'End', 'elementor-pro' ), + 'icon' => 'eicon-text-align-right', + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--shipping-details-alignment: {{VALUE}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'preview_order', + [ + 'label' => esc_html__( 'Preview Settings', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'preview_order_type', + [ + 'label' => esc_html__( 'Preview order with', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => [ + '' => 'Latest Order', + 'custom-order' => 'Order ID', + ], + ] + ); + + $this->add_control( + 'preview_order_custom', + [ + 'label' => esc_html__( 'Order ID', 'elementor-pro' ), + 'type' => Controls_Manager::TEXT, + 'condition' => [ + 'preview_order_type' => 'custom-order', + ], + 'ai' => [ + 'active' => false, + ], + 'render_type' => 'template', + 'description' => esc_html__( 'Note: To find an order ID, go to the WP dashboard: WooCommerce > Orders', 'elementor-pro' ), + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'sections_tabs_style', + [ + 'label' => esc_html__( 'Sections', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'sections_background_color', + [ + 'label' => esc_html__( 'Background Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-background-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'sections_box_shadow', + 'selector' => '{{WRAPPER}} .shop_table, {{WRAPPER}} address', + ] + ); + + $this->add_control( + 'sections_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-type: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'sections_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .shop_table, {{WRAPPER}} address' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'sections_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'sections_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-color: {{VALUE}};', + ], + 'condition' => [ + 'sections_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'sections_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'sections_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'sections_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--sections-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'typography_title', + [ + 'label' => esc_html__( 'Typography', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_control( + 'confirmation_message_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Confirmation Message', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'confirmation_message_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--confirmation-message-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'confirmation_message_typography', + 'selector' => '{{WRAPPER}} .woocommerce-thankyou-order-received', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'confirmation_message_text_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-thankyou-order-received', + ] + ); + + $this->add_control( + 'titles_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Titles', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'titles_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--titles-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'titles_typography', + 'selector' => '{{WRAPPER}} h2', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'titles_text_shadow', + 'selector' => '{{WRAPPER}} h2', + ] + ); + + $this->add_responsive_control( + 'titles_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--titles-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'general_text_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'General Text', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'general_text_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--general-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'general_text_typography', + 'selector' => '{{WRAPPER}} address, {{WRAPPER}} .product-purchase-note, {{WRAPPER}} .woocommerce-thankyou-order-details + p', + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'payment_details_title', + [ + 'label' => esc_html__( 'Payment Details', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'payment_details_space_between', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--payment-details-space-between: {{SIZE}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'payment_details_titles_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Titles', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'payment_details_titles_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--payment-details-titles-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'payment_details_titles_typography', + 'selector' => '{{WRAPPER}} .woocommerce-order-overview.order_details li', + 'exclude' => [ + 'text_decoration', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'payment_details_titles_text_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-order-overview.order_details li', + ] + ); + + $this->add_responsive_control( + 'payment_details_titles_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--payment-details-titles-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'payment_details_items_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Items', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'payment_details_items_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--payment-details-items-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'payment_details_items_typography', + 'selector' => '{{WRAPPER}} .woocommerce-order-overview.order_details li strong', + 'exclude' => [ + 'text_decoration', + ], + ] + ); + + $this->add_control( + 'payment_details_dividers_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Dividers', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'payment_details_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--payment-details-border-type: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'payment_details_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--payment-details-border-width: {{SIZE}}{{UNIT}};', + ], + + 'condition' => [ + 'payment_details_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'payment_details_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--payment-details-border-color: {{VALUE}};', + ], + 'condition' => [ + 'payment_details_border_type!' => 'none', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'bank_details_title', + [ + 'label' => esc_html__( 'Bank Details', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'bank_details_space_between', + [ + 'label' => esc_html__( 'Space Between', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--bank-details-space-between: {{SIZE}}{{UNIT}};', + ], + 'separator' => 'after', + ] + ); + + $this->add_control( + 'bank_details_account_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Account Title', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'account_title_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--account-title-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'account_title_typography', + 'selector' => '{{WRAPPER}} .wc-bacs-bank-details-account-name', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'account_title_text_shadow', + 'selector' => '{{WRAPPER}} .wc-bacs-bank-details-account-name', + ] + ); + + $this->add_responsive_control( + 'account_title_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--account-title-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'bank_details_titles_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Titles', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'bank_details_titles_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--bank-details-titles-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'bank_details_titles_typography', + 'selector' => '{{WRAPPER}} .woocommerce-bacs-bank-details .wc-bacs-bank-details li', + 'exclude' => [ + 'text_decoration', + ], + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'bank_details_titles_text_shadow', + 'selector' => '{{WRAPPER}} .woocommerce-bacs-bank-details .wc-bacs-bank-details li', + ] + ); + + $this->add_responsive_control( + 'bank_details_titles_spacing', + [ + 'label' => esc_html__( 'Spacing', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--bank-details-titles-spacing: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'bank_details_items_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Items', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'bank_details_items_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--bank-details-items-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'bank_details_items_typography', + 'selector' => '{{WRAPPER}} .woocommerce-bacs-bank-details .wc-bacs-bank-details li strong', + 'exclude' => [ + 'text_decoration', + ], + ] + ); + + $this->add_control( + 'bank_details_dividers_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Dividers', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'bank_details_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--bank-details-border-type: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'bank_details_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--bank-details-border-width: {{SIZE}}{{UNIT}};', + ], + + 'condition' => [ + 'bank_details_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'bank_details_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--bank-details-border-color: {{VALUE}};', + ], + 'condition' => [ + 'bank_details_border_type!' => 'none', + ], + ] + ); + + $this->end_controls_section(); + + $this->start_controls_section( + 'order_details_title', + [ + 'label' => esc_html__( 'Order Details', 'elementor-pro' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + + $this->add_responsive_control( + 'order_details_rows_gap', + [ + 'label' => esc_html__( 'Rows Gap', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', 'em', 'rem', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 50, + ], + 'em' => [ + 'max' => 5, + ], + 'rem' => [ + 'max' => 5, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--order-details-rows-gap: {{SIZE}}{{UNIT}};', + ], + ] + ); + + $this->add_control( + 'order_details_titles_totals', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Titles & Totals', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_details_titles_totals_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-details-titles-totals-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_details_titles_totals_typography', + 'selector' => '{{WRAPPER}} .shop_table thead tr th, {{WRAPPER}} .shop_table tfoot th, {{WRAPPER}} .shop_table tfoot tr td, {{WRAPPER}} .shop_table tfoot tr td span, {{WRAPPER}} .woocommerce-table--order-downloads tr td:before', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'order_details_titles_totals_text_shadow', + 'selector' => '{{WRAPPER}} .shop_table thead tr th, {{WRAPPER}} .shop_table tfoot th, {{WRAPPER}} .shop_table tfoot tr td, {{WRAPPER}} .shop_table tfoot tr td span, {{WRAPPER}} .woocommerce-table--order-downloads tr td:before', + ] + ); + + $this->add_control( + 'order_details_items_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Items', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_details_items_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-details-items-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_details_items_typography', + 'selector' => '{{WRAPPER}} .product-quantity, {{WRAPPER}} .woocommerce-table--order-details td a, {{WRAPPER}} td.product-total, {{WRAPPER}} td.download-product, {{WRAPPER}} td.download-remaining, {{WRAPPER}} td.download-expires, {{WRAPPER}} td.download-file', + ] + ); + + $this->add_control( + 'order_details_variations_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Variations', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'order_details_variations_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-details-variations-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_details_variations_typography', + 'selector' => '{{WRAPPER}} .product-name .wc-item-meta .wc-item-meta-label, {{WRAPPER}} .wc-item-meta li p', + ] + ); + + $this->add_control( + 'order_details_product_links_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Product Link', 'elementor-pro' ), + ] + ); + + $this->start_controls_tabs( 'order_details_product_links_colors' ); + + $this->start_controls_tab( 'order_details_product_links_normal_colors', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'order_details_product_links_normal_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-details-product-links-normal-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'order_details_product_links_hover_colors', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'order_details_product_links_hover_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--order-details-product-links-hover-color: {{VALUE}};', + ], + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'order_details_dividers_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Dividers', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_control( + 'order_details_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--tables-divider-border-type: {{VALUE}};', + ], + ] + ); + + $this->add_responsive_control( + 'order_details_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'range' => [ + 'px' => [ + 'max' => 100, + ], + 'em' => [ + 'max' => 10, + ], + 'rem' => [ + 'max' => 10, + ], + ], + 'selectors' => [ + '{{WRAPPER}}' => '--tables-divider-border-width: {{SIZE}}{{UNIT}};', + ], + + 'condition' => [ + 'order_details_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'order_details_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--tables-divider-border-color: {{VALUE}};', + ], + 'condition' => [ + 'order_details_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'order_details_button_title', + [ + 'type' => Controls_Manager::HEADING, + 'label' => esc_html__( 'Buttons', 'elementor-pro' ), + 'separator' => 'before', + ] + ); + + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'order_details_button_typography', + 'selector' => '{{WRAPPER}} .shop_table .button, {{WRAPPER}} .order-again .button', + ] + ); + + $this->add_group_control( + Group_Control_Text_Shadow::get_type(), + [ + 'name' => 'order_details_button_text_shadow', + 'selector' => '{{WRAPPER}} .shop_table .button, {{WRAPPER}} .order-again .button', + ] + ); + + $this->start_controls_tabs( 'order_details_button_styles' ); + + $this->start_controls_tab( 'order_details_button_styles_normal', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ) ] ); + + $this->add_control( + 'order_details_button_normal_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--button-normal-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'order_details_button_normal_background', + 'selector' => '{{WRAPPER}} .shop_table .button, {{WRAPPER}} .order-again .button', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'order_details_button_normal_box_shadow', + 'selector' => '{{WRAPPER}} .shop_table .button, {{WRAPPER}} .order-again .button', + ] + ); + + $this->end_controls_tab(); + + $this->start_controls_tab( 'order_details_button_styles_hover', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ) ] ); + + $this->add_control( + 'order_details_button_hover_text_color', + [ + 'label' => esc_html__( 'Text Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}}' => '--button-hover-text-color: {{VALUE}};', + ], + ] + ); + + $this->add_group_control( + Group_Control_Background::get_type(), + [ + 'name' => 'order_details_button_hover_background', + 'selector' => '{{WRAPPER}} .shop_table .button:hover, {{WRAPPER}} .order-again .button:hover', + ] + ); + + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'order_details_button_hover_box_shadow', + 'selector' => '{{WRAPPER}} .shop_table .button:hover, {{WRAPPER}} .order-again .button:hover', + ] + ); + + $this->add_control( + 'order_details_button_hover_border_color', + [ + 'label' => esc_html__( 'Border Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .shop_table .button:hover, {{WRAPPER}} .order-again .button:hover' => 'border-color: {{VALUE}}', + ], + 'condition' => [ + 'order_details_button_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'order_details_button_hover_transition_duration', + [ + 'label' => esc_html__( 'Transition Duration', 'elementor-pro' ) . ' (ms)', + 'type' => Controls_Manager::SLIDER, + 'selectors' => [ + '{{WRAPPER}}' => '--button-hover-transition-duration: {{SIZE}}ms', + ], + 'range' => [ + 'px' => [ + 'min' => 0, + 'max' => 3000, + 'step' => 100, + ], + ], + ] + ); + + $this->add_control( + 'order_details_button_hover_animation', + [ + 'label' => esc_html__( 'Hover Animation', 'elementor-pro' ), + 'type' => Controls_Manager::HOVER_ANIMATION, + 'frontend_available' => true, + 'render_type' => 'template', + ] + ); + + $this->end_controls_tab(); + + $this->end_controls_tabs(); + + $this->add_control( + 'order_details_button_border_type', + [ + 'label' => esc_html__( 'Border Type', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->get_custom_border_type_options(), + 'selectors' => [ + '{{WRAPPER}}' => '--buttons-border-type: {{VALUE}};', + ], + 'separator' => 'before', + ] + ); + + $this->add_responsive_control( + 'order_details_button_border_width', + [ + 'label' => esc_html__( 'Width', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}} .shop_table .button, {{WRAPPER}} .order-again .button, {{WRAPPER}} .woocommerce-pagination .button' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + 'condition' => [ + 'order_details_button_border_type!' => 'none', + ], + ] + ); + + $this->add_control( + 'order_details_button_border_color', + [ + 'label' => esc_html__( 'Color', 'elementor-pro' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} ' => '--buttons-border-color: {{VALUE}};', + ], + 'condition' => [ + 'order_details_button_border_type!' => 'none', + ], + ] + ); + + $this->add_responsive_control( + 'order_details_button_border_radius', + [ + 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--button-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->add_responsive_control( + 'order_details_button_padding', + [ + 'label' => esc_html__( 'Padding', 'elementor-pro' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], + 'selectors' => [ + '{{WRAPPER}}' => '--button-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', + ], + ] + ); + + $this->end_controls_section(); + } + + /** + * Init Gettext Modifications + * + * Sets the `$gettext_modifications` property used with the `filter_gettext()` in the extended Base_Widget. + * + * @since 3.5.0 + */ + protected function init_gettext_modifications() { + $instance = $this->get_settings_for_display(); + + $this->gettext_modifications = [ + 'Order number:' => isset( $instance['payment_details_number'] ) ? $instance['payment_details_number'] : '', + 'Date:' => isset( $instance['payment_details_date'] ) ? $instance['payment_details_date'] : '', + 'Email:' => isset( $instance['payment_details_email'] ) ? $instance['payment_details_email'] : '', + 'Total:' => isset( $instance['payment_details_total'] ) ? $instance['payment_details_total'] : '', + 'Payment method:' => isset( $instance['payment_details_payment'] ) ? $instance['payment_details_payment'] : '', + 'Our bank details' => isset( $instance['bank_details_text'] ) ? $instance['bank_details_text'] : '', + 'Order details' => isset( $instance['order_summary_text'] ) ? $instance['order_summary_text'] : '', + 'Billing address' => isset( $instance['billing_details_text'] ) ? $instance['billing_details_text'] : '', + 'Shipping address' => isset( $instance['shipping_details_text'] ) ? $instance['shipping_details_text'] : '', + 'Downloads' => isset( $instance['downloads_text'] ) ? $instance['downloads_text'] : '', + ]; + } + + /** + * Modify Order Received Text. + * + * @since 3.5.0 + * + * @param $text + * @return string + */ + public function modify_order_received_text( $text ) { + $instance = $this->get_settings_for_display(); + + if ( isset( $instance['confirmation_message_text'] ) ) { + $text = $instance['confirmation_message_text']; + } + + return $text; + } + + public function get_modified_order_id() { + return $this->order_id; + } + + public function get_modified_order_key() { + return $this->order_key; + } + + protected function render() { + $is_editor = Plugin::elementor()->editor->is_edit_mode(); + $is_preview = Module::is_preview(); + + if ( $is_editor || $is_preview ) { + $this->set_preview_order(); + + add_filter( 'woocommerce_thankyou_order_id', [ $this, 'get_modified_order_id' ] ); + add_filter( 'woocommerce_thankyou_order_key', [ $this, 'get_modified_order_key' ] ); + + /** + * The action `template_redirect` is not run during the re-loading of the Widget and as a result the + * `wc_template_redirect` function is not run which is responsible for loading the following, so we + * must load them ourselves. + */ + WC()->payment_gateways(); + WC()->shipping(); + } + + /* + * Add actions & filters before displaying our Widget. + */ + add_filter( 'gettext', [ $this, 'filter_gettext' ], 20, 3 ); + add_filter( 'woocommerce_thankyou_order_received_text', [ $this, 'modify_order_received_text' ] ); + + /** + * Display our Widget. + */ + global $wp; + if ( isset( $wp->query_vars['order-received'] ) && wc_get_order( intval( $wp->query_vars['order-received'] ) ) ) { + echo do_shortcode( '[woocommerce_checkout]' ); + } elseif ( $is_editor || $is_preview ) { + $this->no_order_notice(); + } + + /* + * Remove actions & filters after displaying our Widget. + */ + remove_filter( 'gettext', [ $this, 'filter_gettext' ], 20 ); + remove_filter( 'woocommerce_thankyou_order_received_text', [ $this, 'modify_order_received_text' ] ); + + if ( $is_editor || $is_preview ) { + remove_filter( 'woocommerce_thankyou_order_id', [ $this, 'get_modified_order_id' ] ); + remove_filter( 'woocommerce_thankyou_order_key', [ $this, 'get_modified_order_key' ] ); + } + } + + public function no_order_notice() { + ?> + + get_settings_for_display(); + $order = false; + + if ( 'custom-order' === $instance['preview_order_type'] ) { + $order = wc_get_order( $instance['preview_order_custom'] ); + } + + if ( ! $order ) { + $latest_order = wc_get_orders( [ + 'limit' => 1, + 'orderby' => 'date', + 'order' => 'DESC', + 'return' => 'ids', + ] ); + + if ( isset( $latest_order[0] ) ) { + $order = wc_get_order( $latest_order[0] ); + } + } + + if ( $order ) { + global $wp; + $wp->set_query_var( 'order-received', $order->get_id() ); + + $this->order_id = $order->get_id(); + $this->order_key = $order->get_order_key(); + } + } + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/woocommerce/widgets/single-elements.php b/modules/woocommerce/widgets/single-elements.php new file mode 100644 index 0000000..de24200 --- /dev/null +++ b/modules/woocommerce/widgets/single-elements.php @@ -0,0 +1,144 @@ +init_allowed_functions(); + } + + public function get_name() { + return 'wc-single-elements'; + } + + public function get_title() { + return esc_html__( 'Woo - Single Elements', 'elementor-pro' ); + } + + public function get_icon() { + return 'eicon-woocommerce'; + } + + /* Deprecated Widget */ + public function show_in_panel() { + return false; + } + + /** + * @return void + */ + private function init_allowed_functions(): void { + $this->allowed_functions = [ + '' => '— ' . esc_html__( 'Select', 'elementor-pro' ) . ' —', + 'woocommerce_output_product_data_tabs' => esc_html__( 'Data Tabs', 'elementor-pro' ), + 'woocommerce_template_single_title' => esc_html__( 'Title', 'elementor-pro' ), + 'woocommerce_template_single_rating' => esc_html__( 'Rating', 'elementor-pro' ), + 'woocommerce_template_single_price' => esc_html__( 'Price', 'elementor-pro' ), + 'woocommerce_template_single_excerpt' => esc_html__( 'Excerpt', 'elementor-pro' ), + 'woocommerce_template_single_meta' => esc_html__( 'Meta', 'elementor-pro' ), + 'woocommerce_template_single_sharing' => esc_html__( 'Sharing', 'elementor-pro' ), + 'woocommerce_show_product_sale_flash' => esc_html__( 'Sale Flash', 'elementor-pro' ), + 'woocommerce_product_additional_information_tab' => esc_html__( 'Additional Information Tab', 'elementor-pro' ), + 'woocommerce_upsell_display' => esc_html__( 'Upsell', 'elementor-pro' ), + 'wc_get_stock_html' => esc_html__( 'Stock Status', 'elementor-pro' ), + ]; + } + + protected function register_controls() { + $this->start_controls_section( + 'section_product', + [ + 'label' => esc_html__( 'Element', 'elementor-pro' ), + ] + ); + + $this->add_control( + 'element', + [ + 'label' => esc_html__( 'Element', 'elementor-pro' ), + 'type' => Controls_Manager::SELECT, + 'options' => $this->allowed_functions, + ] + ); + + $this->end_controls_section(); + } + + public function remove_description_tab( $tabs ) { + unset( $tabs['description'] ); + + return $tabs; + } + + private function get_element() { + global $product; + $product = $this->get_product(); + + $settings = $this->get_settings(); + $html = ''; + + switch ( $settings['element'] ) { + case '': + break; + + case 'wc_get_stock_html': + $html = wc_get_stock_html( $product ); + break; + + case 'woocommerce_output_product_data_tabs': + add_filter( 'woocommerce_product_tabs', [ $this, 'remove_description_tab' ], 11 /* after default tabs*/ ); + ob_start(); + woocommerce_output_product_data_tabs(); + // Wrap with the internal woocommerce `product` class + $html = '
              ' . ob_get_clean() . '
              '; + remove_filter( 'woocommerce_product_tabs', [ $this, 'remove_description_tab' ], 11 ); + break; + + case 'woocommerce_template_single_rating': + $is_edit_mode = Plugin::elementor()->editor->is_edit_mode(); + + if ( 'no' === get_option( 'woocommerce_enable_review_rating' ) ) { + if ( $is_edit_mode ) { + $html = esc_html__( 'Admin Notice:', 'elementor-pro' ) . ' ' . esc_html__( 'Please enable the Review Rating', 'elementor-pro' ); + } + break; + } + + ob_start(); + woocommerce_template_single_rating(); + $html = ob_get_clean(); + if ( '' === $html && $is_edit_mode ) { + $html = esc_html__( 'Admin Notice:', 'elementor-pro' ) . ' ' . esc_html__( 'No Rating Reviews', 'elementor-pro' ); + } + break; + + default: + if ( is_callable( $settings['element'] ) && array_key_exists( $settings['element'], $this->allowed_functions ) ) { + $html = call_user_func( $settings['element'] ); + } + } + + return $html; + } + + protected function render() { + // PHPCS - the method get_content is safe. + echo $this->get_element(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + public function render_plain_content() {} + + public function get_group_name() { + return 'woocommerce'; + } +} diff --git a/modules/wp-cli/license-command.php b/modules/wp-cli/license-command.php new file mode 100644 index 0000000..acec103 --- /dev/null +++ b/modules/wp-cli/license-command.php @@ -0,0 +1,68 @@ + + * - This will try activate your license key. + */ + public function activate( $args, $assoc_args ) { + $license_key = $args[0]; + $data = API::activate_license( $args[0] ); + + if ( is_wp_error( $data ) ) { + \WP_CLI::error( sprintf( '%s (%s) ', $data->get_error_message(), $data->get_error_code() ) ); + } + + if ( ! $data['success'] ) { + $errors = [ + 'no_activations_left' => 'You have no more activations left.', + 'expired' => 'Your License Has Expired', + 'missing' => 'Your license is missing. Please check your key again.', + 'cancelled' => 'Your license key has been cancelled', + 'key_mismatch' => 'Your license is invalid for this domain. Please check your key again.', + ]; + + if ( isset( $errors[ $data['error'] ] ) ) { + $error_msg = $errors[ $data['error'] ]; + } else { + $error_msg = 'An error occurred. (' . $data['error'] . ')'; + } + + \WP_CLI::error( $error_msg ); + } + + Admin::set_license_key( $license_key ); + API::set_license_data( $data ); + + \WP_CLI::success( 'The License has been activated successfully.' ); + } + + /** + * Deactivate Elementor Pro License key. + * + * ## EXAMPLES + * + * 1. wp elementor-pro license deactivate. + * - This will deactivate your license key. + */ + public function deactivate() { + Admin::deactivate(); + \WP_CLI::success( 'The License has been deactivated successfully.' ); + } +} diff --git a/modules/wp-cli/module.php b/modules/wp-cli/module.php new file mode 100644 index 0000000..0e0ea3b --- /dev/null +++ b/modules/wp-cli/module.php @@ -0,0 +1,38 @@ +regenerate(); + + if ( is_wp_error( $cache_cleared ) ) { + \WP_CLI::error( $cache_cleared->get_error_message() ); + } + + \WP_CLI::success( 'Template conditions cache is cleared.' ); + } +} diff --git a/modules/wp-cli/update.php b/modules/wp-cli/update.php new file mode 100644 index 0000000..e8305c9 --- /dev/null +++ b/modules/wp-cli/update.php @@ -0,0 +1,18 @@ + 'ElementorPro\Modules\QueryControl\Module', + 'ElementorPro\Modules\PanelPostsControl\Controls\Group_Control_Posts' => 'ElementorPro\Modules\QueryControl\Controls\Group_Control_Posts', + 'ElementorPro\Modules\PanelPostsControl\Controls\Query' => 'ElementorPro\Modules\QueryControl\Controls\Query', + ]; + + /** + * @var \ElementorPro\License\Updater + */ + public $updater; + + /** + * @var PHP_Api + */ + public $php_api; + + /** + * Throw error on object clone + * + * The whole idea of the singleton design pattern is that there is a single + * object therefore, we don't want the object to be cloned. + * + * @since 1.0.0 + * @return void + */ + public function __clone() { + _doing_it_wrong( + __FUNCTION__, + sprintf( 'Cloning instances of the singleton "%s" class is forbidden.', get_class( $this ) ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + '1.0.0' + ); + } + + /** + * Disable unserializing of the class + * + * @since 1.0.0 + * @return void + */ + public function __wakeup() { + _doing_it_wrong( + __FUNCTION__, + sprintf( 'Unserializing instances of the singleton "%s" class is forbidden.', get_class( $this ) ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + '1.0.0' + ); + } + + /** + * @return \Elementor\Plugin + */ + + public static function elementor() { + return \Elementor\Plugin::$instance; + } + + /** + * @return Plugin + */ + public static function instance() { + if ( is_null( self::$_instance ) ) { + self::$_instance = new self(); + } + + return self::$_instance; + } + + public function autoload( $class ) { + if ( 0 !== strpos( $class, __NAMESPACE__ ) ) { + return; + } + + $has_class_alias = isset( $this->classes_aliases[ $class ] ); + + // Backward Compatibility: Save old class name for set an alias after the new class is loaded + if ( $has_class_alias ) { + $class_alias_name = $this->classes_aliases[ $class ]; + $class_to_load = $class_alias_name; + } else { + $class_to_load = $class; + } + + if ( ! class_exists( $class_to_load ) ) { + $filename = strtolower( + preg_replace( + [ '/^' . __NAMESPACE__ . '\\\/', '/([a-z])([A-Z])/', '/_/', '/\\\/' ], + [ '', '$1-$2', '-', DIRECTORY_SEPARATOR ], + $class_to_load + ) + ); + $filename = ELEMENTOR_PRO_PATH . $filename . '.php'; + + if ( is_readable( $filename ) ) { + include( $filename ); + } + } + + if ( $has_class_alias ) { + class_alias( $class_alias_name, $class ); + } + } + + public function enqueue_styles() { + $suffix = $this->get_assets_suffix(); + + $direction_suffix = is_rtl() ? '-rtl' : ''; + + $frontend_file_name_base = $this->is_optimized_css_mode() ? 'frontend-lite' : 'frontend'; + + $frontend_file_name = $frontend_file_name_base . $direction_suffix . $suffix . '.css'; + + $has_custom_file = self::elementor()->breakpoints->has_custom_breakpoints(); + + $frontend_file_url = $this->get_frontend_file_url( $frontend_file_name, $has_custom_file ); + + wp_enqueue_style( + 'elementor-pro', + $frontend_file_url, + [], + $has_custom_file ? null : ELEMENTOR_PRO_VERSION + ); + } + + public function get_frontend_file_url( $frontend_file_name, $custom_file ) { + if ( $custom_file ) { + $frontend_file = $this->get_frontend_file( $frontend_file_name ); + + $frontend_file_url = $frontend_file->get_url(); + } else { + $frontend_file_url = ELEMENTOR_PRO_ASSETS_URL . 'css/' . $frontend_file_name; + } + + return $frontend_file_url; + } + + public function get_frontend_file_path( $frontend_file_name, $custom_file ) { + if ( $custom_file ) { + $frontend_file = $this->get_frontend_file( $frontend_file_name ); + + $frontend_file_path = $frontend_file->get_path(); + } else { + $frontend_file_path = ELEMENTOR_PRO_ASSETS_PATH . 'css/' . $frontend_file_name; + } + + return $frontend_file_path; + } + + public function enqueue_frontend_scripts() { + $suffix = $this->get_assets_suffix(); + + wp_enqueue_script( + 'elementor-pro-frontend', + ELEMENTOR_PRO_URL . 'assets/js/frontend' . $suffix . '.js', + $this->get_frontend_depends(), + ELEMENTOR_PRO_VERSION, + true + ); + + wp_set_script_translations( 'elementor-pro-frontend', 'elementor-pro', ELEMENTOR_PRO_PATH . 'languages' ); + + wp_enqueue_script( 'pro-elements-handlers' ); + + $assets_url = ELEMENTOR_PRO_ASSETS_URL; + + /** + * Elementor Pro assets URL. + * + * Filters the assets URL used by Elementor Pro. + * + * By default Elementor Pro assets URL is set by the ELEMENTOR_PRO_ASSETS_URL + * constant. This hook allows developers to change this URL. + * + * @param string $assets_url Elementor Pro assets URL. + */ + $assets_url = apply_filters( 'elementor_pro/frontend/assets_url', $assets_url ); + + $locale_settings = [ + 'ajaxurl' => admin_url( 'admin-ajax.php' ), + 'nonce' => wp_create_nonce( 'elementor-pro-frontend' ), + 'urls' => [ + 'assets' => $assets_url, + 'rest' => get_rest_url(), + ], + ]; + + /** + * Localized frontend settings. + * + * Filters the localized settings used in the frontend as JavaScript variables. + * + * By default Elementor Pro passes some frontend settings to be consumed as JavaScript + * variables. This hook allows developers to add extra settings values to be consumed + * using JavaScript in the frontend. + * + * @since 1.0.0 + * + * @param array $locale_settings Localized frontend settings. + */ + $locale_settings = apply_filters( 'elementor_pro/frontend/localize_settings', $locale_settings ); + + Utils::print_js_config( + 'elementor-pro-frontend', + 'ElementorProFrontendConfig', + $locale_settings + ); + + if ( $this->is_assets_loader_exist() ) { + $this->register_assets(); + } + } + + public function register_frontend_scripts() { + $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; + + wp_register_script( + 'elementor-pro-webpack-runtime', + ELEMENTOR_PRO_URL . 'assets/js/webpack-pro.runtime' . $suffix . '.js', + [], + ELEMENTOR_PRO_VERSION, + true + ); + + wp_register_script( + 'pro-elements-handlers', + ELEMENTOR_PRO_URL . 'assets/js/elements-handlers' . $suffix . '.js', + [ + 'elementor-frontend', + ], + ELEMENTOR_PRO_VERSION, + true + ); + + wp_register_script( + 'smartmenus', + ELEMENTOR_PRO_URL . 'assets/lib/smartmenus/jquery.smartmenus' . $suffix . '.js', + [ + 'jquery', + ], + '1.2.1', + true + ); + + if ( ! $this->is_assets_loader_exist() ) { + wp_register_script( + 'elementor-sticky', + ELEMENTOR_PRO_URL . 'assets/lib/sticky/jquery.sticky' . $suffix . '.js', + [ + 'jquery', + ], + ELEMENTOR_PRO_VERSION, + true + ); + } + } + + public function register_preview_scripts() { + $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; + + wp_enqueue_script( + 'elementor-pro-preview', + ELEMENTOR_PRO_URL . 'assets/js/preview' . $suffix . '.js', + [ + 'wp-i18n', + 'elementor-frontend', + ], + ELEMENTOR_PRO_VERSION, + true + ); + } + + public function get_responsive_stylesheet_templates( $templates ) { + $templates_paths = glob( $this->get_responsive_templates_path() . '*.css' ); + + foreach ( $templates_paths as $template_path ) { + $file_name = 'custom-pro-' . basename( $template_path ); + + $templates[ $file_name ] = $template_path; + } + + return $templates; + } + + public function on_elementor_init() { + $this->modules_manager = new Modules_Manager(); + + /** TODO: BC for Elementor v2.4.0 */ + if ( class_exists( '\Elementor\Core\Upgrade\Manager' ) ) { + $this->upgrade = UpgradeManager::instance(); + } + + /** + * Elementor Pro init. + * + * Fires on Elementor Pro initiation, after Elementor has finished loading + * but before any headers are sent. + * + * @since 1.0.0 + */ + do_action( 'elementor_pro/init' ); + } + + /** + * @param \Elementor\Core\Base\Document $document + */ + public function on_document_save_version( $document ) { + $document->update_meta( '_elementor_pro_version', ELEMENTOR_PRO_VERSION ); + } + + private function get_frontend_depends() { + $frontend_depends = [ + 'elementor-pro-webpack-runtime', + 'elementor-frontend-modules', + ]; + + if ( ! $this->is_assets_loader_exist() ) { + $frontend_depends[] = 'elementor-sticky'; + } + + return $frontend_depends; + } + + private function get_responsive_templates_path() { + return ELEMENTOR_PRO_ASSETS_PATH . 'css/templates/'; + } + + private function add_subscription_template_access_level_to_settings( $settings ) { + // Core >= 3.2.0 + if ( isset( $settings['library_connect']['current_access_level'] ) ) { + $settings['library_connect']['current_access_level'] = API::get_library_access_level(); + } + + // Core >= 3.18.0 + if ( isset( $settings['library_connect']['current_access_tier'] ) ) { + $settings['library_connect']['current_access_tier'] = API::get_access_tier(); + } + + return $settings; + } + + private function setup_hooks() { + add_action( 'elementor/init', [ $this, 'on_elementor_init' ] ); + + add_action( 'elementor/frontend/before_register_scripts', [ $this, 'register_frontend_scripts' ] ); + add_action( 'elementor/preview/enqueue_scripts', [ $this, 'register_preview_scripts' ] ); + + add_action( 'elementor/frontend/before_enqueue_scripts', [ $this, 'enqueue_frontend_scripts' ] ); + add_action( 'elementor/frontend/after_enqueue_styles', [ $this, 'enqueue_styles' ] ); + + add_filter( 'elementor/core/breakpoints/get_stylesheet_template', [ $this, 'get_responsive_stylesheet_templates' ] ); + add_action( 'elementor/document/save_version', [ $this, 'on_document_save_version' ] ); + + add_filter( 'elementor/editor/localize_settings', function ( $settings ) { + return $this->add_subscription_template_access_level_to_settings( $settings ); + }, 11 /** After Elementor Core (Library) */ ); + + add_filter( 'elementor/common/localize_settings', function ( $settings ) { + return $this->add_subscription_template_access_level_to_settings( $settings ); + }, 11 /** After Elementor Core (Library) */ ); + } + + private function is_optimized_css_mode() { + $is_optimized_css_loading = self::elementor()->experiments->is_feature_active( 'e_optimized_css_loading' ); + + return ! Utils::is_script_debug() && $is_optimized_css_loading && ! self::elementor()->preview->is_preview_mode(); + } + + private function get_assets() { + $suffix = $this->get_assets_suffix(); + + return [ + 'scripts' => [ + 'e-sticky' => [ + 'src' => ELEMENTOR_PRO_URL . 'assets/lib/sticky/jquery.sticky' . $suffix . '.js', + 'version' => ELEMENTOR_PRO_VERSION, + 'dependencies' => [ + 'jquery', + ], + ], + ], + ]; + } + + private function register_assets() { + $assets = $this->get_assets(); + + if ( $assets ) { + self::elementor()->assets_loader->add_assets( $assets ); + } + } + + private function is_assets_loader_exist() { + return ! ! self::elementor()->assets_loader; + } + + /** + * Plugin constructor. + */ + private function __construct() { + spl_autoload_register( [ $this, 'autoload' ] ); + + Compatibility::register_actions(); + + new Connect\Manager(); + + $this->setup_hooks(); + + $this->editor = new Editor(); + + $this->preview = new Preview(); + + $this->app = new App(); + + $this->license_admin = new License\Admin(); + + $this->php_api = new PHP_Api(); + + if ( is_user_logged_in() ) { + $this->integrations = new Integrations_Manager(); // TODO: This one is safe to move out of the condition. + + $this->notifications = new Notifications_Manager(); + } + + if ( is_admin() ) { + $this->admin = new Admin(); + + $this->license_admin->register_actions(); + } + + // The `Updater` class is responsible for adding some updates related filters, including auto updates, and since + // WP crons don't run on admin mode, it should not depend on it. + $this->updater = new Updater(); + } + + private function get_assets_suffix() { + return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; + } + + private function get_frontend_file( $frontend_file_name ) { + $template_file_path = self::get_responsive_templates_path() . $frontend_file_name; + + return self::elementor()->frontend->get_frontend_file( $frontend_file_name, 'custom-pro-', $template_file_path ); + } + + final public static function get_title() { + return esc_html__( 'Elementor Pro', 'elementor-pro' ); + } +} + +if ( ! defined( 'ELEMENTOR_PRO_TESTS' ) ) { + // In tests we run the instance manually. + Plugin::instance(); +} diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..d3f487d --- /dev/null +++ b/readme.txt @@ -0,0 +1,7 @@ +=== Elementor Pro === +Contributors: elemntor +Tags: page builder, editor, landing page, drag-and-drop, elementor, visual editor, wysiwyg, design, maintenance mode, coming soon, under construction, website builder, landing page builder, front-end builder +Requires at least: 6.0 +Tested up to: 6.4 +Requires PHP: 7.4 +Requires Elementor: 3.19 diff --git a/run-on-linux.js b/run-on-linux.js new file mode 100644 index 0000000..eb15d08 --- /dev/null +++ b/run-on-linux.js @@ -0,0 +1,44 @@ +const { spawn, exec } = require( 'child_process' ); +const packageJson = require( './package.json' ); + +function isDockerExist() { + return new Promise( ( resolve ) => { + exec( 'docker -v', ( error ) => { + resolve( ! error ); + } ); + } ); +} + +async function run( tag ) { + const playwrightVersion = packageJson.devDependencies[ '@playwright/test' ]; + const workingDir = process.cwd(); + + const command = 'docker run'; + const options = [ + '--rm', + '--network host', + `--volume ${ workingDir }:/work`, + '--workdir /work/', + '--interactive', + process.env.CI ? '' : '--tty', + ]; + const image = `mcr.microsoft.com/playwright:v${ playwrightVersion.replace( '^', '' ) }-jammy`; + const commandToRun = `/bin/bash -c "npm run test:playwright -- --grep="${ tag }""`; + + spawn( `${ command } ${ options.join( ' ' ) } ${ image } ${ commandToRun }`, { + stdio: 'inherit', + stderr: 'inherit', + shell: true, + } ); +} + +( async () => { + if ( ! await isDockerExist() ) { + // eslint-disable-next-line no-console + console.error( 'Docker is not installed, please install it first.' ); + + process.exit( 1 ); + } + + await run( process.argv.slice( 2 ) ); +} )(); diff --git a/sample-data/acf-extra-post-fields.json b/sample-data/acf-extra-post-fields.json new file mode 100644 index 0000000..8344f6a --- /dev/null +++ b/sample-data/acf-extra-post-fields.json @@ -0,0 +1,71 @@ +[ + { + "key": "group_65f84bafe0536", + "title": "Taxonomy Extras", + "fields": [ + { + "key": "field_65f84bb059d4d", + "label": "Custom Text", + "name": "custom_text", + "aria-label": "", + "type": "text", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "default_value": "", + "maxlength": "", + "placeholder": "", + "prepend": "", + "append": "" + }, + { + "key": "field_65f84be959d4e", + "label": "Taxonomy Image", + "name": "taxonomy_image", + "aria-label": "", + "type": "image", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "return_format": "array", + "library": "all", + "min_width": "", + "min_height": "", + "min_size": "", + "max_width": "", + "max_height": "", + "max_size": "", + "mime_types": "", + "preview_size": "medium" + } + ], + "location": [ + [ + { + "param": "taxonomy", + "operator": "==", + "value": "all" + } + ] + ], + "menu_order": 0, + "position": "normal", + "style": "default", + "label_placement": "top", + "instruction_placement": "label", + "hide_on_screen": "", + "active": true, + "description": "", + "show_in_rest": 1 + } +] diff --git a/sample-data/acf-fields-products.json b/sample-data/acf-fields-products.json new file mode 100644 index 0000000..ae3fb80 --- /dev/null +++ b/sample-data/acf-fields-products.json @@ -0,0 +1,68 @@ +[ + { + "key": "group_63db85f18df07", + "title": "Products", + "fields": [ + { + "key": "field_63db85f2d1709", + "label": "Test Image", + "name": "test_image", + "aria-label": "", + "type": "image", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "return_format": "array", + "library": "all", + "min_width": "", + "min_height": "", + "min_size": "", + "max_width": "", + "max_height": "", + "max_size": "", + "mime_types": "", + "preview_size": "medium" + }, + { + "key": "field_63dcef444bbde", + "label": "Test Video", + "name": "test_video", + "aria-label": "", + "type": "url", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "default_value": "", + "placeholder": "" + } + ], + "location": [ + [ + { + "param": "post_type", + "operator": "==", + "value": "product" + } + ] + ], + "menu_order": 0, + "position": "normal", + "style": "default", + "label_placement": "top", + "instruction_placement": "label", + "hide_on_screen": "", + "active": true, + "description": "", + "show_in_rest": 0 + } +] \ No newline at end of file diff --git a/sample-data/sample_products_with_acf_meta.xml b/sample-data/sample_products_with_acf_meta.xml new file mode 100644 index 0000000..ff7f73f --- /dev/null +++ b/sample-data/sample_products_with_acf_meta.xml @@ -0,0 +1,4904 @@ + + + + + + WooCommerce Demo Store + http: + Just another WooCommerce store + Wed, 16 Jan 2019 13:09:24 +0000 + en-US + 1.2 + http: + http: + + 1 + shopmanager + info@woocommerce.com + + + + + https://wordpress.org/?v=5.0.3 + + V-Neck T-Shirt + https://woocommercecore.mystagingwebsite.com/product/v-neck-t-shirt/ + Wed, 16 Jan 2019 13:01:52 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/v-neck-t-shirt/ + + + + 6 + 2019-01-16 13:01:52 + 2019-01-16 13:01:52 + open + closed + v-neck-t-shirt + publish + 0 + 0 + product + + 0 + + + + + + + + + + + + _sku + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _thumbnail_id + + + + _price + + + + _price + + + + _regular_price + + + + _sale_price + + + + + Hoodie + https://woocommercecore.mystagingwebsite.com/product/hoodie/ + Wed, 16 Jan 2019 13:01:52 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/hoodie/ + + + + 7 + 2019-01-16 13:01:52 + 2019-01-16 13:01:52 + open + closed + hoodie + publish + 0 + 0 + product + + 0 + + + + + + + + _sku + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _thumbnail_id + + + + _price + + + + _price + + + + _regular_price + + + + _sale_price + + + + + Hoodie with Logo + https://woocommercecore.mystagingwebsite.com/product/hoodie-with-logo/ + Wed, 16 Jan 2019 13:01:52 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/hoodie-with-logo/ + + + + 8 + 2019-01-16 13:01:52 + 2019-01-16 13:01:52 + open + closed + hoodie-with-logo + publish + 0 + 0 + product + + 0 + + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + T-Shirt Hebrew + https://woocommercecore.mystagingwebsite.com/product/t-shirt/ + Wed, 16 Jan 2019 13:01:52 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/t-shirt/ + + + + 9 + 2019-01-16 13:01:52 + 2019-01-16 13:01:52 + open + closed + t-shirt + publish + 0 + 0 + product + + 0 + + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + Beanie + https://woocommercecore.mystagingwebsite.com/product/beanie/ + Wed, 16 Jan 2019 13:01:52 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/beanie/ + + + + 10 + 2019-01-16 13:01:52 + 2019-01-16 13:01:52 + open + closed + beanie + publish + 0 + 0 + product + + 0 + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + Belt + https://woocommercecore.mystagingwebsite.com/product/belt/ + Wed, 16 Jan 2019 13:01:52 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/belt/ + + + + 11 + 2019-01-16 13:01:52 + 2019-01-16 13:01:52 + open + closed + belt + publish + 0 + 0 + product + + 0 + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + Cap + https://woocommercecore.mystagingwebsite.com/product/cap/ + Wed, 16 Jan 2019 13:01:53 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/cap/ + + + + 12 + 2019-01-16 13:01:53 + 2019-01-16 13:01:53 + open + closed + cap + publish + 0 + 0 + product + + 0 + + + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + Sunglasses + https://woocommercecore.mystagingwebsite.com/product/sunglasses/ + Wed, 16 Jan 2019 13:01:53 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/sunglasses/ + + + + 13 + 2019-01-16 13:01:53 + 2019-01-16 13:01:53 + open + closed + sunglasses + publish + 0 + 0 + product + + 0 + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + Hoodie with Pocket + https://woocommercecore.mystagingwebsite.com/product/hoodie-with-pocket/ + Wed, 16 Jan 2019 13:01:53 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/hoodie-with-pocket/ + + + + 14 + 2019-01-16 13:01:53 + 2019-01-16 13:01:53 + open + closed + hoodie-with-pocket + publish + 0 + 0 + product + + 0 + + + + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + Hoodie with Zipper + https://woocommercecore.mystagingwebsite.com/product/hoodie-with-zipper/ + Wed, 16 Jan 2019 13:01:53 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/hoodie-with-zipper/ + + + + 15 + 2019-01-16 13:01:53 + 2019-01-16 13:01:53 + open + closed + hoodie-with-zipper + publish + 0 + 0 + product + + 0 + + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + Long Sleeve Tee + https://woocommercecore.mystagingwebsite.com/product/long-sleeve-tee/ + Wed, 16 Jan 2019 13:01:53 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/long-sleeve-tee/ + + + + 16 + 2019-01-16 13:01:53 + 2019-01-16 13:01:53 + open + closed + long-sleeve-tee + publish + 0 + 0 + product + + 0 + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + Polo + https://woocommercecore.mystagingwebsite.com/product/polo/ + Wed, 16 Jan 2019 13:01:53 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/polo/ + + + + 17 + 2019-01-16 13:01:53 + 2019-01-16 13:01:53 + open + closed + polo + publish + 0 + 0 + product + + 0 + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + Album + https://woocommercecore.mystagingwebsite.com/product/album/ + Wed, 16 Jan 2019 13:01:54 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/album/ + + + + 18 + 2019-01-16 13:01:54 + 2019-01-16 13:01:54 + open + closed + album + publish + 0 + 0 + product + + 0 + + + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + _weight + + + + _length + + + + _width + + + + _height + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + Single + https://woocommercecore.mystagingwebsite.com/product/single/ + Wed, 16 Jan 2019 13:01:54 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/single/ + + + + 19 + 2019-01-16 13:01:54 + 2019-01-16 13:01:54 + open + closed + single + publish + 0 + 0 + product + + 0 + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + _weight + + + + _length + + + + _width + + + + _height + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + V-Neck T-Shirt - Red + https://woocommercecore.mystagingwebsite.com/product/v-neck-t-shirt/?attribute_pa_color=red + Wed, 16 Jan 2019 13:01:54 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/v-neck-t-shirt-red/ + + + + 20 + 2019-01-16 13:01:54 + 2019-01-16 13:01:54 + closed + closed + v-neck-t-shirt-red + publish + 6 + 0 + product_variation + + 0 + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + _weight + + + + _length + + + + _width + + + + _height + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _variation_description + + + + _thumbnail_id + + + + attribute_pa_color + + + + attribute_pa_size + + + + + V-Neck T-Shirt - Green + https://woocommercecore.mystagingwebsite.com/product/v-neck-t-shirt/?attribute_pa_color=green + Wed, 16 Jan 2019 13:01:54 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/v-neck-t-shirt-green/ + + + + 21 + 2019-01-16 13:01:54 + 2019-01-16 13:01:54 + closed + closed + v-neck-t-shirt-green + publish + 6 + 0 + product_variation + + 0 + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + _weight + + + + _length + + + + _width + + + + _height + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _variation_description + + + + _thumbnail_id + + + + attribute_pa_color + + + + attribute_pa_size + + + + + V-Neck T-Shirt - Blue + https://woocommercecore.mystagingwebsite.com/product/v-neck-t-shirt/?attribute_pa_color=blue + Wed, 16 Jan 2019 13:01:54 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/v-neck-t-shirt-blue/ + + + + 22 + 2019-01-16 13:01:54 + 2019-01-16 13:01:54 + closed + closed + v-neck-t-shirt-blue + publish + 6 + 0 + product_variation + + 0 + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + _weight + + + + _length + + + + _width + + + + _height + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _wpcom_is_markdown + + + + _wp_old_slug + + + + _variation_description + + + + _thumbnail_id + + + + attribute_pa_color + + + + attribute_pa_size + + + + + Hoodie - Red, No + https://woocommercecore.mystagingwebsite.com/product/hoodie/?attribute_pa_color=red&attribute_logo=no + Wed, 16 Jan 2019 13:01:54 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/hoodie-red-no + + + + 23 + 2019-01-16 13:01:54 + 2019-01-16 13:01:54 + closed + closed + hoodie-red-no + publish + 7 + 1 + product_variation + + 0 + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + _weight + + + + _length + + + + _width + + + + _height + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _variation_description + + + + _thumbnail_id + + + + attribute_pa_color + + + + attribute_logo + + + + + Hoodie - Green, No + https://woocommercecore.mystagingwebsite.com/product/hoodie/?attribute_pa_color=green&attribute_logo=No + Wed, 16 Jan 2019 13:01:54 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/hoodie-green-no/ + + + + 24 + 2019-01-16 13:01:54 + 2019-01-16 13:01:54 + closed + closed + hoodie-green-no + publish + 7 + 2 + product_variation + + 0 + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + _weight + + + + _length + + + + _width + + + + _height + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _variation_description + + + + _thumbnail_id + + + + attribute_pa_color + + + + attribute_logo + + + + + Hoodie - Blue, No + https://woocommercecore.mystagingwebsite.com/product/hoodie/?attribute_pa_color=blue&attribute_logo=No + Wed, 16 Jan 2019 13:01:55 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/hoodie-blue-no + + + + 25 + 2019-01-16 13:01:55 + 2019-01-16 13:01:55 + closed + closed + hoodie-blue-no + publish + 7 + 3 + product_variation + + 0 + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + _weight + + + + _length + + + + _width + + + + _height + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _variation_description + + + + _thumbnail_id + + + + attribute_pa_color + + + + attribute_logo + + + + + T-Shirt with Logo + https://woocommercecore.mystagingwebsite.com/product/t-shirt-with-logo/ + Wed, 16 Jan 2019 13:01:55 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/t-shirt-with-logo/ + + + + 26 + 2019-01-16 13:01:55 + 2019-01-16 13:01:55 + open + closed + t-shirt-with-logo + publish + 0 + 0 + product + + 0 + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + Beanie with Logo + https://woocommercecore.mystagingwebsite.com/product/beanie-with-logo/ + Wed, 16 Jan 2019 13:01:55 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/beanie-with-logo/ + + + + 27 + 2019-01-16 13:01:55 + 2019-01-16 13:01:55 + open + closed + beanie-with-logo + publish + 0 + 0 + product + + 0 + + + + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + + + + + + + + + + + + + + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + + + + + + + + + + Logo Collection + https://woocommercecore.mystagingwebsite.com/product/logo-collection/ + Wed, 16 Jan 2019 13:01:55 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/logo-collection/ + + + + 28 + 2019-01-16 13:01:55 + 2019-01-16 13:01:55 + open + closed + logo-collection + publish + 0 + 0 + product + + 0 + + + + + _sku + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + _weight + + + + _length + + + + _width + + + + _height + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _children + + + + _thumbnail_id + + + + _price + + + + _price + + + + + + + + + + + + + WordPress Pennant + https://woocommercecore.mystagingwebsite.com/product/wordpress-pennant/ + Wed, 16 Jan 2019 13:01:55 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/wordpress-pennant/ + + + + 29 + 2019-01-16 13:01:55 + 2019-01-16 13:01:55 + open + closed + wordpress-pennant + publish + 0 + 0 + product + + 0 + + + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + _weight + + + + _length + + + + _width + + + + _height + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _thumbnail_id + + + + _product_url + + + + _button_text + + + + + + + + + + + + + Hoodie - Blue, Yes + https://woocommercecore.mystagingwebsite.com/product/hoodie/?attribute_pa_color=blue&attribute_logo=Yes + Wed, 16 Jan 2019 13:01:55 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/product/hoodie-blue-yes/ + + + + 30 + 2019-01-16 13:01:55 + 2019-01-16 13:01:55 + closed + closed + hoodie-blue-yes + publish + 7 + 0 + product_variation + + 0 + + _sku + + + + _regular_price + + + + _sale_price + + + + _sale_price_dates_from + + + + _sale_price_dates_to + + + + total_sales + + + + _tax_status + + + + _tax_class + + + + _manage_stock + + + + _backorders + + + + _low_stock_amount + + + + _sold_individually + + + + _weight + + + + _length + + + + _width + + + + _height + + + + _upsell_ids + + + + _crosssell_ids + + + + _purchase_note + + + + _default_attributes + + + + _virtual + + + + _downloadable + + + + _product_image_gallery + + + + _download_limit + + + + _download_expiry + + + + _stock + + + + _stock_status + + + + _wc_average_rating + + + + _wc_rating_count + + + + _wc_review_count + + + + _downloadable_files + + + + _product_attributes + + + + _product_version + + + + _price + + + + _variation_description + + + + _thumbnail_id + + + + attribute_pa_color + + + + attribute_logo + + + + + vneck-tee-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=31 + Wed, 16 Jan 2019 13:01:56 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/vneck-tee-2.jpg + + + + 31 + 2019-01-16 13:01:56 + 2019-01-16 13:01:56 + open + closed + vneck-tee-2-jpg + inherit + 6 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/vneck-tee-2.jpg + + _wc_attachment_source + + + + + vnech-tee-green-1.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=32 + Wed, 16 Jan 2019 13:01:57 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/vnech-tee-green-1.jpg + + + + 32 + 2019-01-16 13:01:57 + 2019-01-16 13:01:57 + open + closed + vnech-tee-green-1-jpg + inherit + 6 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/vnech-tee-green-1.jpg + + _wc_attachment_source + + + + + vnech-tee-blue-1.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=33 + Wed, 16 Jan 2019 13:01:58 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/vnech-tee-blue-1.jpg + + + + 33 + 2019-01-16 13:01:58 + 2019-01-16 13:01:58 + open + closed + vnech-tee-blue-1-jpg + inherit + 6 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/vnech-tee-blue-1.jpg + + _wc_attachment_source + + + + + hoodie-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=34 + Wed, 16 Jan 2019 13:01:58 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-2.jpg + + + + 34 + 2019-01-16 13:01:58 + 2019-01-16 13:01:58 + open + closed + hoodie-2-jpg + inherit + 7 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-2.jpg + + _wc_attachment_source + + + + + hoodie-blue-1.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=35 + Wed, 16 Jan 2019 13:01:59 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-blue-1.jpg + + + + 35 + 2019-01-16 13:01:59 + 2019-01-16 13:01:59 + open + closed + hoodie-blue-1-jpg + inherit + 7 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-blue-1.jpg + + _wc_attachment_source + + + + + hoodie-green-1.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=36 + Wed, 16 Jan 2019 13:02:00 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-green-1.jpg + + + + 36 + 2019-01-16 13:02:00 + 2019-01-16 13:02:00 + open + closed + hoodie-green-1-jpg + inherit + 7 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-green-1.jpg + + _wc_attachment_source + + + + + hoodie-with-logo-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=37 + Wed, 16 Jan 2019 13:02:01 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-with-logo-2.jpg + + + + 37 + 2019-01-16 13:02:01 + 2019-01-16 13:02:01 + open + closed + hoodie-with-logo-2-jpg + inherit + 7 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-with-logo-2.jpg + + _wc_attachment_source + + + + + tshirt-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=38 + Wed, 16 Jan 2019 13:02:02 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/tshirt-2.jpg + + + + 38 + 2019-01-16 13:02:02 + 2019-01-16 13:02:02 + open + closed + tshirt-2-jpg + inherit + 9 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/tshirt-2.jpg + + _wc_attachment_source + + + + + beanie-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=39 + Wed, 16 Jan 2019 13:02:02 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/beanie-2.jpg + + + + 39 + 2019-01-16 13:02:02 + 2019-01-16 13:02:02 + open + closed + beanie-2-jpg + inherit + 10 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/beanie-2.jpg + + _wc_attachment_source + + + + + belt-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=40 + Wed, 16 Jan 2019 13:02:03 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/belt-2.jpg + + + + 40 + 2019-01-16 13:02:03 + 2019-01-16 13:02:03 + open + closed + belt-2-jpg + inherit + 11 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/belt-2.jpg + + _wc_attachment_source + + + + + cap-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=41 + Wed, 16 Jan 2019 13:02:04 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/cap-2.jpg + + + + 41 + 2019-01-16 13:02:04 + 2019-01-16 13:02:04 + open + closed + cap-2-jpg + inherit + 12 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/cap-2.jpg + + _wc_attachment_source + + + + + sunglasses-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=42 + Wed, 16 Jan 2019 13:02:05 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/sunglasses-2.jpg + + + + 42 + 2019-01-16 13:02:05 + 2019-01-16 13:02:05 + open + closed + sunglasses-2-jpg + inherit + 13 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/sunglasses-2.jpg + + _wc_attachment_source + + + + + hoodie-with-pocket-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=43 + Wed, 16 Jan 2019 13:02:06 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-with-pocket-2.jpg + + + + 43 + 2019-01-16 13:02:06 + 2019-01-16 13:02:06 + open + closed + hoodie-with-pocket-2-jpg + inherit + 14 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-with-pocket-2.jpg + + _wc_attachment_source + + + + + hoodie-with-zipper-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=44 + Wed, 16 Jan 2019 13:02:06 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-with-zipper-2.jpg + + + + 44 + 2019-01-16 13:02:06 + 2019-01-16 13:02:06 + open + closed + hoodie-with-zipper-2-jpg + inherit + 15 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-with-zipper-2.jpg + + _wc_attachment_source + + + + + long-sleeve-tee-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=45 + Wed, 16 Jan 2019 13:02:07 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/long-sleeve-tee-2.jpg + + + + 45 + 2019-01-16 13:02:07 + 2019-01-16 13:02:07 + open + closed + long-sleeve-tee-2-jpg + inherit + 16 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/long-sleeve-tee-2.jpg + + _wc_attachment_source + + + + + polo-2.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=46 + Wed, 16 Jan 2019 13:02:08 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/polo-2.jpg + + + + 46 + 2019-01-16 13:02:08 + 2019-01-16 13:02:08 + open + closed + polo-2-jpg + inherit + 17 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/polo-2.jpg + + _wc_attachment_source + + + + + album-1.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=47 + Wed, 16 Jan 2019 13:02:09 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2022/05/album-1.jpg + + + + 47 + 2019-01-16 13:02:09 + 2019-01-16 13:02:09 + open + closed + album-1-jpg + inherit + 18 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2022/05/album-1.jpg + + _wc_attachment_source + + + + + single-1.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=48 + Wed, 16 Jan 2019 13:02:10 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/single-1.jpg + + + + 48 + 2019-01-16 13:02:10 + 2019-01-16 13:02:10 + open + closed + single-1-jpg + inherit + 19 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/single-1.jpg + + _wc_attachment_source + + + + + t-shirt-with-logo-1.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=49 + Wed, 16 Jan 2019 13:02:11 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/t-shirt-with-logo-1.jpg + + + + 49 + 2019-01-16 13:02:11 + 2019-01-16 13:02:11 + open + closed + t-shirt-with-logo-1-jpg + inherit + 26 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/t-shirt-with-logo-1.jpg + + _wc_attachment_source + + + + + beanie-with-logo-1.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=50 + Wed, 16 Jan 2019 13:02:12 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/beanie-with-logo-1.jpg + + + + 50 + 2019-01-16 13:02:12 + 2019-01-16 13:02:12 + open + closed + beanie-with-logo-1-jpg + inherit + 27 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/beanie-with-logo-1.jpg + + _wc_attachment_source + + + + + logo-1.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=51 + Wed, 16 Jan 2019 13:02:13 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/logo-1.jpg + + + + 51 + 2019-01-16 13:02:13 + 2019-01-16 13:02:13 + open + closed + logo-1-jpg + inherit + 28 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/logo-1.jpg + + _wc_attachment_source + + + + + pennant-1.jpg + https://woocommercecore.mystagingwebsite.com/?attachment_id=52 + Wed, 16 Jan 2019 13:02:13 +0000 + shopmanager + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/pennant-1.jpg + + + + 52 + 2019-01-16 13:02:13 + 2019-01-16 13:02:13 + open + closed + pennant-1-jpg + inherit + 29 + 0 + attachment + + 0 + https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/pennant-1.jpg + + _wc_attachment_source + + + + + diff --git a/sample-data/simple-taxonomy-loop-template.json b/sample-data/simple-taxonomy-loop-template.json new file mode 100644 index 0000000..8e8dee5 --- /dev/null +++ b/sample-data/simple-taxonomy-loop-template.json @@ -0,0 +1 @@ +{"content":[{"id":"ba28a8c","settings":[],"elements":[{"id":"59717e2b","settings":{"title":"Add Your Heading Text Here","align":"center","title_color":"#E46EC0","typography_typography":"custom","typography_font_family":"Assistant","typography_font_weight":"700","typography_text_transform":"uppercase","text_stroke_text_stroke":{"unit":"px","size":0,"sizes":[]},"_padding":{"unit":"%","top":"0","right":"4","bottom":"0","left":"4","isLinked":false},"_padding_widescreen":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_laptop":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"__dynamic__":{"title":"[elementor-tag id=\"fddf42e\" name=\"archive-title\" settings=\"%7B%7D\"]"}},"elements":[],"isInner":false,"widgetType":"heading","elType":"widget"},{"id":"1f3c48a4","settings":{"text_columns":"1","align":"center","_padding":{"unit":"%","top":"0","right":"4","bottom":"0","left":"4","isLinked":false},"_padding_widescreen":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_laptop":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"__dynamic__":{"editor":"[elementor-tag id=\"76b1db6\" name=\"archive-description\" settings=\"%7B%7D\"]"}},"elements":[],"isInner":false,"widgetType":"text-editor","elType":"widget"},{"id":"28993854","settings":{"_padding":{"unit":"%","top":"0","right":"4","bottom":"0","left":"4","isLinked":false},"_padding_widescreen":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_laptop":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"__dynamic__":{"image":"[elementor-tag id=\"e1425e3\" name=\"woocommerce-category-image-tag\" settings=\"%7B%7D\"]"},"image":{"id":69,"url":"http:\/\/wordpress-dev.local\/wp-content\/uploads\/2024\/03\/placeholder.png"}},"elements":[],"isInner":false,"widgetType":"image","elType":"widget"},{"id":"4038b812","settings":{"width":{"unit":"%","size":100,"sizes":[]},"_padding":{"unit":"%","top":"0","right":"4","bottom":"0","left":"4","isLinked":false},"_padding_widescreen":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_laptop":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"__dynamic__":{"image":"[elementor-tag id=\"b6ac506\" name=\"acf-image\" settings=\"%7B%22key%22%3A%22field_65f84be959d4e%3Ataxonomy_image%22%7D\"]"},"image":{"id":70,"url":"http:\/\/wordpress-dev.local\/wp-content\/uploads\/2024\/03\/placeholder-1.png"}},"elements":[],"isInner":false,"widgetType":"image","elType":"widget"},{"id":"46ec20a8","settings":{"align":"center","text_color":"#473C3C","typography_typography":"custom","typography_font_family":"Roboto","typography_font_size":{"unit":"px","size":24,"sizes":[]},"typography_font_weight":"400","typography_text_transform":"capitalize","typography_letter_spacing":{"unit":"px","size":0.10000000000000001,"sizes":[]},"typography_word_spacing":{"unit":"px","size":11,"sizes":[]},"_padding":{"unit":"%","top":"0","right":"4","bottom":"0","left":"4","isLinked":false},"_padding_widescreen":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_laptop":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"__dynamic__":{"editor":"[elementor-tag id=\"e50714a\" name=\"acf-text\" settings=\"%7B%22key%22%3A%22field_65f84bb059d4d%3Acustom_text%22%7D\"]"}},"elements":[],"isInner":false,"widgetType":"text-editor","elType":"widget"},{"id":"255d6f31","settings":{"text":"Click here","align":"justify","typography_typography":"custom","typography_font_family":"Assistant","typography_font_size":{"unit":"px","size":21,"sizes":[]},"typography_font_weight":"300","typography_text_transform":"uppercase","typography_letter_spacing":{"unit":"px","size":1.8999999999999999,"sizes":[]},"typography_word_spacing":{"unit":"px","size":8,"sizes":[]},"background_color":"#CE61B2","_padding":{"unit":"%","top":"0","right":"4","bottom":"0","left":"4","isLinked":false},"_padding_widescreen":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_laptop":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_tablet":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile_extra":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"_padding_mobile":{"unit":"%","top":"","right":"","bottom":"","left":"","isLinked":true},"__dynamic__":{"link":"[elementor-tag id=\"74247d7\" name=\"archive-url\" settings=\"%7B%7D\"]"}},"elements":[],"isInner":false,"widgetType":"button","elType":"widget"}],"isInner":false,"elType":"container"}],"page_settings":{"preview_id":"1244"},"version":"0.4","title":"TaxonomyTemplate","type":"loop-item"} \ No newline at end of file