From 66bcf36f299327aaf99beda090d57c26d39d6b65 Mon Sep 17 00:00:00 2001 From: Amin Ramezani Date: Sun, 25 Jun 2023 17:20:47 +0330 Subject: [PATCH] =?UTF-8?q?=D8=A2=D9=BE=D8=AF=DB=8C=D8=AA=20=D9=88=D8=B1?= =?UTF-8?q?=DA=98=D9=86=20=D8=AC=D8=AF=DB=8C=D8=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/_forms.scss | 25 + css/_input-group.scss | 2 +- css/menu-editor.css | 72 +- css/menu-editor.css.map | 2 +- css/menu-editor.scss | 83 +- customizables/Builders/BaseElementBuilder.php | 133 + customizables/Builders/CodeEditorBuilder.php | 26 + customizables/Builders/ContainerBuilder.php | 64 + customizables/Builders/ControlBuilder.php | 103 + customizables/Builders/EditorBuilder.php | 29 + customizables/Builders/ElementBuilder.php | 14 + .../Builders/ElementBuilderFactory.php | 314 + customizables/Builders/FormBuilder.php | 152 + customizables/Builders/GroupBuilder.php | 28 + customizables/Builders/InterfaceBuilder.php | 89 + customizables/Builders/NumberInputBuilder.php | 41 + customizables/Builders/RadioGroupBuilder.php | 32 + customizables/Builders/SectionBuilder.php | 17 + customizables/Builders/SettingFactory.php | 357 + customizables/Builders/StaticHtmlBuilder.php | 26 + .../Builders/StructChildSettingFactory.php | 29 + customizables/Builders/TextBoxBuilder.php | 21 + customizables/Builders/TextareaBuilder.php | 21 + .../Builders/ToggleCheckBoxBuilder.php | 29 + .../Controls/AbstractNumericControl.php | 140 + customizables/Controls/AlignmentSelector.php | 39 + customizables/Controls/CheckBox.php | 57 + customizables/Controls/ChoiceControl.php | 64 + .../Controls/ChoiceControlOption.php | 107 + customizables/Controls/ClassicControl.php | 91 + customizables/Controls/CodeEditor.php | 155 + customizables/Controls/ColorPicker.php | 57 + customizables/Controls/Container.php | 159 + customizables/Controls/ContentToggle.php | 57 + customizables/Controls/Control.php | 402 + customizables/Controls/ControlContainer.php | 26 + customizables/Controls/ControlGroup.php | 142 + customizables/Controls/ImageSelector.php | 164 + customizables/Controls/InterfaceStructure.php | 34 + customizables/Controls/NumberInput.php | 227 + customizables/Controls/PopupSlider.php | 55 + customizables/Controls/RadioButtonBar.php | 82 + customizables/Controls/RadioGroup.php | 198 + customizables/Controls/Section.php | 46 + customizables/Controls/SelectBox.php | 37 + customizables/Controls/StaticHtml.php | 46 + customizables/Controls/TextArea.php | 50 + customizables/Controls/TextInputControl.php | 65 + customizables/Controls/ToggleCheckbox.php | 122 + customizables/Controls/Toggleable.php | 49 + customizables/Controls/Tooltip.php | 45 + customizables/Controls/UiElement.php | 146 + customizables/Controls/WpEditor.php | 73 + customizables/Customizable.php | 72 + customizables/HtmlHelper.php | 81 + .../Rendering/AdminCustomizerListRenderer.php | 152 + customizables/Rendering/BoxyFormRenderer.php | 118 + customizables/Rendering/ClassicRenderer.php | 124 + customizables/Rendering/FormTableRenderer.php | 142 + customizables/Rendering/Renderer.php | 119 + .../Rendering/TabbedPanelRenderer.php | 138 + customizables/SampleModule.php | 567 ++ customizables/SettingCondition.php | 121 + customizables/Settings/AbstractSetting.php | 543 ++ .../Settings/AbstractStructSetting.php | 276 + customizables/Settings/BooleanSetting.php | 34 + customizables/Settings/ColorSetting.php | 31 + customizables/Settings/CompositeSetting.php | 53 + .../Settings/DeferredUpdateSubscriber.php | 57 + customizables/Settings/EnumSetting.php | 149 + customizables/Settings/FloatSetting.php | 7 + customizables/Settings/ImageSetting.php | 218 + customizables/Settings/IntegerSetting.php | 31 + customizables/Settings/MapSetting.php | 58 + .../Settings/NotificationSenderQueue.php | 56 + customizables/Settings/NumericSetting.php | 81 + customizables/Settings/PlainTextSetting.php | 17 + customizables/Settings/PredefinedSet.php | 24 + customizables/Settings/Setting.php | 100 + .../Settings/SettingGeneratorInterface.php | 15 + customizables/Settings/StringEnumSetting.php | 23 + customizables/Settings/StringSetting.php | 54 + customizables/Settings/UniqueSettingQueue.php | 57 + .../Settings/UpdateNotificationSender.php | 12 + customizables/Settings/UrlSetting.php | 40 + customizables/Settings/UserDefinedSetting.php | 26 + customizables/Settings/UserDefinedStruct.php | 45 + .../Settings/UserSanitizedStringSetting.php | 62 + customizables/SettingsForm.php | 162 + .../Storage/AbstractSettingsDictionary.php | 416 + .../Storage/ActiveMenuConfiguration.php | 110 + customizables/Storage/CompressedStorage.php | 11 + customizables/Storage/LazyArrayStorage.php | 148 + .../Storage/MenuConfigurationWrapper.php | 76 + customizables/Storage/ModuleSettings.php | 49 + customizables/Storage/NullStorage.php | 74 + customizables/Storage/ScopedOptionStorage.php | 131 + customizables/Storage/Slot.php | 101 + customizables/Storage/StorageInterface.php | 80 + customizables/Storage/StorageMethods.php | 177 + customizables/UpdateRequestHandler.php | 400 + customizables/Validation/ColorValidator.php | 21 + customizables/Validation/StringValidator.php | 70 + customizables/assets/_code-editor.scss | 11 + .../assets/_combined-base-controls.scss | 6 + customizables/assets/_image-selector.scss | 70 + customizables/assets/_number-input.scss | 17 + customizables/assets/_popup-slider.scss | 123 + customizables/assets/_radio-button-bar.scss | 69 + customizables/assets/_radio-group.scss | 71 + customizables/assets/combined-controls.js | 95 + customizables/assets/combined-controls.js.map | 1 + customizables/assets/combined-controls.ts | 111 + customizables/assets/controls.css | 253 + customizables/assets/controls.css.map | 1 + customizables/assets/controls.scss | 1 + customizables/assets/form-box.css | 59 + customizables/assets/form-box.css.map | 1 + customizables/assets/form-box.scss | 63 + customizables/assets/form-table.css | 25 + customizables/assets/form-table.css.map | 1 + customizables/assets/form-table.scss | 38 + customizables/assets/image-selector.js | 359 + customizables/assets/image-selector.js.map | 1 + customizables/assets/image-selector.ts | 455 + customizables/assets/popup-slider.d.ts | 32 + customizables/assets/popup-slider.js | 369 + customizables/assets/tabbed-panels.css | 120 + customizables/assets/tabbed-panels.css.map | 1 + customizables/assets/tabbed-panels.scss | 180 + customizables/assets/tinymce-5.d.ts | 2820 ++++++ customizables/constants.php | 3 + dist/admin-customizer.bundle.js | 8052 +++++++++++++++++ dist/admin-customizer.bundle.js.map | 1 + dist/build.manifest.json | 7 + dist/customizable.bundle.js | 851 ++ dist/customizable.bundle.js.map | 1 + dist/runtime.bundle.js | 151 + dist/runtime.bundle.js.map | 1 + extras.php | 281 +- extras/.htaccess | 2 +- .../dynamic-stylesheets/Cache/DummyCache.php | 17 + .../Cache/LocalTransientCache.php | 30 + .../Cache/NetworkTransientCache.php | 27 + .../Cache/StyleCacheInterface.php | 40 + .../Cache/TransientCache.php | 20 + .../DynamicStylesheetBundle.php | 80 + .../MenuScopedStylesheetHelper.php | 230 + extras/dynamic-stylesheets/Stylesheet.php | 439 + extras/jszip/jszip.d.ts | 330 + extras/jszip/jszip.min.js | 13 + extras/ko-extensions.js | 545 +- extras/ko-extensions.js.map | 2 +- extras/ko-extensions.ts | 642 +- extras/menu-headings/ameMenuHeadingStyler.php | 2 +- .../menu-headings/menu-headings-template.php | 4 +- extras/menu-headings/menu-headings.js | 133 +- extras/menu-headings/menu-headings.js.map | 2 +- extras/menu-headings/menu-headings.ts | 47 +- .../admin-customizer/admin-customizer-base.js | 40 + .../admin-customizer-base.js.map | 1 + .../admin-customizer/admin-customizer-base.ts | 66 + .../admin-customizer-template.php | 336 + .../admin-customizer/admin-customizer.css | 677 ++ .../admin-customizer/admin-customizer.css.map | 1 + .../admin-customizer/admin-customizer.js | 1512 ++++ .../admin-customizer/admin-customizer.js.map | 1 + .../admin-customizer/admin-customizer.php | 2128 +++++ .../admin-customizer/admin-customizer.scss | 908 ++ .../admin-customizer/admin-customizer.ts | 1961 ++++ .../admin-theme-template/admin-theme.php | 166 + .../admin-theme-template/readme.txt | 17 + .../modules/admin-customizer/communicator.js | 426 + .../admin-customizer/communicator.js.map | 1 + .../modules/admin-customizer/communicator.ts | 577 ++ .../ko-components/ame-ac-content-section.js | 39 + .../ame-ac-content-section.js.map | 1 + .../ko-components/ame-ac-content-section.ts | 48 + .../ko-components/ame-ac-control-group.js | 47 + .../ko-components/ame-ac-control-group.js.map | 1 + .../ko-components/ame-ac-control-group.ts | 57 + .../ko-components/ame-ac-control.js | 40 + .../ko-components/ame-ac-control.js.map | 1 + .../ko-components/ame-ac-control.ts | 49 + .../ko-components/ame-ac-section-link.js | 23 + .../ko-components/ame-ac-section-link.js.map | 1 + .../ko-components/ame-ac-section-link.ts | 31 + .../ko-components/ame-ac-section.js | 106 + .../ko-components/ame-ac-section.js.map | 1 + .../ko-components/ame-ac-section.ts | 128 + .../ko-components/ame-ac-separator.js | 10 + .../ko-components/ame-ac-separator.js.map | 1 + .../ko-components/ame-ac-separator.ts | 16 + .../ko-components/ame-ac-structure.js | 38 + .../ko-components/ame-ac-structure.js.map | 1 + .../ko-components/ame-ac-structure.ts | 50 + .../ko-components/ame-ac-validation-errors.js | 29 + .../ame-ac-validation-errors.js.map | 1 + .../ko-components/ame-ac-validation-errors.ts | 36 + .../admin-customizer/preview-handler.js | 143 + .../admin-customizer/preview-handler.js.map | 1 + .../admin-customizer/preview-handler.ts | 196 + extras/modules/admin-customizer/preview.css | 5 + .../modules/admin-customizer/preview.css.map | 1 + extras/modules/admin-customizer/preview.scss | 3 + .../admin-menu-colors/admin-menu-colors.js | 408 + .../admin-menu-colors.js.map | 1 + .../admin-menu-colors/admin-menu-colors.php | 862 ++ .../admin-menu-colors/admin-menu-colors.ts | 519 ++ .../admin-menu-colors/color-dialog.php | 25 + .../admin-menu-colors/menu-colors-ui.css | 65 + .../admin-menu-colors/menu-colors-ui.css.map | 1 + .../admin-menu-colors/menu-colors-ui.scss | 82 + .../dashboard-styler/custom-css-preview.js | 28 + .../custom-css-preview.js.map | 1 + .../dashboard-styler/custom-css-preview.ts | 43 + .../dashboard-styler/dashboard-styler.php | 1556 ++++ .../ameCustomRssWidget.php | 115 +- .../ameDashboardWidget.php | 20 +- .../ameStandardWidgetWrapper.php | 44 +- .../ameWidgetCollection.php | 248 +- .../ameWidgetEditor.php | 303 +- .../custom-columns.css | 118 + .../custom-columns.css.map | 1 + .../custom-columns.scss | 107 + .../custom-widget-layout.js | 67 + .../custom-widget-layout.js.map | 1 + .../custom-widget-layout.ts | 81 + .../dashboard-widget-editor-template.php | 366 +- .../dashboard-widget-editor.css | 130 +- .../dashboard-widget-editor.css.map | 1 + .../dashboard-widget-editor.js | 354 +- .../dashboard-widget-editor.js.map | 2 +- .../dashboard-widget-editor.scss | 154 +- .../dashboard-widget-editor.ts | 326 +- .../dashboard-widget.js | 352 +- .../dashboard-widget.js.map | 2 +- .../dashboard-widget.ts | 134 +- .../refresh-widgets.js | 3 +- extras/modules/easy-hide/easy-hide.js | 579 +- extras/modules/easy-hide/easy-hide.js.map | 2 +- extras/modules/easy-hide/easy-hide.php | 2 +- extras/modules/easy-hide/easy-hide.ts | 65 +- .../ko-customizable-dev.js | 61 + .../ko-customizable-dev.js.map | 1 + .../ko-customizable-dev.php | 164 + .../ko-customizable-dev.ts | 89 + .../menu-styler/menu-styler-features.js | 379 + .../menu-styler/menu-styler-features.js.map | 1 + .../menu-styler/menu-styler-features.ts | 506 ++ .../menu-styler/menu-styler-template.php | 40 + extras/modules/menu-styler/menu-styler-ui.js | 236 + .../modules/menu-styler/menu-styler-ui.js.map | 1 + extras/modules/menu-styler/menu-styler-ui.ts | 291 + extras/modules/menu-styler/menu-styler.css | 79 + .../modules/menu-styler/menu-styler.css.map | 1 + extras/modules/menu-styler/menu-styler.php | 866 ++ extras/modules/menu-styler/menu-styler.scss | 116 + extras/modules/metaboxes/ameMetaBoxEditor.php | 16 +- .../metaboxes/hide-gutenberg-panels.js | 8 +- extras/modules/metaboxes/metabox-editor.js | 229 +- .../modules/metaboxes/metabox-editor.js.map | 2 +- extras/modules/metaboxes/metabox-editor.ts | 115 +- extras/modules/role-editor/ameRoleEditor.php | 5 +- extras/modules/role-editor/role-editor.js | 2153 +++-- extras/modules/role-editor/role-editor.js.map | 2 +- extras/modules/role-editor/role-editor.ts | 413 +- .../separator-styles/MenuSeparatorStyler.php | 630 ++ extras/modules/super-users/super-users.js | 58 +- extras/modules/super-users/super-users.js.map | 2 +- extras/modules/super-users/super-users.php | 2 +- extras/modules/super-users/super-users.ts | 4 +- .../tweaks/ameGutenbergBlockManager.php | 2 +- extras/modules/tweaks/default-tweaks.php | 3 +- .../tweaks/gutenberg-block-detector.js | 8 + extras/modules/tweaks/tweak-manager.js | 640 +- extras/modules/tweaks/tweak-manager.js.map | 2 +- extras/modules/tweaks/tweak-manager.ts | 215 +- extras/modules/tweaks/tweaks.php | 2 +- extras/persistent-pro-module.php | 82 +- extras/phpColors/src/color.php | 12 +- extras/pro-admin-helpers.js | 74 +- extras/pro-admin-helpers.js.map | 2 +- extras/pro-admin-helpers.ts | 14 +- extras/pro-admin-styles.css.map | 1 + extras/pro-autoloader.php | 81 + extras/pro-common-lib.js | 43 + extras/pro-common-lib.js.map | 1 + extras/pro-common-lib.ts | 46 + .../Controls/BackgroundPositionSelector.php | 67 + .../Controls/BackgroundRepeat.php | 24 + .../Controls/BorderStyleSelector.php | 43 + .../Controls/BoxDimensions.php | 225 + .../Controls/FontStylePicker.php | 144 + .../Controls/HorizontalSeparator.php | 23 + .../CssPropertyGenerator.php | 19 + .../pro-customizables/CssValueGenerator.php | 15 + .../pro-customizables/Settings/Background.php | 128 + .../Settings/BackgroundImageSetting.php | 22 + .../Settings/BackgroundSizeSetting.php | 36 + .../Settings/BorderRadius.php | 69 + .../Settings/BorderStyle.php | 25 + extras/pro-customizables/Settings/Borders.php | 18 + .../pro-customizables/Settings/BoxShadow.php | 222 + .../Settings/CssBoxDimensions.php | 87 + .../Settings/CssColorSetting.php | 71 + .../Settings/CssEnumSetting.php | 30 + .../Settings/CssLengthSetting.php | 92 + .../Settings/CssSettingCollection.php | 47 + extras/pro-customizables/Settings/Font.php | 193 + .../Settings/IndividualBorder.php | 84 + extras/pro-customizables/Settings/Margins.php | 9 + extras/pro-customizables/Settings/Padding.php | 8 + extras/pro-customizables/Settings/Spacing.php | 60 + .../Settings/SpacingSetting.php | 46 + .../assets/_background-position.scss | 132 + .../assets/_border-style.scss | 13 + .../assets/_box-dimensions.scss | 196 + .../assets/_font-style-picker.scss | 82 + .../assets/combined-pro-controls.js | 283 + .../assets/combined-pro-controls.js.map | 1 + .../assets/combined-pro-controls.ts | 319 + extras/pro-customizables/assets/controls.css | 539 ++ .../pro-customizables/assets/controls.css.map | 1 + extras/pro-customizables/assets/controls.scss | 7 + .../pro-customizables/assets/customizable.js | 832 ++ .../assets/customizable.js.map | 1 + .../pro-customizables/assets/customizable.ts | 1249 +++ .../ame-box-dimensions/ame-box-dimensions.js | 294 + .../ame-box-dimensions.js.map | 1 + .../ame-box-dimensions/ame-box-dimensions.ts | 374 + .../ame-choice-control/ame-choice-control.js | 20 + .../ame-choice-control.js.map | 1 + .../ame-choice-control/ame-choice-control.ts | 38 + .../ame-code-editor/ame-code-editor.js | 33 + .../ame-code-editor/ame-code-editor.js.map | 1 + .../ame-code-editor/ame-code-editor.ts | 36 + .../ame-color-picker/ame-color-picker.js | 44 + .../ame-color-picker/ame-color-picker.js.map | 1 + .../ame-color-picker/ame-color-picker.ts | 49 + .../ko-components/ame-components.js | 51 + .../ko-components/ame-components.js.map | 1 + .../ko-components/ame-components.ts | 55 + .../ame-description/ame-description.js | 11 + .../ame-description/ame-description.js.map | 1 + .../ame-description/ame-description.ts | 13 + .../ame-font-style-picker.js | 164 + .../ame-font-style-picker.js.map | 1 + .../ame-font-style-picker.ts | 181 + .../ame-horizontal-separator.js | 10 + .../ame-horizontal-separator.js.map | 1 + .../ame-horizontal-separator.ts | 11 + .../ame-image-selector/ame-image-selector.js | 48 + .../ame-image-selector.js.map | 1 + .../ame-image-selector/ame-image-selector.ts | 64 + .../ame-nested-description.js | 14 + .../ame-nested-description.js.map | 1 + .../ame-nested-description.ts | 16 + .../ame-number-input/ame-number-input.js | 116 + .../ame-number-input/ame-number-input.js.map | 1 + .../ame-number-input/ame-number-input.ts | 151 + .../ame-radio-button-bar.js | 31 + .../ame-radio-button-bar.js.map | 1 + .../ame-radio-button-bar.ts | 36 + .../ame-radio-group/ame-radio-group.js | 99 + .../ame-radio-group/ame-radio-group.js.map | 1 + .../ame-radio-group/ame-radio-group.ts | 116 + .../ame-select-box/ame-select-box.js | 18 + .../ame-select-box/ame-select-box.js.map | 1 + .../ame-select-box/ame-select-box.ts | 20 + .../ame-si-control-group.js | 50 + .../ame-si-control-group.js.map | 1 + .../ame-si-control-group.ts | 58 + .../ame-si-section/ame-si-section.js | 64 + .../ame-si-section/ame-si-section.js.map | 1 + .../ame-si-section/ame-si-section.ts | 78 + .../ame-si-structure/ame-si-structure.css | 6 + .../ame-si-structure/ame-si-structure.css.map | 1 + .../ame-si-structure/ame-si-structure.js | 41 + .../ame-si-structure/ame-si-structure.js.map | 1 + .../ame-si-structure/ame-si-structure.scss | 4 + .../ame-si-structure/ame-si-structure.ts | 43 + .../ame-sibling-description.js | 16 + .../ame-sibling-description.js.map | 1 + .../ame-sibling-description.ts | 18 + .../ame-static-html/ame-static-html.js | 22 + .../ame-static-html/ame-static-html.js.map | 1 + .../ame-static-html/ame-static-html.ts | 26 + .../ame-text-input/ame-text-input.js | 27 + .../ame-text-input/ame-text-input.js.map | 1 + .../ame-text-input/ame-text-input.ts | 36 + .../ame-toggle-checkbox.js | 39 + .../ame-toggle-checkbox.js.map | 1 + .../ame-toggle-checkbox.ts | 49 + .../ame-unit-dropdown/ame-unit-dropdown.js | 18 + .../ame-unit-dropdown.js.map | 1 + .../ame-unit-dropdown/ame-unit-dropdown.ts | 30 + .../ame-wp-editor/ame-wp-editor.js | 165 + .../ame-wp-editor/ame-wp-editor.js.map | 1 + .../ame-wp-editor/ame-wp-editor.ts | 205 + .../ko-components/control-base.js | 285 + .../ko-components/control-base.js.map | 1 + .../ko-components/control-base.ts | 394 + .../lazy-popup-slider-adapter.js | 58 + .../lazy-popup-slider-adapter.js.map | 1 + .../lazy-popup-slider-adapter.ts | 72 + extras/style-generator/ConditionalAtRule.php | 49 + extras/style-generator/CssMediaQuery.php | 10 + extras/style-generator/CssRuleSet.php | 188 + extras/style-generator/CssStatement.php | 15 + extras/style-generator/Dsl/ArrayValue.php | 35 + extras/style-generator/Dsl/ConstantValue.php | 27 + extras/style-generator/Dsl/DslFunctions.php | 227 + extras/style-generator/Dsl/Expression.php | 30 + extras/style-generator/Dsl/FunctionCall.php | 43 + extras/style-generator/Dsl/JsFunctionCall.php | 77 + .../Dsl/SerializableToJsExpression.php | 11 + .../style-generator/Dsl/SettingReference.php | 37 + .../style-generator/Dsl/VariableReference.php | 38 + extras/style-generator/StyleGenerator.php | 454 + extras/style-generator/style-generator.js | 938 ++ extras/style-generator/style-generator.js.map | 1 + extras/style-generator/style-generator.ts | 1235 +++ extras/zod/LICENSE | 21 + extras/zod/README.md | 2739 ++++++ extras/zod/index.d.ts | 2 + extras/zod/lib/ZodError.d.ts | 163 + extras/zod/lib/ZodError.js | 124 + extras/zod/lib/errors.d.ts | 5 + extras/zod/lib/errors.js | 17 + extras/zod/lib/external.d.ts | 6 + extras/zod/lib/external.js | 18 + extras/zod/lib/helpers/enumUtil.d.ts | 8 + extras/zod/lib/helpers/enumUtil.js | 2 + extras/zod/lib/helpers/errorUtil.d.ts | 9 + extras/zod/lib/helpers/errorUtil.js | 8 + extras/zod/lib/helpers/parseUtil.d.ts | 78 + extras/zod/lib/helpers/parseUtil.js | 114 + extras/zod/lib/helpers/partialUtil.d.ts | 8 + extras/zod/lib/helpers/partialUtil.js | 2 + extras/zod/lib/helpers/typeAliases.d.ts | 2 + extras/zod/lib/helpers/typeAliases.js | 2 + extras/zod/lib/helpers/util.d.ts | 68 + extras/zod/lib/helpers/util.js | 142 + extras/zod/lib/index.d.ts | 4 + extras/zod/lib/index.js | 3953 ++++++++ extras/zod/lib/locales/en.d.ts | 3 + extras/zod/lib/locales/en.js | 129 + extras/zod/lib/types.d.ts | 1012 +++ extras/zod/package.json | 105 + images/eye-dashed.png | Bin 0 -> 1358 bytes images/eye-dashed.svg | 85 + images/gion/edit-paste.png | Bin 0 -> 412 bytes images/gnome-icon-theme/edit-cut-blue2.png | Bin 0 -> 1766 bytes images/gnome-icon-theme/edit-cut.png | Bin 0 -> 959 bytes images/page.png | Bin 0 -> 1452 bytes includes/AmeAutoloader.php | 47 + includes/ame-option.php | 16 + includes/ame-utils.php | 52 + includes/basic-dependencies.php | 21 + includes/editor-page.php | 1 - includes/menu-editor-core.php | 92 +- includes/menu-item.php | 48 +- includes/menu.php | 21 +- includes/module.php | 20 +- includes/persistent-module.php | 3 + js/actor-manager.js | 653 +- js/actor-manager.js.map | 2 +- js/actor-manager.ts | 486 +- js/common.d.ts | 58 + js/jquery.biscuit.d.ts | 8 + js/jquery.biscuit.js | 83 + js/jquery.color.d.ts | 204 + js/jquery.form.d.ts | 20 +- js/knockout-sortable.js | 494 + js/menu-editor.d.ts | 20 + js/menu-editor.js | 520 +- js/mini-func.js | 198 + js/mini-func.js.map | 1 + js/mini-func.ts | 274 + license-manager/BasicPluginLicensingUi.php | 4 +- license-manager/LicenseManager.php | 14 +- license-manager/LicenseServer.php | 18 +- menu-editor.php | 2 +- modules/actor-selector/actor-selector.js | 189 +- modules/actor-selector/actor-selector.js.map | 2 +- modules/actor-selector/actor-selector.ts | 50 +- .../plugin-visibility/plugin-visibility.js | 254 +- .../plugin-visibility.js.map | 2 +- .../plugin-visibility/plugin-visibility.php | 152 +- .../plugin-visibility/plugin-visibility.ts | 55 +- modules/redirector/redirector-ui.js | 496 +- modules/redirector/redirector-ui.js.map | 2 +- modules/redirector/redirector-ui.ts | 93 +- modules/redirector/redirector.css.map | 1 + modules/redirector/redirector.php | 110 +- package-lock.json | 4365 +++++++++ package.json | 18 + phpcs.xml | 19 +- plugin-updates/.gitattributes | 1 + plugin-updates/Puc/v5/PucFactory.php | 2 +- plugin-updates/Puc/v5p1/Autoloader.php | 86 + .../Puc/v5p1/DebugBar/Extension.php | 199 + plugin-updates/Puc/v5p1/DebugBar/Panel.php | 178 + .../Puc/v5p1/DebugBar/PluginExtension.php | 40 + .../Puc/v5p1/DebugBar/PluginPanel.php | 41 + .../Puc/v5p1/DebugBar/ThemePanel.php | 25 + plugin-updates/Puc/v5p1/InstalledPackage.php | 105 + plugin-updates/Puc/v5p1/Metadata.php | 140 + plugin-updates/Puc/v5p1/OAuthSignature.php | 102 + plugin-updates/Puc/v5p1/Plugin/Package.php | 188 + plugin-updates/Puc/v5p1/Plugin/PluginInfo.php | 136 + plugin-updates/Puc/v5p1/Plugin/Ui.php | 294 + plugin-updates/Puc/v5p1/Plugin/Update.php | 116 + .../Puc/v5p1/Plugin/UpdateChecker.php | 425 + plugin-updates/Puc/v5p1/PucFactory.php | 362 + plugin-updates/Puc/v5p1/Scheduler.php | 278 + plugin-updates/Puc/v5p1/StateStore.php | 209 + plugin-updates/Puc/v5p1/Theme/Package.php | 69 + plugin-updates/Puc/v5p1/Theme/Update.php | 88 + .../Puc/v5p1/Theme/UpdateChecker.php | 159 + plugin-updates/Puc/v5p1/Update.php | 38 + plugin-updates/Puc/v5p1/UpdateChecker.php | 1004 ++ plugin-updates/Puc/v5p1/UpgraderStatus.php | 200 + plugin-updates/Puc/v5p1/Utils.php | 70 + plugin-updates/Puc/v5p1/Vcs/Api.php | 379 + plugin-updates/Puc/v5p1/Vcs/BaseChecker.php | 29 + plugin-updates/Puc/v5p1/Vcs/BitBucketApi.php | 272 + plugin-updates/Puc/v5p1/Vcs/GitHubApi.php | 467 + plugin-updates/Puc/v5p1/Vcs/GitLabApi.php | 414 + .../Puc/v5p1/Vcs/PluginUpdateChecker.php | 260 + plugin-updates/Puc/v5p1/Vcs/Reference.php | 51 + .../Puc/v5p1/Vcs/ReleaseAssetSupport.php | 83 + .../Puc/v5p1/Vcs/ReleaseFilteringFeature.php | 108 + .../Puc/v5p1/Vcs/ThemeUpdateChecker.php | 83 + .../Puc/v5p1/Vcs/VcsCheckerMethods.php | 59 + plugin-updates/README.md | 91 +- plugin-updates/build/bump-version.php | 136 + plugin-updates/composer.json | 2 +- plugin-updates/js/debug-bar.js | 2 + .../languages/plugin-update-checker.pot | 12 +- plugin-updates/load-v5p1.php | 34 + plugin-updates/phpcs.xml | 21 + plugin-updates/plugin-update-checker.php | 4 +- plugin-updates/vendor/PucReadmeParser.php | 6 +- readme.txt | 60 +- requirement-notes.md | 8 + tsconfig.json | 9 +- uninstall.php | 51 + webpack.config.js | 89 + wp-dependency-wrapper/ScriptDependency.php | 590 ++ 551 files changed, 86090 insertions(+), 5089 deletions(-) create mode 100644 css/_forms.scss create mode 100644 customizables/Builders/BaseElementBuilder.php create mode 100644 customizables/Builders/CodeEditorBuilder.php create mode 100644 customizables/Builders/ContainerBuilder.php create mode 100644 customizables/Builders/ControlBuilder.php create mode 100644 customizables/Builders/EditorBuilder.php create mode 100644 customizables/Builders/ElementBuilder.php create mode 100644 customizables/Builders/ElementBuilderFactory.php create mode 100644 customizables/Builders/FormBuilder.php create mode 100644 customizables/Builders/GroupBuilder.php create mode 100644 customizables/Builders/InterfaceBuilder.php create mode 100644 customizables/Builders/NumberInputBuilder.php create mode 100644 customizables/Builders/RadioGroupBuilder.php create mode 100644 customizables/Builders/SectionBuilder.php create mode 100644 customizables/Builders/SettingFactory.php create mode 100644 customizables/Builders/StaticHtmlBuilder.php create mode 100644 customizables/Builders/StructChildSettingFactory.php create mode 100644 customizables/Builders/TextBoxBuilder.php create mode 100644 customizables/Builders/TextareaBuilder.php create mode 100644 customizables/Builders/ToggleCheckBoxBuilder.php create mode 100644 customizables/Controls/AbstractNumericControl.php create mode 100644 customizables/Controls/AlignmentSelector.php create mode 100644 customizables/Controls/CheckBox.php create mode 100644 customizables/Controls/ChoiceControl.php create mode 100644 customizables/Controls/ChoiceControlOption.php create mode 100644 customizables/Controls/ClassicControl.php create mode 100644 customizables/Controls/CodeEditor.php create mode 100644 customizables/Controls/ColorPicker.php create mode 100644 customizables/Controls/Container.php create mode 100644 customizables/Controls/ContentToggle.php create mode 100644 customizables/Controls/Control.php create mode 100644 customizables/Controls/ControlContainer.php create mode 100644 customizables/Controls/ControlGroup.php create mode 100644 customizables/Controls/ImageSelector.php create mode 100644 customizables/Controls/InterfaceStructure.php create mode 100644 customizables/Controls/NumberInput.php create mode 100644 customizables/Controls/PopupSlider.php create mode 100644 customizables/Controls/RadioButtonBar.php create mode 100644 customizables/Controls/RadioGroup.php create mode 100644 customizables/Controls/Section.php create mode 100644 customizables/Controls/SelectBox.php create mode 100644 customizables/Controls/StaticHtml.php create mode 100644 customizables/Controls/TextArea.php create mode 100644 customizables/Controls/TextInputControl.php create mode 100644 customizables/Controls/ToggleCheckbox.php create mode 100644 customizables/Controls/Toggleable.php create mode 100644 customizables/Controls/Tooltip.php create mode 100644 customizables/Controls/UiElement.php create mode 100644 customizables/Controls/WpEditor.php create mode 100644 customizables/Customizable.php create mode 100644 customizables/HtmlHelper.php create mode 100644 customizables/Rendering/AdminCustomizerListRenderer.php create mode 100644 customizables/Rendering/BoxyFormRenderer.php create mode 100644 customizables/Rendering/ClassicRenderer.php create mode 100644 customizables/Rendering/FormTableRenderer.php create mode 100644 customizables/Rendering/Renderer.php create mode 100644 customizables/Rendering/TabbedPanelRenderer.php create mode 100644 customizables/SampleModule.php create mode 100644 customizables/SettingCondition.php create mode 100644 customizables/Settings/AbstractSetting.php create mode 100644 customizables/Settings/AbstractStructSetting.php create mode 100644 customizables/Settings/BooleanSetting.php create mode 100644 customizables/Settings/ColorSetting.php create mode 100644 customizables/Settings/CompositeSetting.php create mode 100644 customizables/Settings/DeferredUpdateSubscriber.php create mode 100644 customizables/Settings/EnumSetting.php create mode 100644 customizables/Settings/FloatSetting.php create mode 100644 customizables/Settings/ImageSetting.php create mode 100644 customizables/Settings/IntegerSetting.php create mode 100644 customizables/Settings/MapSetting.php create mode 100644 customizables/Settings/NotificationSenderQueue.php create mode 100644 customizables/Settings/NumericSetting.php create mode 100644 customizables/Settings/PlainTextSetting.php create mode 100644 customizables/Settings/PredefinedSet.php create mode 100644 customizables/Settings/Setting.php create mode 100644 customizables/Settings/SettingGeneratorInterface.php create mode 100644 customizables/Settings/StringEnumSetting.php create mode 100644 customizables/Settings/StringSetting.php create mode 100644 customizables/Settings/UniqueSettingQueue.php create mode 100644 customizables/Settings/UpdateNotificationSender.php create mode 100644 customizables/Settings/UrlSetting.php create mode 100644 customizables/Settings/UserDefinedSetting.php create mode 100644 customizables/Settings/UserDefinedStruct.php create mode 100644 customizables/Settings/UserSanitizedStringSetting.php create mode 100644 customizables/SettingsForm.php create mode 100644 customizables/Storage/AbstractSettingsDictionary.php create mode 100644 customizables/Storage/ActiveMenuConfiguration.php create mode 100644 customizables/Storage/CompressedStorage.php create mode 100644 customizables/Storage/LazyArrayStorage.php create mode 100644 customizables/Storage/MenuConfigurationWrapper.php create mode 100644 customizables/Storage/ModuleSettings.php create mode 100644 customizables/Storage/NullStorage.php create mode 100644 customizables/Storage/ScopedOptionStorage.php create mode 100644 customizables/Storage/Slot.php create mode 100644 customizables/Storage/StorageInterface.php create mode 100644 customizables/Storage/StorageMethods.php create mode 100644 customizables/UpdateRequestHandler.php create mode 100644 customizables/Validation/ColorValidator.php create mode 100644 customizables/Validation/StringValidator.php create mode 100644 customizables/assets/_code-editor.scss create mode 100644 customizables/assets/_combined-base-controls.scss create mode 100644 customizables/assets/_image-selector.scss create mode 100644 customizables/assets/_number-input.scss create mode 100644 customizables/assets/_popup-slider.scss create mode 100644 customizables/assets/_radio-button-bar.scss create mode 100644 customizables/assets/_radio-group.scss create mode 100644 customizables/assets/combined-controls.js create mode 100644 customizables/assets/combined-controls.js.map create mode 100644 customizables/assets/combined-controls.ts create mode 100644 customizables/assets/controls.css create mode 100644 customizables/assets/controls.css.map create mode 100644 customizables/assets/controls.scss create mode 100644 customizables/assets/form-box.css create mode 100644 customizables/assets/form-box.css.map create mode 100644 customizables/assets/form-box.scss create mode 100644 customizables/assets/form-table.css create mode 100644 customizables/assets/form-table.css.map create mode 100644 customizables/assets/form-table.scss create mode 100644 customizables/assets/image-selector.js create mode 100644 customizables/assets/image-selector.js.map create mode 100644 customizables/assets/image-selector.ts create mode 100644 customizables/assets/popup-slider.d.ts create mode 100644 customizables/assets/popup-slider.js create mode 100644 customizables/assets/tabbed-panels.css create mode 100644 customizables/assets/tabbed-panels.css.map create mode 100644 customizables/assets/tabbed-panels.scss create mode 100644 customizables/assets/tinymce-5.d.ts create mode 100644 customizables/constants.php create mode 100644 dist/admin-customizer.bundle.js create mode 100644 dist/admin-customizer.bundle.js.map create mode 100644 dist/build.manifest.json create mode 100644 dist/customizable.bundle.js create mode 100644 dist/customizable.bundle.js.map create mode 100644 dist/runtime.bundle.js create mode 100644 dist/runtime.bundle.js.map create mode 100644 extras/dynamic-stylesheets/Cache/DummyCache.php create mode 100644 extras/dynamic-stylesheets/Cache/LocalTransientCache.php create mode 100644 extras/dynamic-stylesheets/Cache/NetworkTransientCache.php create mode 100644 extras/dynamic-stylesheets/Cache/StyleCacheInterface.php create mode 100644 extras/dynamic-stylesheets/Cache/TransientCache.php create mode 100644 extras/dynamic-stylesheets/DynamicStylesheetBundle.php create mode 100644 extras/dynamic-stylesheets/MenuScopedStylesheetHelper.php create mode 100644 extras/dynamic-stylesheets/Stylesheet.php create mode 100644 extras/jszip/jszip.d.ts create mode 100644 extras/jszip/jszip.min.js create mode 100644 extras/modules/admin-customizer/admin-customizer-base.js create mode 100644 extras/modules/admin-customizer/admin-customizer-base.js.map create mode 100644 extras/modules/admin-customizer/admin-customizer-base.ts create mode 100644 extras/modules/admin-customizer/admin-customizer-template.php create mode 100644 extras/modules/admin-customizer/admin-customizer.css create mode 100644 extras/modules/admin-customizer/admin-customizer.css.map create mode 100644 extras/modules/admin-customizer/admin-customizer.js create mode 100644 extras/modules/admin-customizer/admin-customizer.js.map create mode 100644 extras/modules/admin-customizer/admin-customizer.php create mode 100644 extras/modules/admin-customizer/admin-customizer.scss create mode 100644 extras/modules/admin-customizer/admin-customizer.ts create mode 100644 extras/modules/admin-customizer/admin-theme-template/admin-theme.php create mode 100644 extras/modules/admin-customizer/admin-theme-template/readme.txt create mode 100644 extras/modules/admin-customizer/communicator.js create mode 100644 extras/modules/admin-customizer/communicator.js.map create mode 100644 extras/modules/admin-customizer/communicator.ts create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-content-section.js create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-content-section.js.map create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-content-section.ts create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-control-group.js create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-control-group.js.map create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-control-group.ts create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-control.js create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-control.js.map create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-control.ts create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-section-link.js create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-section-link.js.map create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-section-link.ts create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-section.js create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-section.js.map create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-section.ts create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-separator.js create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-separator.js.map create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-separator.ts create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-structure.js create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-structure.js.map create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-structure.ts create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js.map create mode 100644 extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.ts create mode 100644 extras/modules/admin-customizer/preview-handler.js create mode 100644 extras/modules/admin-customizer/preview-handler.js.map create mode 100644 extras/modules/admin-customizer/preview-handler.ts create mode 100644 extras/modules/admin-customizer/preview.css create mode 100644 extras/modules/admin-customizer/preview.css.map create mode 100644 extras/modules/admin-customizer/preview.scss create mode 100644 extras/modules/admin-menu-colors/admin-menu-colors.js create mode 100644 extras/modules/admin-menu-colors/admin-menu-colors.js.map create mode 100644 extras/modules/admin-menu-colors/admin-menu-colors.php create mode 100644 extras/modules/admin-menu-colors/admin-menu-colors.ts create mode 100644 extras/modules/admin-menu-colors/color-dialog.php create mode 100644 extras/modules/admin-menu-colors/menu-colors-ui.css create mode 100644 extras/modules/admin-menu-colors/menu-colors-ui.css.map create mode 100644 extras/modules/admin-menu-colors/menu-colors-ui.scss create mode 100644 extras/modules/dashboard-styler/custom-css-preview.js create mode 100644 extras/modules/dashboard-styler/custom-css-preview.js.map create mode 100644 extras/modules/dashboard-styler/custom-css-preview.ts create mode 100644 extras/modules/dashboard-styler/dashboard-styler.php create mode 100644 extras/modules/dashboard-widget-editor/custom-columns.css create mode 100644 extras/modules/dashboard-widget-editor/custom-columns.css.map create mode 100644 extras/modules/dashboard-widget-editor/custom-columns.scss create mode 100644 extras/modules/dashboard-widget-editor/custom-widget-layout.js create mode 100644 extras/modules/dashboard-widget-editor/custom-widget-layout.js.map create mode 100644 extras/modules/dashboard-widget-editor/custom-widget-layout.ts create mode 100644 extras/modules/dashboard-widget-editor/dashboard-widget-editor.css.map create mode 100644 extras/modules/ko-customizable-dev/ko-customizable-dev.js create mode 100644 extras/modules/ko-customizable-dev/ko-customizable-dev.js.map create mode 100644 extras/modules/ko-customizable-dev/ko-customizable-dev.php create mode 100644 extras/modules/ko-customizable-dev/ko-customizable-dev.ts create mode 100644 extras/modules/menu-styler/menu-styler-features.js create mode 100644 extras/modules/menu-styler/menu-styler-features.js.map create mode 100644 extras/modules/menu-styler/menu-styler-features.ts create mode 100644 extras/modules/menu-styler/menu-styler-template.php create mode 100644 extras/modules/menu-styler/menu-styler-ui.js create mode 100644 extras/modules/menu-styler/menu-styler-ui.js.map create mode 100644 extras/modules/menu-styler/menu-styler-ui.ts create mode 100644 extras/modules/menu-styler/menu-styler.css create mode 100644 extras/modules/menu-styler/menu-styler.css.map create mode 100644 extras/modules/menu-styler/menu-styler.php create mode 100644 extras/modules/menu-styler/menu-styler.scss create mode 100644 extras/modules/separator-styles/MenuSeparatorStyler.php create mode 100644 extras/pro-admin-styles.css.map create mode 100644 extras/pro-autoloader.php create mode 100644 extras/pro-common-lib.js create mode 100644 extras/pro-common-lib.js.map create mode 100644 extras/pro-common-lib.ts create mode 100644 extras/pro-customizables/Controls/BackgroundPositionSelector.php create mode 100644 extras/pro-customizables/Controls/BackgroundRepeat.php create mode 100644 extras/pro-customizables/Controls/BorderStyleSelector.php create mode 100644 extras/pro-customizables/Controls/BoxDimensions.php create mode 100644 extras/pro-customizables/Controls/FontStylePicker.php create mode 100644 extras/pro-customizables/Controls/HorizontalSeparator.php create mode 100644 extras/pro-customizables/CssPropertyGenerator.php create mode 100644 extras/pro-customizables/CssValueGenerator.php create mode 100644 extras/pro-customizables/Settings/Background.php create mode 100644 extras/pro-customizables/Settings/BackgroundImageSetting.php create mode 100644 extras/pro-customizables/Settings/BackgroundSizeSetting.php create mode 100644 extras/pro-customizables/Settings/BorderRadius.php create mode 100644 extras/pro-customizables/Settings/BorderStyle.php create mode 100644 extras/pro-customizables/Settings/Borders.php create mode 100644 extras/pro-customizables/Settings/BoxShadow.php create mode 100644 extras/pro-customizables/Settings/CssBoxDimensions.php create mode 100644 extras/pro-customizables/Settings/CssColorSetting.php create mode 100644 extras/pro-customizables/Settings/CssEnumSetting.php create mode 100644 extras/pro-customizables/Settings/CssLengthSetting.php create mode 100644 extras/pro-customizables/Settings/CssSettingCollection.php create mode 100644 extras/pro-customizables/Settings/Font.php create mode 100644 extras/pro-customizables/Settings/IndividualBorder.php create mode 100644 extras/pro-customizables/Settings/Margins.php create mode 100644 extras/pro-customizables/Settings/Padding.php create mode 100644 extras/pro-customizables/Settings/Spacing.php create mode 100644 extras/pro-customizables/Settings/SpacingSetting.php create mode 100644 extras/pro-customizables/assets/_background-position.scss create mode 100644 extras/pro-customizables/assets/_border-style.scss create mode 100644 extras/pro-customizables/assets/_box-dimensions.scss create mode 100644 extras/pro-customizables/assets/_font-style-picker.scss create mode 100644 extras/pro-customizables/assets/combined-pro-controls.js create mode 100644 extras/pro-customizables/assets/combined-pro-controls.js.map create mode 100644 extras/pro-customizables/assets/combined-pro-controls.ts create mode 100644 extras/pro-customizables/assets/controls.css create mode 100644 extras/pro-customizables/assets/controls.css.map create mode 100644 extras/pro-customizables/assets/controls.scss create mode 100644 extras/pro-customizables/assets/customizable.js create mode 100644 extras/pro-customizables/assets/customizable.js.map create mode 100644 extras/pro-customizables/assets/customizable.ts create mode 100644 extras/pro-customizables/ko-components/ame-box-dimensions/ame-box-dimensions.js create mode 100644 extras/pro-customizables/ko-components/ame-box-dimensions/ame-box-dimensions.js.map create mode 100644 extras/pro-customizables/ko-components/ame-box-dimensions/ame-box-dimensions.ts create mode 100644 extras/pro-customizables/ko-components/ame-choice-control/ame-choice-control.js create mode 100644 extras/pro-customizables/ko-components/ame-choice-control/ame-choice-control.js.map create mode 100644 extras/pro-customizables/ko-components/ame-choice-control/ame-choice-control.ts create mode 100644 extras/pro-customizables/ko-components/ame-code-editor/ame-code-editor.js create mode 100644 extras/pro-customizables/ko-components/ame-code-editor/ame-code-editor.js.map create mode 100644 extras/pro-customizables/ko-components/ame-code-editor/ame-code-editor.ts create mode 100644 extras/pro-customizables/ko-components/ame-color-picker/ame-color-picker.js create mode 100644 extras/pro-customizables/ko-components/ame-color-picker/ame-color-picker.js.map create mode 100644 extras/pro-customizables/ko-components/ame-color-picker/ame-color-picker.ts create mode 100644 extras/pro-customizables/ko-components/ame-components.js create mode 100644 extras/pro-customizables/ko-components/ame-components.js.map create mode 100644 extras/pro-customizables/ko-components/ame-components.ts create mode 100644 extras/pro-customizables/ko-components/ame-description/ame-description.js create mode 100644 extras/pro-customizables/ko-components/ame-description/ame-description.js.map create mode 100644 extras/pro-customizables/ko-components/ame-description/ame-description.ts create mode 100644 extras/pro-customizables/ko-components/ame-font-style-picker/ame-font-style-picker.js create mode 100644 extras/pro-customizables/ko-components/ame-font-style-picker/ame-font-style-picker.js.map create mode 100644 extras/pro-customizables/ko-components/ame-font-style-picker/ame-font-style-picker.ts create mode 100644 extras/pro-customizables/ko-components/ame-horizontal-separator/ame-horizontal-separator.js create mode 100644 extras/pro-customizables/ko-components/ame-horizontal-separator/ame-horizontal-separator.js.map create mode 100644 extras/pro-customizables/ko-components/ame-horizontal-separator/ame-horizontal-separator.ts create mode 100644 extras/pro-customizables/ko-components/ame-image-selector/ame-image-selector.js create mode 100644 extras/pro-customizables/ko-components/ame-image-selector/ame-image-selector.js.map create mode 100644 extras/pro-customizables/ko-components/ame-image-selector/ame-image-selector.ts create mode 100644 extras/pro-customizables/ko-components/ame-nested-description/ame-nested-description.js create mode 100644 extras/pro-customizables/ko-components/ame-nested-description/ame-nested-description.js.map create mode 100644 extras/pro-customizables/ko-components/ame-nested-description/ame-nested-description.ts create mode 100644 extras/pro-customizables/ko-components/ame-number-input/ame-number-input.js create mode 100644 extras/pro-customizables/ko-components/ame-number-input/ame-number-input.js.map create mode 100644 extras/pro-customizables/ko-components/ame-number-input/ame-number-input.ts create mode 100644 extras/pro-customizables/ko-components/ame-radio-button-bar/ame-radio-button-bar.js create mode 100644 extras/pro-customizables/ko-components/ame-radio-button-bar/ame-radio-button-bar.js.map create mode 100644 extras/pro-customizables/ko-components/ame-radio-button-bar/ame-radio-button-bar.ts create mode 100644 extras/pro-customizables/ko-components/ame-radio-group/ame-radio-group.js create mode 100644 extras/pro-customizables/ko-components/ame-radio-group/ame-radio-group.js.map create mode 100644 extras/pro-customizables/ko-components/ame-radio-group/ame-radio-group.ts create mode 100644 extras/pro-customizables/ko-components/ame-select-box/ame-select-box.js create mode 100644 extras/pro-customizables/ko-components/ame-select-box/ame-select-box.js.map create mode 100644 extras/pro-customizables/ko-components/ame-select-box/ame-select-box.ts create mode 100644 extras/pro-customizables/ko-components/ame-si-control-group/ame-si-control-group.js create mode 100644 extras/pro-customizables/ko-components/ame-si-control-group/ame-si-control-group.js.map create mode 100644 extras/pro-customizables/ko-components/ame-si-control-group/ame-si-control-group.ts create mode 100644 extras/pro-customizables/ko-components/ame-si-section/ame-si-section.js create mode 100644 extras/pro-customizables/ko-components/ame-si-section/ame-si-section.js.map create mode 100644 extras/pro-customizables/ko-components/ame-si-section/ame-si-section.ts create mode 100644 extras/pro-customizables/ko-components/ame-si-structure/ame-si-structure.css create mode 100644 extras/pro-customizables/ko-components/ame-si-structure/ame-si-structure.css.map create mode 100644 extras/pro-customizables/ko-components/ame-si-structure/ame-si-structure.js create mode 100644 extras/pro-customizables/ko-components/ame-si-structure/ame-si-structure.js.map create mode 100644 extras/pro-customizables/ko-components/ame-si-structure/ame-si-structure.scss create mode 100644 extras/pro-customizables/ko-components/ame-si-structure/ame-si-structure.ts create mode 100644 extras/pro-customizables/ko-components/ame-sibling-description/ame-sibling-description.js create mode 100644 extras/pro-customizables/ko-components/ame-sibling-description/ame-sibling-description.js.map create mode 100644 extras/pro-customizables/ko-components/ame-sibling-description/ame-sibling-description.ts create mode 100644 extras/pro-customizables/ko-components/ame-static-html/ame-static-html.js create mode 100644 extras/pro-customizables/ko-components/ame-static-html/ame-static-html.js.map create mode 100644 extras/pro-customizables/ko-components/ame-static-html/ame-static-html.ts create mode 100644 extras/pro-customizables/ko-components/ame-text-input/ame-text-input.js create mode 100644 extras/pro-customizables/ko-components/ame-text-input/ame-text-input.js.map create mode 100644 extras/pro-customizables/ko-components/ame-text-input/ame-text-input.ts create mode 100644 extras/pro-customizables/ko-components/ame-toggle-checkbox/ame-toggle-checkbox.js create mode 100644 extras/pro-customizables/ko-components/ame-toggle-checkbox/ame-toggle-checkbox.js.map create mode 100644 extras/pro-customizables/ko-components/ame-toggle-checkbox/ame-toggle-checkbox.ts create mode 100644 extras/pro-customizables/ko-components/ame-unit-dropdown/ame-unit-dropdown.js create mode 100644 extras/pro-customizables/ko-components/ame-unit-dropdown/ame-unit-dropdown.js.map create mode 100644 extras/pro-customizables/ko-components/ame-unit-dropdown/ame-unit-dropdown.ts create mode 100644 extras/pro-customizables/ko-components/ame-wp-editor/ame-wp-editor.js create mode 100644 extras/pro-customizables/ko-components/ame-wp-editor/ame-wp-editor.js.map create mode 100644 extras/pro-customizables/ko-components/ame-wp-editor/ame-wp-editor.ts create mode 100644 extras/pro-customizables/ko-components/control-base.js create mode 100644 extras/pro-customizables/ko-components/control-base.js.map create mode 100644 extras/pro-customizables/ko-components/control-base.ts create mode 100644 extras/pro-customizables/ko-components/lazy-popup-slider-adapter.js create mode 100644 extras/pro-customizables/ko-components/lazy-popup-slider-adapter.js.map create mode 100644 extras/pro-customizables/ko-components/lazy-popup-slider-adapter.ts create mode 100644 extras/style-generator/ConditionalAtRule.php create mode 100644 extras/style-generator/CssMediaQuery.php create mode 100644 extras/style-generator/CssRuleSet.php create mode 100644 extras/style-generator/CssStatement.php create mode 100644 extras/style-generator/Dsl/ArrayValue.php create mode 100644 extras/style-generator/Dsl/ConstantValue.php create mode 100644 extras/style-generator/Dsl/DslFunctions.php create mode 100644 extras/style-generator/Dsl/Expression.php create mode 100644 extras/style-generator/Dsl/FunctionCall.php create mode 100644 extras/style-generator/Dsl/JsFunctionCall.php create mode 100644 extras/style-generator/Dsl/SerializableToJsExpression.php create mode 100644 extras/style-generator/Dsl/SettingReference.php create mode 100644 extras/style-generator/Dsl/VariableReference.php create mode 100644 extras/style-generator/StyleGenerator.php create mode 100644 extras/style-generator/style-generator.js create mode 100644 extras/style-generator/style-generator.js.map create mode 100644 extras/style-generator/style-generator.ts create mode 100644 extras/zod/LICENSE create mode 100644 extras/zod/README.md create mode 100644 extras/zod/index.d.ts create mode 100644 extras/zod/lib/ZodError.d.ts create mode 100644 extras/zod/lib/ZodError.js create mode 100644 extras/zod/lib/errors.d.ts create mode 100644 extras/zod/lib/errors.js create mode 100644 extras/zod/lib/external.d.ts create mode 100644 extras/zod/lib/external.js create mode 100644 extras/zod/lib/helpers/enumUtil.d.ts create mode 100644 extras/zod/lib/helpers/enumUtil.js create mode 100644 extras/zod/lib/helpers/errorUtil.d.ts create mode 100644 extras/zod/lib/helpers/errorUtil.js create mode 100644 extras/zod/lib/helpers/parseUtil.d.ts create mode 100644 extras/zod/lib/helpers/parseUtil.js create mode 100644 extras/zod/lib/helpers/partialUtil.d.ts create mode 100644 extras/zod/lib/helpers/partialUtil.js create mode 100644 extras/zod/lib/helpers/typeAliases.d.ts create mode 100644 extras/zod/lib/helpers/typeAliases.js create mode 100644 extras/zod/lib/helpers/util.d.ts create mode 100644 extras/zod/lib/helpers/util.js create mode 100644 extras/zod/lib/index.d.ts create mode 100644 extras/zod/lib/index.js create mode 100644 extras/zod/lib/locales/en.d.ts create mode 100644 extras/zod/lib/locales/en.js create mode 100644 extras/zod/lib/types.d.ts create mode 100644 extras/zod/package.json create mode 100644 images/eye-dashed.png create mode 100644 images/eye-dashed.svg create mode 100644 images/gion/edit-paste.png create mode 100644 images/gnome-icon-theme/edit-cut-blue2.png create mode 100644 images/gnome-icon-theme/edit-cut.png create mode 100644 images/page.png create mode 100644 includes/AmeAutoloader.php create mode 100644 js/jquery.color.d.ts create mode 100644 js/knockout-sortable.js create mode 100644 js/menu-editor.d.ts create mode 100644 js/mini-func.js create mode 100644 js/mini-func.js.map create mode 100644 js/mini-func.ts create mode 100644 modules/redirector/redirector.css.map create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 plugin-updates/.gitattributes create mode 100644 plugin-updates/Puc/v5p1/Autoloader.php create mode 100644 plugin-updates/Puc/v5p1/DebugBar/Extension.php create mode 100644 plugin-updates/Puc/v5p1/DebugBar/Panel.php create mode 100644 plugin-updates/Puc/v5p1/DebugBar/PluginExtension.php create mode 100644 plugin-updates/Puc/v5p1/DebugBar/PluginPanel.php create mode 100644 plugin-updates/Puc/v5p1/DebugBar/ThemePanel.php create mode 100644 plugin-updates/Puc/v5p1/InstalledPackage.php create mode 100644 plugin-updates/Puc/v5p1/Metadata.php create mode 100644 plugin-updates/Puc/v5p1/OAuthSignature.php create mode 100644 plugin-updates/Puc/v5p1/Plugin/Package.php create mode 100644 plugin-updates/Puc/v5p1/Plugin/PluginInfo.php create mode 100644 plugin-updates/Puc/v5p1/Plugin/Ui.php create mode 100644 plugin-updates/Puc/v5p1/Plugin/Update.php create mode 100644 plugin-updates/Puc/v5p1/Plugin/UpdateChecker.php create mode 100644 plugin-updates/Puc/v5p1/PucFactory.php create mode 100644 plugin-updates/Puc/v5p1/Scheduler.php create mode 100644 plugin-updates/Puc/v5p1/StateStore.php create mode 100644 plugin-updates/Puc/v5p1/Theme/Package.php create mode 100644 plugin-updates/Puc/v5p1/Theme/Update.php create mode 100644 plugin-updates/Puc/v5p1/Theme/UpdateChecker.php create mode 100644 plugin-updates/Puc/v5p1/Update.php create mode 100644 plugin-updates/Puc/v5p1/UpdateChecker.php create mode 100644 plugin-updates/Puc/v5p1/UpgraderStatus.php create mode 100644 plugin-updates/Puc/v5p1/Utils.php create mode 100644 plugin-updates/Puc/v5p1/Vcs/Api.php create mode 100644 plugin-updates/Puc/v5p1/Vcs/BaseChecker.php create mode 100644 plugin-updates/Puc/v5p1/Vcs/BitBucketApi.php create mode 100644 plugin-updates/Puc/v5p1/Vcs/GitHubApi.php create mode 100644 plugin-updates/Puc/v5p1/Vcs/GitLabApi.php create mode 100644 plugin-updates/Puc/v5p1/Vcs/PluginUpdateChecker.php create mode 100644 plugin-updates/Puc/v5p1/Vcs/Reference.php create mode 100644 plugin-updates/Puc/v5p1/Vcs/ReleaseAssetSupport.php create mode 100644 plugin-updates/Puc/v5p1/Vcs/ReleaseFilteringFeature.php create mode 100644 plugin-updates/Puc/v5p1/Vcs/ThemeUpdateChecker.php create mode 100644 plugin-updates/Puc/v5p1/Vcs/VcsCheckerMethods.php create mode 100644 plugin-updates/build/bump-version.php create mode 100644 plugin-updates/load-v5p1.php create mode 100644 plugin-updates/phpcs.xml create mode 100644 requirement-notes.md create mode 100644 webpack.config.js create mode 100644 wp-dependency-wrapper/ScriptDependency.php diff --git a/css/_forms.scss b/css/_forms.scss new file mode 100644 index 0000000..09e6976 --- /dev/null +++ b/css/_forms.scss @@ -0,0 +1,25 @@ +// General form mixins and styles. + +@mixin ame-visually-hide-input { + //Hide an input like a radio or a checkbox but leave it interactive. + position: absolute; + left: -9999em; + overflow: hidden; + clip: rect(0, 0, 0, 0); + margin: -1px; +} + +$invalidColor: #d63638; //Matches the Theme Customizer. + +@mixin ame-invalid-input-styles { + select, input { + &:invalid, &.ame-has-validation-errors { + border-color: $invalidColor; + + //Override the box shadow that WordPress adds on focus. + &:focus { + box-shadow: 0 0 0 1px $invalidColor; + } + } + } +} \ No newline at end of file diff --git a/css/_input-group.scss b/css/_input-group.scss index 273a9e9..9fd7b08 100644 --- a/css/_input-group.scss +++ b/css/_input-group.scss @@ -2,7 +2,7 @@ display: flex; flex-wrap: wrap; - > :not(:first-child) { + .ame-input-group-secondary, > :not(:first-child) { margin-left: -1px; border-top-left-radius: 0; border-bottom-left-radius: 0; diff --git a/css/menu-editor.css b/css/menu-editor.css index b612be6..db9f3f6 100644 --- a/css/menu-editor.css +++ b/css/menu-editor.css @@ -4,7 +4,7 @@ display: flex; flex-wrap: wrap; } -.ame-input-group > :not(:first-child) { +.ame-input-group .ame-input-group-secondary, .ame-input-group > :not(:first-child) { margin-left: -1px; border-top-left-radius: 0; border-bottom-left-radius: 0; @@ -936,74 +936,6 @@ select.ws_dropdown optgroup option { /************************************ Menu color picker *************************************/ -#ws-ame-menu-color-settings { - background: white; - display: none; -} - -#ame-menu-color-list { - height: 500px; - overflow-y: auto; -} - -.ame-menu-color-column { - min-width: 460px; -} - -.ame-menu-color-name { - display: inline-block; - vertical-align: top; - padding-top: 2px; - line-height: 1.3; - font-size: 14px; - font-weight: 600; - min-width: 180px; -} - -.ame-color-option { - padding: 10px 0; -} -.ame-color-option .wp-picker-container { - display: inline-block; -} - -.ame-advanced-menu-color { - display: none; -} - -#ws-ame-apply-colors-to-all { - display: block; - float: left; - margin-left: 5px; -} - -/* Color presets */ -#ame-color-preset-container { - padding: 0 8px 8px 8px; - margin-left: -8px; - margin-right: -8px; - margin-bottom: 4px; - border-bottom: 1px solid #eee; -} - -#ame-menu-color-presets { - width: 290px; - margin-right: 5px; -} - -#ws-ame-save-color-preset { - /*margin-right: 5px;*/ -} - -a#ws-ame-delete-color-preset { - color: #A00; - text-decoration: none; -} - -a#ws-ame-delete-color-preset:hover { - color: #F00; -} - /* Color scheme display in the editor widget. */ .ws_color_scheme_display { display: inline-block; @@ -1650,7 +1582,7 @@ a#ws-ame-delete-color-preset:hover { font-size: 18px; } -.ws_ame_custom_postbox .ws_tooltip_trigger .dashicons { +.ws_ame_custom_postbox .ws_tooltip_trigger .dashicons, .postbox .ws_tooltip_trigger .dashicons { font-size: 18px; height: 18px; vertical-align: bottom; diff --git a/css/menu-editor.css.map b/css/menu-editor.css.map index 1b905b9..988351e 100644 --- a/css/menu-editor.css.map +++ b/css/menu-editor.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["menu-editor.scss","_input-group.scss","_indeterminate-checkbox.scss","_test-access-screen.scss","_main-tabs.scss"],"names":[],"mappings":"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AAA2B;EAC3B;;;AAGD;AACA;EACC;;;AAGD;AACA;EACC;EACA;EACA;EAEA;AAA2B;EAC3B;;;AAGD;AAMA;AAAA;AAAA;AAIA;EACC;EAEA;EACA;EACA;EAEA;EACA;;;AASD;EACC;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;;AAEA;EACC;;;AAIF;EACC;EACA;;;AAID;EACC;;;AAED;EACC;EACA;;;AAED;EACC;EACA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;;AAID;EACC;EACA;EACA,SAJ0B;;;AAO3B;EACC;;;AAGD;AAAA;AAAA;AAAA;AAAA;AAQA;EACC;EACA;EACA;;AAEA;EACC;EAEA;EACA;EACA;EACA;EAEA;;AAGD;EACC;EACA,qBApBwB;EAqBxB;;AAGD;EACC;;AAGD;EACC;;;AAIF;EACC;EACA;EACA,kBApCyB;;;AAyC1B;AAAA;AAAA;AAMA;EACC;EACA;EACA;EAEA;EACA;EACG;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAIJ;EACC;EACA;EAEA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;AACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;;AAEA;EACC,OAnEc;EAoEd;;;AAIF;EACI;EACA;EACA;;;AAGJ;EACC;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;;;AAGD;AACA;EACC;EACA;EACA;EAEG;EACA;EACH;;AAEA;EACC,QA/dqB;EAgerB,YAheqB;EAierB;;;AAIF;EACC,OA9Ge;EA+Gf;;AAEA;EACC;;AAEA;EACC;;;AAKH;AACA;EACC;EACA;EACA;EACA;;;AAOA;EACC;EACA;EAMA;;AAJA;EACC;;AAMF;EACC;EACA;EACA;;;AAIF;EACC;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EAEA;EACA;EACA;EACA;EAEA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;AAAe;EACf;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAAA;AAIA;EACC;;;AAID;AAAA;AAAA;AAIA;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EAEA;EACA;EACA;EAEA;;;AAGJ;EACI;;AAEH;EACC;;;AAIF;EACI;;;AAGJ;EACC;EACA;EACA;;;AAGD;AACA;EACC;EAEA;EACA;EACA;EAEA;;;AAGD;EACC;EACA;;;AAGD;AACC;;;AAGD;EACC;EACA;;;AAED;EACC;;;AAGD;AAKA;EAGI;EACH;EACG,QAJgB;EAKhB,OATc;EAWd,cAVoB;EAWvB;EACG;EAEH;EACG;EACA;EACA;EAEH;;AAEA;EACC;EACA;EAEA;EACA;EAEA;EACA;EACA;;;AAIF;EACC;;;AAGD;EACI;EACA;EACA;EAEA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;AAAA;AAAA;AAIA;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEG;EACA;;;AAGJ;EACI;;;AAIH;EACC;EACA;EAEA;EAEA;EACA;EACA;;AAEA;EACC;EACA;;AAGD;EACC;EACA;EACA;;AAIF;EACC;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;EAEA;EACA;EAEA;;AAGD;EACC;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;;AAGD;AACC;EACA;;AAQF;EACC;;;AAGD;EACC;;;AAIA;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;;;AAGD;EACC;EACA;EACG;EACA;;;AAGJ;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACC;EACA;EAEA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACI;EACA;;;AAGJ;AAAA;AAAA;AAIA;AAGA;EAEC,OAJuB;EAKvB,cAJwB;;;AAOzB;EACC;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;;AAEA;EACC;EACA;;;AAIF;EACC;EACA;EACA;EACA;;;AAGD;AAAA;EAEC;EACA;EACA;;;AAGD;AAAA;EAEC;;;AAGD;EACC;;;AAGD;AACC;AAAA;EAEA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EAEA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAIA;AAAA;AAAA;AAGA;AAuBC;;AArBA;EACC;;AAGD;EACC;;AAGD;EACC;;AAIA;EACC;;AAED;EACC;;AAKF;EACC;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;EACA;;;AAKD;EACC,cAH0B;EAI1B;EACA;EACA;;;AAGD;EACC;;AAEA;EACC,cAbyB;;AAgB1B;EACC;EACA;;AAGD;AAAA;EAEC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;AAIA;EACC;;;AAQF;EACC;EACA;EACA;EACA;;AAGD;EACC;;AAID;EACC;;AAED;EACC;;AAKA;EACC;;AAED;EACC;;;AAQF;EACC;;;AAIF;AACA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;EAGA;EAEA;EACA;;;AAGD;EACC;EAEA;EACA;EACA;EACA;EACA;EAEA;EAEA;;;AAGD;EACC;;;AAID;EAGC,WAFgB;EAGhB,QAHgB;EAIhB,OAJgB;EAOhB;EAEA;EACA;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAQA;EAEC;EACA,SAFoB;;;AAKrB;EACC;;AAEA;EACC;EACA;EACA;EAEA;EACA;EACA;EAEA,OAtBwB;EAuBxB,QAtByB;EAwBzB;EACA;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EAGA,KAFc;EAGd,MA7C0B;EA8C1B,OA9C0B;EAiD1B;EAEA;EACA;;AAGD;EACC;EAGA;EACA;EACA;EACA;;AAEA;EACC;EACA;EAEA;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;AAGD;EACC;;AAKD;EACC;;AAGD;EACC;;AAID;EACC;;AAMD;EACC;;AAED;EACC;;AAED;EACC;;AAGD;EACC;;AAKA;EACC;;AAaH;EACC;EACA;EACA;EAEA;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;;;AAIF;EACC;EACA,OAzJ2B;EA0J3B,QA1J2B;EA4J3B;EACA;;;AAKD;AAAA;AAAA;AAIA;EACI;;;AAMJ;AAAA;AAAA;AAIA;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAID;EACC;;;AAID;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAIA;EACC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;AAGD;EACC;;;AAIF;EAEC;EACA;;;AAGD;EACC;;AAEA;EACC;EACA;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;;;AAIF;AAAA;AAAA;AAGA;EACC;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;AAEA;EACC;EACA;;AAGD;EAEC,aAr0CyB;EAs0CzB,gBAt0CyB;;AAy0C1B;EACC;;AAGD;EACC;;AAEA;EACC;;;AAKH;AAAA;AAAA;AAIA;EACC;EACA;EACA;;;AAIA;EACC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;;AAIF;EACC;;;AAGD;AACC;AAAA;;;AAID;AAAA;AAAA;AAIA;EACC;;;AAGD;AAAA;AAAA;AAKC;EACC;;AAGD;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;;AAIF;AAAA;AAAA;AAIA;EACI;EAEH;EACA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;;;AAGD;EACC;EAEA;;;AAGD;EACC;;;AAGD;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EAEA;EACA;EAEA;;;AAGJ;EACI;EACA;EAEA;EACA;;;AAGJ;EACI;;;AGlsEJ;AAAA;AAAA;AAIA;EACC;EACA;;;AAGD;EAEC;;;AAGD;EACC;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;;;AAIF;EACC;EACA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;;AAEA;EACC;;;AAIF;EACC;EACA;;;AAGD;EACC;;;ACjID;AAAA;AAAA;AAKC;EACC;;AAGD;EACC;EACA;EACA;;;AAIF;;AAAA;AAAA;AAAA;AAMA;EACC;;;AAIA;EACC;;;AAIF;AAAA;AAEA;EACC","file":"menu-editor.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["menu-editor.scss","_input-group.scss","_indeterminate-checkbox.scss","_test-access-screen.scss","_main-tabs.scss"],"names":[],"mappings":";AAAA;ACAA;EACC;EACA;;AAEA;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;;ADPF;EACC;;;AAGD;EACC;;;AAQD;EACC;EACA,OAPoB;EAQpB;EACA;EAEA;EACA;EACA;EAEA,eAb2B;EAc3B,oBAd2B;EAe3B,uBAf2B;;;AAkB5B;EACC;EACA;EACA;;;AAGD;EACC;EACA;;;AASD;EACC;EACA;EAEA;EACA;EAEA;;;AAGD;AAAA;AAAA;EAGC;;;AAGD;AAAA;EAEC;;;AAGD;AAAA;AAAA;AAGA;EACI;;;AAGJ;EACI;;;AAIH;EACC;EACA;;AAGD;EACC;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAIF;EACC;;;AAGD;AAAA;AAAA;AAKA;AAAA;EAGI;EACA;EACA;EACA;;AEnHH;AAAA;EACC;EACA,OAH4C;EAU5C;EACA;EAMA;EACA;EACA;EACA;EACA;;AAGD;EACC;AAAA;IAEC,QADU;IAEV,OAFU;IAGV,aAHU;IAIV;IAEA;IACA;IACA;;;;AFuFH;EAEE;IACC;IACA;;;AAKH;AACA;EACI;;;AAGJ;EACI;;;AAGJ;AAEA;EACI;;;AAGJ;EACI;;;AAGJ;AAAA;AAAA;AAIA;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;AAAA;AAAA;AAIA;EACC;;;AAGD;AAAA;AAAA;AAIA;EAMC;EACA,OANY;EAQZ,SAPc;EAQd;;AAEA;EACC,cATsB;EAUtB;;;AAWF;EACC;;;AAID;EACC;;;AAGD;EACC;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;;;AAMD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;AAAA;AAAA;AAAA;AAAA;AAKA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAwBA;AAAA;AAAA;AAAA;AAKA;EACC;;;AAGD;EAKC;EACA;EAGA;EACA;EACA;EAEA,OAVkB;EAWlB,QAZmB;EAanB;;;AAID;AAAA;EAEC;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EAEA;EACA;EAEA;EACA;EAEA;EACA;;;AAGD;EACI;EACA;EACA;;;AAGJ;EACC;EACA;;AAGA;EACC;EACA;EACA;EACA;EAEA;EACA;;;AAIF;EACC;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;AAAA;AAAA;AAGA;EAEC;EACA;EACA;EAEA;EACA;;AAEA;EACC;EACA;;;AAIF;AACA;EACC;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;;AAEA;EACC;;;AAIF;EACC;;;AAGD;AAAA;AAAA;EAGC;;;AAGD;AACA;AAAA;EAEC;;;AAGD;AAIA;AAAA;EAEC;EACA;EACA,OAPiB;EAQjB;EAEA;EACA;EAEA;EACA;;AAEA;AAAA;EACC,QAhBqB;EAiBrB;;;AAIF;EACC;EACA;;;AAGD;EACC;;;AAGD;EACC;;AAEA;EACC,YAlCqB;;;AAsCvB;AAGA;AAAA;EAGC;EACA,OANqB;EAOrB;EACA;EAEA;EACA;EAEA;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;;;AAGD;AAAA;EAGC,QAtEsB;EAwEtB;EACA;EACA;EAEA;EACA;EACA;;AAEA;AAAA;EACC;;;AAIF;EACC;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;AAAA;AAAA;AAAA;AAIA;AAAA;EAGC;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;AACA;EACC;;;AAGD;AAAA;AAAA;AAGA;EACC;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;;;AAGD;AACA;EACC;;;AAGD;AACA;EACC;EACA;;;AAGD;AACA;EACC;;;AAGD;AACA;EACC;;;AAED;AACC;AAAsB;EACtB;AAAkB;EAClB;EAEA;AAA2B;EAC3B;;;AAGD;AACA;EACC;;;AAGD;AACA;EACC;EACA;EACA;EAEA;AAA2B;EAC3B;;;AAGD;AAMA;AAAA;AAAA;AAIA;EACC;EAEA;EACA;EACA;EAEA;EACA;;;AASD;EACC;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;;AAEA;EACC;;;AAIF;EACC;EACA;;;AAID;EACC;;;AAED;EACC;EACA;;;AAED;EACC;EACA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;;AAID;EACC;EACA;EACA,SAJ0B;;;AAO3B;EACC;;;AAGD;AAAA;AAAA;AAAA;AAAA;AAQA;EACC;EACA;EACA;;AAEA;EACC;EAEA;EACA;EACA;EACA;EAEA;;AAGD;EACC;EACA,qBApBwB;EAqBxB;;AAGD;EACC;;AAGD;EACC;;;AAIF;EACC;EACA;EACA,kBApCyB;;;AAyC1B;AAAA;AAAA;AAMA;EACC;EACA;EACA;EAEA;EACA;EACG;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAIJ;EACC;EACA;EAEA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;AACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;;AAEA;EACC,OAnEc;EAoEd;;;AAIF;EACI;EACA;EACA;;;AAGJ;EACC;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;;;AAGD;AACA;EACC;EACA;EACA;EAEG;EACA;EACH;;AAEA;EACC,QA/dqB;EAgerB,YAheqB;EAierB;;;AAIF;EACC,OA9Ge;EA+Gf;;AAEA;EACC;;AAEA;EACC;;;AAKH;AACA;EACC;EACA;EACA;EACA;;;AAOA;EACC;EACA;EAMA;;AAJA;EACC;;AAMF;EACC;EACA;EACA;;;AAIF;EACC;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EAEA;EACA;EACA;EACA;EAEA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;AAAe;EACf;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAAA;AAIA;EACC;;;AAID;AAAA;AAAA;AAIA;AAKA;EAGI;EACH;EACG,QAJgB;EAKhB,OATc;EAWd,cAVoB;EAWvB;EACG;EAEH;EACG;EACA;EACA;EAEH;;AAEA;EACC;EACA;EAEA;EACA;EAEA;EACA;EACA;;;AAIF;EACC;;;AAGD;EACI;EACA;EACA;EAEA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;AAAA;AAAA;AAIA;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEG;EACA;;;AAGJ;EACI;;;AAIH;EACC;EACA;EAEA;EAEA;EACA;EACA;;AAEA;EACC;EACA;;AAGD;EACC;EACA;EACA;;AAIF;EACC;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;EAEA;EACA;EAEA;;AAGD;EACC;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;;AAGD;AACC;EACA;;AAQF;EACC;;;AAGD;EACC;;;AAIA;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;;;AAGD;EACC;EACA;EACG;EACA;;;AAGJ;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACC;EACA;EAEA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACI;EACA;;;AAGJ;AAAA;AAAA;AAIA;AAGA;EAEC,OAJuB;EAKvB,cAJwB;;;AAOzB;EACC;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;;AAEA;EACC;EACA;;;AAIF;EACC;EACA;EACA;EACA;;;AAGD;AAAA;EAEC;EACA;EACA;;;AAGD;AAAA;EAEC;;;AAGD;EACC;;;AAGD;AACC;AAAA;EAEA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EAEA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAIA;AAAA;AAAA;AAGA;AAuBC;;AArBA;EACC;;AAGD;EACC;;AAGD;EACC;;AAIA;EACC;;AAED;EACC;;AAKF;EACC;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;EACA;;;AAKD;EACC,cAH0B;EAI1B;EACA;EACA;;;AAGD;EACC;;AAEA;EACC,cAbyB;;AAgB1B;EACC;EACA;;AAGD;AAAA;EAEC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;AAIA;EACC;;;AAQF;EACC;EACA;EACA;EACA;;AAGD;EACC;;AAID;EACC;;AAED;EACC;;AAKA;EACC;;AAED;EACC;;;AAQF;EACC;;;AAIF;AACA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;EAGA;EAEA;EACA;;;AAGD;EACC;EAEA;EACA;EACA;EACA;EACA;EAEA;EAEA;;;AAGD;EACC;;;AAID;EAGC,WAFgB;EAGhB,QAHgB;EAIhB,OAJgB;EAOhB;EAEA;EACA;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAQA;EAEC;EACA,SAFoB;;;AAKrB;EACC;;AAEA;EACC;EACA;EACA;EAEA;EACA;EACA;EAEA,OAtBwB;EAuBxB,QAtByB;EAwBzB;EACA;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EAGA,KAFc;EAGd,MA7C0B;EA8C1B,OA9C0B;EAiD1B;EAEA;EACA;;AAGD;EACC;EAGA;EACA;EACA;EACA;;AAEA;EACC;EACA;EAEA;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;AAGD;EACC;;AAKD;EACC;;AAGD;EACC;;AAID;EACC;;AAMD;EACC;;AAED;EACC;;AAED;EACC;;AAGD;EACC;;AAKA;EACC;;AAaH;EACC;EACA;EACA;EAEA;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;;;AAIF;EACC;EACA,OAzJ2B;EA0J3B,QA1J2B;EA4J3B;EACA;;;AAKD;AAAA;AAAA;AAIA;EACI;;;AAMJ;AAAA;AAAA;AAIA;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAID;EACC;;;AAKA;EACC;EACA;EACA;;;AAIF;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAIA;EACC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;AAGD;EACC;;;AAIF;EAEC;EACA;;;AAGD;EACC;;AAEA;EACC;EACA;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;;;AAIF;AAAA;AAAA;AAGA;EACC;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;AAEA;EACC;EACA;;AAGD;EAEC,aA/vCyB;EAgwCzB,gBAhwCyB;;AAmwC1B;EACC;;AAGD;EACC;;AAEA;EACC;;;AAKH;AAAA;AAAA;AAIA;EACC;EACA;EACA;;;AAIA;EACC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;;AAIF;EACC;;;AAGD;AACC;AAAA;;;AAID;AAAA;AAAA;AAIA;EACC;;;AAGD;AAAA;AAAA;AAKC;EACC;;AAGD;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;;AAIF;AAAA;AAAA;AAIA;EACI;EAEH;EACA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;;;AAGD;EACC;EAEA;;;AAGD;EACC;;;AAGD;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EAEA;EACA;EAEA;;;AAGJ;EACI;EACA;EAEA;EACA;;;AAGJ;EACI;;;AG7nEJ;AAAA;AAAA;AAIA;EACC;EACA;;;AAGD;EAEC;;;AAGD;EACC;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;;;AAIF;EACC;EACA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;;AAEA;EACC;;;AAIF;EACC;EACA;;;AAGD;EACC;;;ACjID;AAAA;AAAA;AAKC;EACC;;AAGD;EACC;EACA;EACA;;;AAIF;;AAAA;AAAA;AAAA;AAMA;EACC;;;AAIA;EACC;;;AAIF;AAAA;AAEA;EACC","file":"menu-editor.css"} \ No newline at end of file diff --git a/css/menu-editor.scss b/css/menu-editor.scss index 916da07..6f1ed82 100644 --- a/css/menu-editor.scss +++ b/css/menu-editor.scss @@ -2,6 +2,7 @@ @import "boxes"; @import "input-group"; +@import "forms"; #ws_menu_editor { min-width: 780px; @@ -1055,78 +1056,6 @@ $iconFontColor: #85888c; Menu color picker *************************************/ -#ws-ame-menu-color-settings { - background: white; - display: none; -} - -#ame-menu-color-list { - height: 500px; - overflow-y: auto; -} - -.ame-menu-color-column { - min-width: 460px; -} - -.ame-menu-color-name { - display: inline-block; - vertical-align: top; - padding-top: 2px; - - line-height: 1.3; - font-size: 14px; - font-weight: 600; - - min-width: 180px; -} - -.ame-color-option { - padding: 10px 0; - - .wp-picker-container { - display: inline-block; - } -} - -.ame-advanced-menu-color { - display: none; -} - -#ws-ame-apply-colors-to-all { - display: block; - float: left; - margin-left: 5px; -} - -/* Color presets */ -#ame-color-preset-container { - padding: 0 8px 8px 8px; - - margin-left: -8px; - margin-right: -8px; - margin-bottom: 4px; - - border-bottom: 1px solid #eee; -} - -#ame-menu-color-presets { - width: 290px; - margin-right: 5px; -} - -#ws-ame-save-color-preset { - /*margin-right: 5px;*/ -} - -a#ws-ame-delete-color-preset { - color: #A00; - text-decoration: none; -} -a#ws-ame-delete-color-preset:hover { - color: #F00; -} - /* Color scheme display in the editor widget. */ $colorFieldWidth: 190px; @@ -1941,10 +1870,12 @@ $userSelectionPanelPadding: 10px; } //And in other boxes. -.ws_ame_custom_postbox .ws_tooltip_trigger .dashicons { - font-size: 18px; - height: 18px; - vertical-align: bottom; +.ws_ame_custom_postbox, .postbox { + .ws_tooltip_trigger .dashicons { + font-size: 18px; + height: 18px; + vertical-align: bottom; + } } .ws_tooltip_trigger.ame-warning-tooltip { diff --git a/customizables/Builders/BaseElementBuilder.php b/customizables/Builders/BaseElementBuilder.php new file mode 100644 index 0000000..4fe01c0 --- /dev/null +++ b/customizables/Builders/BaseElementBuilder.php @@ -0,0 +1,133 @@ + + */ + protected $elementClass; + + /** + * @param class-string<\YahnisElsts\AdminMenuEditor\Customizable\Controls\UiElement> $elementClass + * @param array $params + */ + protected function __construct($elementClass, $params = array()) { + $this->elementClass = $elementClass; + $this->params = $params; + } + + protected static function buildItems($items, $preserveKeys = false) { + $results = array(); + foreach ($items as $key => $item) { + if ( is_array($item) ) { + //Flatten nested arrays of buildable things. + $results = array_merge($results, self::buildItems($item, $preserveKeys)); + continue; + } + + if ( $item instanceof ElementBuilder ) { + $item = $item->build(); + } elseif ( !($item instanceof UiElement) ) { + $typeString = is_object($item) ? get_class($item) : gettype($item); + throw new \InvalidArgumentException( + 'Invalid item type for an element builder: ' . $typeString + ); + } + + if ( $preserveKeys ) { + $results[$key] = $item; + } else { + $results[] = $item; + } + } + return $results; + } + + public function id($string) { + $this->params['id'] = $string; + return $this; + } + + public function getCustomId() { + return isset($this->params['id']) ? $this->params['id'] : null; + } + + /** + * @param string|callable $textOrCallback + * @return $this + */ + public function description($textOrCallback) { + $this->params['description'] = $textOrCallback; + return $this; + } + + public function classes(...$cssClassNames) { + return $this->addItemsToArrayParam('classes', $cssClassNames); + } + + /** + * Add CSS class names if their values are truthy. + * + * @param array $classEnabled ['class-a' => true, 'class-b' => false, ...] + * @return $this + */ + public function conditionalClasses($classEnabled) { + return $this->classes(...array_keys(array_filter($classEnabled))); + } + + public function styles($propertyPairs) { + return $this->addItemsToArrayParam('style', $propertyPairs); + } + + /** + * @param string $paramName + * @param $items + * @return $this + */ + protected function addItemsToArrayParam($paramName, $items) { + if ( !isset($this->params[$paramName]) ) { + $this->params[$paramName] = array(); + } + $this->params[$paramName] = array_merge($this->params[$paramName], (array)$items); + return $this; + } + + /** + * Set one or more parameters for the element. + * + * Will overwrite any existing parameters with the same name. + * + * @param array $additionalParams + * @return $this + */ + public function params($additionalParams) { + $this->params = array_merge($this->params, $additionalParams); + return $this; + } + + /** + * Render the element only if the condition evaluates to true. + * + * @param bool|callable $condition + */ + public function onlyIf($condition) { + $this->params['renderCondition'] = $condition; + return $this; + } + + /** + * @return ElementClass + */ + abstract public function build(); +} \ No newline at end of file diff --git a/customizables/Builders/CodeEditorBuilder.php b/customizables/Builders/CodeEditorBuilder.php new file mode 100644 index 0000000..3edc3d4 --- /dev/null +++ b/customizables/Builders/CodeEditorBuilder.php @@ -0,0 +1,26 @@ +params['mimeType'] = 'text/css'; + return $this; + } + + public function jsMode() { + $this->params['mimeType'] = 'application/javascript'; + return $this; + } + + public function htmlMode() { + $this->params['mimeType'] = 'text/html'; + return $this; + } +} \ No newline at end of file diff --git a/customizables/Builders/ContainerBuilder.php b/customizables/Builders/ContainerBuilder.php new file mode 100644 index 0000000..ea1b0a8 --- /dev/null +++ b/customizables/Builders/ContainerBuilder.php @@ -0,0 +1,64 @@ + + */ + protected $children = array(); + + /** + * @param class-string<\YahnisElsts\AdminMenuEditor\Customizable\Controls\Container> $containerClass + * @param string $title + */ + protected function __construct($containerClass, $title, $children = array()) { + parent::__construct($containerClass); + $this->title = $title; + $this->children = $children; + } + + /** + * @return UiElement[] + */ + protected function buildChildren() { + return self::buildItems($this->children); + } + + public function build() { + $className = $this->elementClass; + return new $className($this->title, $this->buildChildren(), $this->params); + } + + /** + * @param ElementBuilder|UiElement ...$children + * @return $this + */ + public function add(...$children) { + return $this->addAll($children); + } + + /** + * @param array $children + * @return $this + */ + public function addAll($children) { + foreach ($children as $child) { + $this->children[] = $child; + } + return $this; + } + + public function tooltip($html, $type = Controls\Tooltip::DEFAULT_TYPE) { + $this->params['tooltip'] = new Controls\Tooltip($html, $type); + return $this; + } +} \ No newline at end of file diff --git a/customizables/Builders/ControlBuilder.php b/customizables/Builders/ControlBuilder.php new file mode 100644 index 0000000..1dc4124 --- /dev/null +++ b/customizables/Builders/ControlBuilder.php @@ -0,0 +1,103 @@ + $controlClass + * @param array $settings + * @param array $params + */ + public function __construct($controlClass, $settings = array(), $params = array()) { + parent::__construct($controlClass, $params); + $this->settings = $settings; + } + + /** + * @param string|null $text + * @return $this + */ + public function label($text) { + $this->params['label'] = $text; + return $this; + } + + /** + * @param ...$cssClassNames + * @return $this + */ + public function inputClasses(...$cssClassNames) { + return $this->addItemsToArrayParam('inputClasses', $cssClassNames); + } + + public function inputStyles($propertyPairs) { + return $this->addItemsToArrayParam('inputStyles', $propertyPairs); + } + + public function inputAttr($attributePairs) { + return $this->addItemsToArrayParam('inputAttributes', $attributePairs); + } + + public function setting(Settings\Setting $setting) { + $this->settings[] = $setting; + return $this; + } + + /** + * @param bool|\YahnisElsts\AdminMenuEditor\Customizable\SettingCondition $enabled + * @return $this + */ + public function enabled($enabled) { + $this->params['enabled'] = $enabled; + return $this; + } + + /** + * Wrap a new control group around this control. + * + * By default, the group will use the group title assigned to the associated + * setting, or the setting/control label. + * + * @param string|null $groupTitle + * @return GroupBuilder + */ + public function asGroup($groupTitle = null) { + if ( $groupTitle === null ) { + //Settings can have an optional group title assigned to them in case + //a setting is displayed as a standalone group. + $firstSetting = reset($this->settings); + if ( $firstSetting instanceof Settings\AbstractSetting ) { + $groupTitle = $firstSetting->getCustomGroupTitle(); + } + + if ( empty($groupTitle) ) { + if ( !empty($this->params['label']) ) { + //Use the control label as the group title. + $groupTitle = $this->params['label']; + } else if ( $firstSetting instanceof Settings\AbstractSetting ) { + //Use the setting label. + $groupTitle = $firstSetting->getLabel(); + } + } + } + return new GroupBuilder($groupTitle, array($this)); + } + + /** + * @return ElementClass + */ + public function build() { + $className = $this->elementClass; + return new $className($this->settings, $this->params); + } +} \ No newline at end of file diff --git a/customizables/Builders/EditorBuilder.php b/customizables/Builders/EditorBuilder.php new file mode 100644 index 0000000..bede3bb --- /dev/null +++ b/customizables/Builders/EditorBuilder.php @@ -0,0 +1,29 @@ +params['rows'] = $rows; + return $this; + } + + /** + * @param bool $isTeeny + * @return $this + */ + public function setTeeny($isTeeny) { + $this->params['teeny'] = $isTeeny; + return $this; + } +} \ No newline at end of file diff --git a/customizables/Builders/ElementBuilder.php b/customizables/Builders/ElementBuilder.php new file mode 100644 index 0000000..45d76ad --- /dev/null +++ b/customizables/Builders/ElementBuilder.php @@ -0,0 +1,14 @@ +settingLookup = $settingLookup; + } + + /** + * @return \YahnisElsts\AdminMenuEditor\Customizable\Storage\AbstractSettingsDictionary|null + */ + public function getSettingDictionary() { + return $this->settingLookup; + } + + /** + * @param \YahnisElsts\AdminMenuEditor\Customizable\Controls\Container|ContainerBuilder ...$containers + * @return InterfaceBuilder + */ + public function structure(...$containers) { + $builder = new InterfaceBuilder(); + foreach ($containers as $container) { + $builder->add($container); + } + return $builder; + } + + /** + * + * @param string $title + * @param ElementBuilder|UiElement|ElementBuilder[]|UiElement[] ...$children + * @return SectionBuilder + */ + public function section($title = '', ...$children) { + return new SectionBuilder($title, $children); + } + + /** + * @param string $title + * @param ElementBuilder|UiElement ...$children + * @return GroupBuilder + */ + public function group($title = '', ...$children) { + return new GroupBuilder($title, $children); + } + + /** + * @param string|Setting|null $idOrSetting + * @return TextBoxBuilder + */ + public function textBox($idOrSetting = '') { + return new TextBoxBuilder($this->findSettings($idOrSetting)); + } + + /** + * @param string|Setting|null $idOrSetting + * @return ControlBuilder<\YahnisElsts\AdminMenuEditor\Customizable\Controls\CheckBox> + */ + public function checkBox($idOrSetting = null) { + return $this->initControlBuilder(Controls\CheckBox::class, $idOrSetting); + } + + /** + * @param class-string<\YahnisElsts\AdminMenuEditor\Customizable\Controls\Control> $controlClass + * @param string|Setting|array $idOrSetting + */ + protected function initControlBuilder($controlClass, $idOrSetting) { + return new ControlBuilder($controlClass, $this->findSettings($idOrSetting)); + } + + /** + * @param Setting|null $idOrSetting + * @return TextareaBuilder + */ + public function textArea($idOrSetting = null) { + return new TextareaBuilder($this->findSettings($idOrSetting), []); + } + + /** + * @param string $rawHtml + * @return StaticHtmlBuilder + */ + public function html($rawHtml) { + return new StaticHtmlBuilder($rawHtml); + } + + /** + * @param Setting|null|string $idOrSetting + * @return EditorBuilder + */ + public function editor($idOrSetting = null) { + return new EditorBuilder($this->findSettings($idOrSetting)); + } + + /** + * @param Setting|null|string $idOrSetting + * @return ControlBuilder<\YahnisElsts\AdminMenuEditor\Customizable\Controls\RadioGroup> + */ + public function radioGroup($idOrSetting = null) { + return new RadioGroupBuilder($this->findSettings($idOrSetting)); + } + + /** + * @param Setting|null|string $idOrSetting + * @return ControlBuilder<\YahnisElsts\AdminMenuEditor\Customizable\Controls\SelectBox> + */ + public function select($idOrSetting = null) { + return $this->initControlBuilder(Controls\SelectBox::class, $idOrSetting); + } + + /** + * @param Setting|null|string $idOrSetting + * @return ControlBuilder<\YahnisElsts\AdminMenuEditor\Customizable\Controls\ColorPicker> + */ + public function colorPicker($idOrSetting = null) { + return $this->initControlBuilder(Controls\ColorPicker::class, $idOrSetting); + } + + public function imageSelector($idOrSetting = null) { + return $this->initControlBuilder(Controls\ImageSelector::class, $idOrSetting); + } + + public function codeEditor($idOrSetting = null) { + return new CodeEditorBuilder($this->findSettings($idOrSetting)); + } + + public function backgroundPosition($idOrSetting = null) { + return $this->initControlBuilder( + BackgroundPositionSelector::class, + $idOrSetting + ); + } + + public function backgroundRepeat($idOrSetting = null) { + return $this->initControlBuilder(BackgroundRepeat::class, $idOrSetting); + } + + public function backgroundSize($idOrSetting = null) { + return $this->radioGroup($idOrSetting) + ->label('Image size') + ->params(['descriptionsAsTooltips' => true]); + } + + public function fontStyle($idOrSetting = null) { + return $this->initControlBuilder(FontStylePicker::class, $idOrSetting); + } + + public function toggleCheckBox($idOrSetting = null) { + return new ToggleCheckBoxBuilder($this->findSettings($idOrSetting)); + } + + /** + * Create a "Save Changes" button. It uses the default submit button settings. + * + * @param boolean $wrapInParagraph + * @return \YahnisElsts\AdminMenuEditor\Customizable\Builders\StaticHtmlBuilder + */ + public function saveButton($wrapInParagraph = false) { + return $this->html( + get_submit_button(null, 'primary', 'submit', $wrapInParagraph) + ); + } + + public function number($idOrSetting = null) { + return new NumberInputBuilder($this->findSettings($idOrSetting)); + } + + public function boxDimensions($idOrSetting = null) { + return $this->initControlBuilder(BoxDimensions::class, $idOrSetting); + } + + /** + * Automatically choose a suitable control or container for the given setting. + * + * @param \YahnisElsts\AdminMenuEditor\Customizable\Settings\AbstractSetting|string $idOrSetting + * @return BaseElementBuilder + */ + public function auto($idOrSetting) { + list($setting) = $this->findSettings($idOrSetting); + + if ( $setting instanceof Settings\EnumSetting ) { + return $this->radioGroup($setting); + } else if ( $setting instanceof Settings\BooleanSetting ) { + return $this->toggleCheckBox($setting)->onValue(true)->offValue(false); + } else if ( $setting instanceof Settings\ImageSetting ) { + return $this->imageSelector($setting); + } else if ( $setting instanceof Settings\NumericSetting ) { + return $this->number($setting); + } else if ( $setting instanceof Settings\PredefinedSet ) { + return $this->autoSection($setting); + } else if ( $setting instanceof Settings\AbstractSetting ) { + switch ($setting->getDataType()) { + case 'color': + return $this->colorPicker($setting); + case 'url': + return $this->textBox($setting)->type('url')->code(); + default: + return $this->textBox($setting); + } + } else { + if ( empty($setting) ) { + throw new \InvalidArgumentException("Setting not found: " . $idOrSetting); + } else { + throw new \InvalidArgumentException("Unsupported setting type: " . get_class($setting)); + } + } + } + + /** + * Create a section from the specified setting(s). Supports predefined sets of settings + * as well as regular structs. You could even pass a single setting, but that's not very useful. + * + * @param $idOrSetting + * @param string|null $title + * @param string $role + * @return SectionBuilder + */ + public function autoSection($idOrSetting, $title = null, $role = Controls\Section::CONTENT_ROLE) { + $settings = $this->findSettings($idOrSetting); + if ( empty($settings) && is_string($idOrSetting) ) { + $predefinedSet = $this->settingLookup->getPredefinedSet($idOrSetting); + if ( $predefinedSet !== null ) { + $settings = [$predefinedSet]; + } + } + + $firstSetting = reset($settings); + if ( $firstSetting instanceof Settings\PredefinedSet ) { + $controls = $firstSetting->createControls($this); + return $this->section( + $title ?: $firstSetting->getLabel(), + ...$controls + )->params(['preferredRole' => $role]); + } + + if ( !empty($settings) ) { + $section = $this->section($title)->params(['preferredRole' => $role]); + foreach ($settings as $setting) { + $section->add($this->auto($setting)); + } + return $section; + } else { + throw new \InvalidArgumentException("Setting not found: " . $idOrSetting); + } + } + + /** + * @param string $title + * @param ElementBuilder|UiElement|ElementBuilder[]|UiElement[] ...$children + * @return \YahnisElsts\AdminMenuEditor\Customizable\Builders\SectionBuilder + */ + public function contentSection($title = '', ...$children) { + return $this->section($title, $children) + ->params(['preferredRole' => Controls\Section::CONTENT_ROLE]); + } + + /** + * @template T of \YahnisElsts\AdminMenuEditor\Customizable\Controls\Control + * @param class-string $controlClass + * @param Setting|null|string $idOrSetting + * @return ControlBuilder + */ + public function control($controlClass, $idOrSetting = null) { + return $this->initControlBuilder($controlClass, $idOrSetting); + } + + /** + * @param Setting|string|null|array $idOrSetting + * @return Setting[] + */ + protected function findSettings($idOrSetting) { + if ( $idOrSetting instanceof Settings\AbstractSetting ) { + return [$idOrSetting]; + } else if ( is_string($idOrSetting) ) { + if ( isset($this->settingLookup) ) { + return [$this->settingLookup->getSetting($idOrSetting)]; + } else { + throw new \InvalidArgumentException(sprintf( + 'Cannot find a setting "%s" because no setting lookup was provided.', + $idOrSetting + )); + } + } else if ( $idOrSetting === null ) { + return []; + } else if ( is_array($idOrSetting) ) { + $settings = []; + foreach ($idOrSetting as $key => $setting) { + $found = $this->findSettings($setting); + $settings[$key] = !empty($found) ? reset($found) : null; + } + return $settings; + } + throw new \InvalidArgumentException(sprintf( + 'Invalid setting query (type: "%s"). Input must be a Setting, a string, or NULL.', + gettype($idOrSetting) + )); + } +} \ No newline at end of file diff --git a/customizables/Builders/FormBuilder.php b/customizables/Builders/FormBuilder.php new file mode 100644 index 0000000..7546663 --- /dev/null +++ b/customizables/Builders/FormBuilder.php @@ -0,0 +1,152 @@ +params['structure'] = $structure; + return $this; + } + + /** + * @param array $settings + * @return $this + */ + public function settings($settings) { + $this->params['settings'] = $settings; + return $this; + } + + public function renderer(Renderer $renderer) { + $this->params['renderer'] = $renderer; + return $this; + } + + /** + * Set the action name that will be used for nonce generation, hooks, + * and the hidden "action" field. + * + * Not to be confused with the "action" attribute of a form element. + * Use the submitUrl() method to set that. + * + * @param string $actionName + * @return $this + */ + public function actionName($actionName) { + $this->params['action'] = $actionName; + return $this; + } + + /** + * @param string $httpMethod Either 'get' or 'post'. + * @return $this + */ + public function method($httpMethod) { + $httpMethod = trim(strtolower($httpMethod)); + if ( ($httpMethod !== 'get') && ($httpMethod !== 'post') ) { + throw new \InvalidArgumentException(sprintf( + 'Invalid HTTP method "%s" for a settings form. Must be "get" or "post".', + $httpMethod + )); + } + + $this->params['method'] = $httpMethod; + return $this; + } + + public function submitUrl($url) { + $this->params['submitUrl'] = $url; + return $this; + } + + public function requiredCapability($capability) { + $this->params['requiredCapability'] = $capability; + return $this; + } + + /** + * @param callable $callback + * @return $this + */ + public function permissionCallback($callback) { + $this->params['permissionCallback'] = $callback; + return $this; + } + + public function id($id) { + $this->params['id'] = $id; + return $this; + } + + /** + * @param bool $shouldAddButton + * @return $this + */ + public function addDefaultSubmitButton($shouldAddButton = true) { + $this->params['defaultSubmitButtonEnabled'] = $shouldAddButton; + return $this; + } + + public function redirectAfterSaving($url, $successParams = array('updated' => 1)) { + $this->params['redirectUrl'] = $url; + $this->params['successParams'] = $successParams; + return $this; + } + + public function passThroughParams($params) { + $this->params['passThroughParams'] = $params; + return $this; + } + + public function dieOnError() { + $this->params['errorReporting'] = UpdateRequestHandler::DIE_ON_ERRORS; + return $this; + } + + public function storeErrors($transientName = null) { + $this->params['errorReporting'] = UpdateRequestHandler::STORE_ERRORS; + $this->params['errorTransientName'] = $transientName; + return $this; + } + + public function postProcessSettings($callback) { + $this->params['postProcessingCallback'] = $callback; + return $this; + } + + public function skipMissingFields() { + $this->params['missingFieldHandling'] = UpdateRequestHandler::SKIP_MISSING_FIELDS; + return $this; + } + + public function treatMissingFieldsAsEmpty() { + $this->params['missingFieldHandling'] = UpdateRequestHandler::TREAT_MISSING_FIELDS_AS_EMPTY; + return $this; + } + + public function allowPartialUpdates() { + $this->params['partialUpdatesAllowed'] = true; + return $this; + } + + public function forbidPartialUpdates() { + $this->params['partialUpdatesAllowed'] = false; + return $this; + } + + public function stopOnFirstValidationError() { + $this->params['$stopOnFirstError'] = true; + return $this; + } + + public function build() { + return new SettingsForm($this->params); + } +} \ No newline at end of file diff --git a/customizables/Builders/GroupBuilder.php b/customizables/Builders/GroupBuilder.php new file mode 100644 index 0000000..033bae7 --- /dev/null +++ b/customizables/Builders/GroupBuilder.php @@ -0,0 +1,28 @@ +title, $this->buildChildren(), $this->params); + } + + public function stacked($isStacked = true) { + $this->params['stacked'] = $isStacked; + return $this; + } + + public function fieldset($wantsFieldset = true) { + $this->params['fieldset'] = $wantsFieldset; + return $this; + } +} \ No newline at end of file diff --git a/customizables/Builders/InterfaceBuilder.php b/customizables/Builders/InterfaceBuilder.php new file mode 100644 index 0000000..03613e9 --- /dev/null +++ b/customizables/Builders/InterfaceBuilder.php @@ -0,0 +1,89 @@ +children[] = $container; + return $this; + } + + /** + * Add a container before the first direct descendant that has the specified ID. + * + * If there is no child with that ID, the container will be added to the beginning + * of the list. + * + * @param Controls\Container|ContainerBuilder $container + * @param string $beforeId + * @return $this + */ + public function addBefore($container, $beforeId) { + $index = $this->findChildIndex($beforeId); + if ( $index === false ) { + array_unshift($this->children, $container); + } else { + array_splice($this->children, $index, 0, [$container]); + } + return $this; + } + + /** + * @param Controls\Container|ContainerBuilder $container + * @param string $afterId + * @return $this + */ + public function addAfter($container, $afterId) { + $index = $this->findChildIndex($afterId); + if ( $index === false ) { + $this->children[] = $container; + } else { + array_splice($this->children, $index + 1, 0, [$container]); + } + return $this; + } + + protected function findChildIndex($id) { + foreach ($this->children as $index => $child) { + if ( $child instanceof Controls\UiElement ) { + $childId = $child->getId(); + } else if ( $child instanceof BaseElementBuilder ) { + $childId = $child->getCustomId(); + } else { + $childId = null; + } + + if ( $childId === $id ) { + return $index; + } + } + return null; + } + + /** + * @return \YahnisElsts\AdminMenuEditor\Customizable\Controls\InterfaceStructure + */ + public function build() { + return new Controls\InterfaceStructure('', $this->buildChildren()); + } + + protected function buildChildren() { + $children = array(); + foreach ($this->children as $child) { + if ( $child instanceof ElementBuilder ) { + $children[] = $child->build(); + } else { + $children[] = $child; + } + } + return $children; + } +} \ No newline at end of file diff --git a/customizables/Builders/NumberInputBuilder.php b/customizables/Builders/NumberInputBuilder.php new file mode 100644 index 0000000..25def5d --- /dev/null +++ b/customizables/Builders/NumberInputBuilder.php @@ -0,0 +1,41 @@ +params['min'] = $min; + return $this; + } + + public function max($max) { + $this->params['max'] = $max; + return $this; + } + + public function step($step) { + $this->params['step'] = $step; + return $this; + } + + public function unitSetting(Settings\Setting $unitSetting) { + $this->params['unit'] = $unitSetting; + return $this; + } + + /** + * @param string $unit + * @return $this + */ + public function unitText($unit) { + $this->params['unit'] = (string)$unit; + return $this; + } +} \ No newline at end of file diff --git a/customizables/Builders/RadioGroupBuilder.php b/customizables/Builders/RadioGroupBuilder.php new file mode 100644 index 0000000..a9a20ea --- /dev/null +++ b/customizables/Builders/RadioGroupBuilder.php @@ -0,0 +1,32 @@ +params['choiceChildren']) ) { + $this->params['choiceChildren'] = []; + } + $this->params['choiceChildren'][$value] = $childControl; + return $this; + } + + public function build() { + if ( isset($this->params['choiceChildren']) ) { + $this->params['choiceChildren'] = self::buildItems($this->params['choiceChildren'], true); + } + return parent::build(); + } +} \ No newline at end of file diff --git a/customizables/Builders/SectionBuilder.php b/customizables/Builders/SectionBuilder.php new file mode 100644 index 0000000..9b698c9 --- /dev/null +++ b/customizables/Builders/SectionBuilder.php @@ -0,0 +1,17 @@ +children[] = $builder; + return $builder; + } +} \ No newline at end of file diff --git a/customizables/Builders/SettingFactory.php b/customizables/Builders/SettingFactory.php new file mode 100644 index 0000000..53bbc22 --- /dev/null +++ b/customizables/Builders/SettingFactory.php @@ -0,0 +1,357 @@ + + */ + protected $defaults; + + protected $idPrefix; + + /** + * @var bool Whether to enable postMessage for all settings created by this factory. + */ + protected $enablePostMessageForAll = false; + + protected $tagsToApply = array(); + + public function __construct(StorageInterface $store, array $defaults = array(), $idPrefix = '') { + $this->store = $store; + $this->defaults = $defaults; + $this->idPrefix = $idPrefix; + } + + /** + * @param $path + * @param $label + * @param $params + * @return array + */ + protected function prepareParams($path, $label, $params) { + if ( !array_key_exists('default', $params) && array_key_exists($path, $this->defaults) ) { + $params['default'] = $this->defaults[$path]; + } + if ( isset($label) ) { + $params['label'] = $label; + } + if ( $this->enablePostMessageForAll ) { + $params['supportsPostMessage'] = true; + } + if ( !empty($this->tagsToApply) ) { + $params['tags'] = $this->tagsToApply; + } + return $params; + } + + protected function idFrom($path) { + return $this->idPrefix . str_replace('.', '-', $path); + } + + protected function slotFor($path) { + return $this->store->buildSlot($path); + } + + public function enum($path, $enumValues, $label = null, $params = array()) { + return new Settings\EnumSetting( + $this->idFrom($path), + $this->slotFor($path), + $enumValues, + $this->prepareParams($path, $label, $params) + ); + } + + public function stringEnum($path, $enumValues, $label = null, $params = array()) { + return new Settings\StringEnumSetting( + $this->idFrom($path), + $this->slotFor($path), + $enumValues, + $this->prepareParams($path, $label, $params) + ); + } + + public function boolean($path, $label = null, $params = array()) { + return new Settings\BooleanSetting( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + public function url($path, $label = null, $params = array()) { + return new Settings\UrlSetting( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + public function string($path, $label = null, $params = array()) { + return new Settings\StringSetting( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + public function userSanitizedString( + $path, + $mode = Settings\UserSanitizedStringSetting::SANITIZE_STRIP_HTML, + $label = null, + $params = array() + ) { + $params['sanitizationMode'] = $mode; + return new Settings\UserSanitizedStringSetting( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + /** + * Plain text (no tags) for regular users, arbitrary content for users with + * the "unfiltered_html" capability. + * + * HTML entities are allowed in either case. + * + * @param $path + * @param $label + * @param $params + * @return \YahnisElsts\AdminMenuEditor\Customizable\Settings\UserSanitizedStringSetting + */ + public function userText($path, $label = null, $params = array()) { + return $this->userSanitizedString( + $path, + Settings\UserSanitizedStringSetting::SANITIZE_STRIP_HTML, + $label, + $params + ); + } + + public function userHtml($path, $label = null, $params = array()) { + return $this->userSanitizedString( + $path, + Settings\UserSanitizedStringSetting::SANITIZE_POST_HTML, + $label, + $params + ); + } + + public function plainText($path, $label = null, $params = array()) { + return new Settings\PlainTextSetting( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + public function integer($path, $label = null, $params = array()) { + return new Settings\IntegerSetting( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + public function cssLength($path, $label = null, $cssProperty = '', $params = array()) { + return new CssLengthSetting( + $this->idFrom($path), + $this->slotFor($path), + $cssProperty, + $this->prepareParams($path, $label, $params) + ); + } + + public function cssColor($path, $cssProperty, $label = null, $params = array()) { + return new CssColorSetting( + $this->idFrom($path), + $this->slotFor($path), + $cssProperty, + $this->prepareParams($path, $label, $params) + ); + } + + public function image($path, $label = null, $params = array()) { + return new Settings\ImageSetting( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + public function cssBoxShadow($path, $label = null, $params = array()) { + return new BoxShadow( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + public function cssFont($path, $label = null, $params = array()) { + return new Font( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + public function cssSpacing($path, $label = null, $params = array()) { + return new Spacing( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + public function cssEnum($path, $cssProperty, $enumValues, $label = null, $params = array()) { + return new CssEnumSetting( + $this->idFrom($path), + $this->slotFor($path), + $cssProperty, + $enumValues, + $this->prepareParams($path, $label, $params) + ); + } + + public function cssBorders($path, $label = null, $params = array()) { + return new Borders( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + public function cssIndividualBorder($path, $label = null, $params = array()) { + return new IndividualBorder( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, $label, $params) + ); + } + + public function cssBorderStyle($path, $cssProperty = 'border-style', $label = null, $params = array()) { + return new BorderStyle( + $this->idFrom($path), + $this->slotFor($path), + $cssProperty, + $this->prepareParams($path, $label, $params) + ); + } + + /** + * @param string $path + * @param string $dataType + * @param callable $validationCallback + * @param $label + * @param array $params + * @return \YahnisElsts\AdminMenuEditor\Customizable\Settings\UserDefinedSetting + */ + public function custom( + $path, + $dataType, + $validationCallback, + $label = null, + $params = array() + ) { + return new Settings\UserDefinedSetting( + $this->idFrom($path), + $this->slotFor($path), + array_merge( + $this->prepareParams($path, $label, $params), + array( + 'validationCallback' => $validationCallback, + 'type' => $dataType, + ) + ) + ); + } + + /** + * @param string|array $path + * @param callable|null $childGeneratorCallback + * @param array $params + * @return \YahnisElsts\AdminMenuEditor\Customizable\Settings\UserDefinedStruct + */ + public function customStruct( + $path, + $childGeneratorCallback = null, + $params = array() + ) { + if ( isset($childGeneratorCallback) ) { + $params['childGenerator'] = $childGeneratorCallback; + } + + return new Settings\UserDefinedStruct( + $this->idFrom($path), + $this->slotFor($path), + $this->prepareParams($path, '', $params) + ); + } + + /** + * @param class-string<\YahnisElsts\AdminMenuEditor\Customizable\Settings\AbstractSetting> $settingClass + * @param string|array $path + * @param string|null $label + * @param $params + * @param ...$otherConstructorArgs + * @return \YahnisElsts\AdminMenuEditor\Customizable\Settings\AbstractSetting + */ + public function create($settingClass, $path, $label = null, $params = array(), ...$otherConstructorArgs) { + //$params is always the last constructor argument. + $otherConstructorArgs[] = $this->prepareParams($path, $label, $params); + + return new $settingClass( + $this->idFrom($path), + $this->slotFor($path), + ...$otherConstructorArgs + ); + } + + /** + * @return string + */ + public function getIdPrefix() { + return $this->idPrefix; + } + + /** + * Tell the factory to automatically enable postMessage support for all settings + * that it creates. + * + * @return void + */ + public function enablePostMessageSupport() { + $this->enablePostMessageForAll = true; + } + + public function disablePostMessage() { + $this->enablePostMessageForAll = false; + } + + /** + * Tell the factory to add the specified tags to all settings that it creates. + * + * @param string[] $tags + * @return void + */ + public function setTags(...$tags) { + $this->tagsToApply = $tags; + } +} \ No newline at end of file diff --git a/customizables/Builders/StaticHtmlBuilder.php b/customizables/Builders/StaticHtmlBuilder.php new file mode 100644 index 0000000..bef4545 --- /dev/null +++ b/customizables/Builders/StaticHtmlBuilder.php @@ -0,0 +1,26 @@ +html = $html; + } + + /** + * @return \YahnisElsts\AdminMenuEditor\Customizable\Controls\StaticHtml + */ + public function build() { + return new Controls\StaticHtml($this->html); + } +} \ No newline at end of file diff --git a/customizables/Builders/StructChildSettingFactory.php b/customizables/Builders/StructChildSettingFactory.php new file mode 100644 index 0000000..12ea6d2 --- /dev/null +++ b/customizables/Builders/StructChildSettingFactory.php @@ -0,0 +1,29 @@ +getStore(), $defaults); + $this->struct = $parent; + } + + protected function idFrom($path) { + $id = $this->struct->makeChildId(str_replace('.', '-', $path)); + $this->idToChildKey[$id] = $path; + return $id; + } + + public function getChildKeyFromId($id) { + return isset($this->idToChildKey[$id]) ? $this->idToChildKey[$id] : null; + } +} \ No newline at end of file diff --git a/customizables/Builders/TextBoxBuilder.php b/customizables/Builders/TextBoxBuilder.php new file mode 100644 index 0000000..83a26c6 --- /dev/null +++ b/customizables/Builders/TextBoxBuilder.php @@ -0,0 +1,21 @@ +params['inputType'] = $inputTypeAttribute; + return $this; + } + + public function code($isCode = true) { + $this->params['isCode'] = $isCode; + return $this; + } +} \ No newline at end of file diff --git a/customizables/Builders/TextareaBuilder.php b/customizables/Builders/TextareaBuilder.php new file mode 100644 index 0000000..c2f8b14 --- /dev/null +++ b/customizables/Builders/TextareaBuilder.php @@ -0,0 +1,21 @@ +params['rows'] = $rows; + return $this; + } + + public function cols($cols) { + $this->params['cols'] = $cols; + return $this; + } +} \ No newline at end of file diff --git a/customizables/Builders/ToggleCheckBoxBuilder.php b/customizables/Builders/ToggleCheckBoxBuilder.php new file mode 100644 index 0000000..655df71 --- /dev/null +++ b/customizables/Builders/ToggleCheckBoxBuilder.php @@ -0,0 +1,29 @@ +params['onValue'] = $value; + return $this; + } + + /** + * @param scalar $value + * @return $this + */ + public function offValue($value) { + $this->params['offValue'] = $value; + return $this; + } +} \ No newline at end of file diff --git a/customizables/Controls/AbstractNumericControl.php b/customizables/Controls/AbstractNumericControl.php new file mode 100644 index 0000000..b049f28 --- /dev/null +++ b/customizables/Controls/AbstractNumericControl.php @@ -0,0 +1,140 @@ +min = $params['min']; + } else if ( $this->mainSetting instanceof Settings\NumericSetting ) { + $this->min = $this->mainSetting->getMinValue(); + } + if ( array_key_exists('max', $params) ) { + $this->max = $params['max']; + } else if ( $this->mainSetting instanceof Settings\NumericSetting ) { + $this->max = $this->mainSetting->getMaxValue(); + } + + //Step. + if ( array_key_exists('step', $params) ) { + //Step must be a positive number, null, or the special value "any". + if ( is_numeric($params['step']) ) { + $this->step = abs($params['step']); + } else if ( ($params['step'] === null) || ($params['step'] === 'any') ) { + $this->step = $params['step']; + } else { + throw new \InvalidArgumentException("Invalid step value: {$params['step']}"); + } + } + + //Each unit can have a different range. + if ( array_key_exists('rangeByUnit', $params) ) { + $this->rangeByUnit = $params['rangeByUnit']; + } + } + + protected function getSliderRanges() { + $sliderRanges = []; + if ( ($this->min !== null) && ($this->max !== null) ) { + $sliderRanges['_default'] = [ + 'min' => $this->min, + 'max' => $this->max, + 'step' => $this->getDefaultStep(), + ]; + } + return array_merge($sliderRanges, $this->rangeByUnit); + } + + /** + * @return float|int + */ + protected function getDefaultStep() { + if ( is_numeric($this->step) ) { + $step = (float)$this->step; + } else { + if ( $this->mainSetting instanceof Settings\FloatSetting ) { + $step = ($this->max - $this->min) / 100; + } else { + $step = 1; + } + } + return $step; + } + + protected function getBasicInputAttributes() { + $attributes = [ + 'min' => $this->min, + 'max' => $this->max, + 'step' => $this->step, + ]; + + if ( $this->spinButtonsAllowed ) { + $attributes['type'] = 'number'; + } else { + $attributes['type'] = 'text'; + $attributes['inputmode'] = 'numeric'; + $attributes['pattern'] = self::NUMBER_VALIDATION_PATTERN; + $attributes['maxlength'] = 20; + } + return $attributes; + } + + protected function renderUnitDropdown( + Settings\StringEnumSetting $unitSetting, + $elementAttributes = [], + $includeKoBindings = true + ) { + //Display a dropdown list of units. + $units = $unitSetting->generateChoiceOptions(); + $selectedUnit = $unitSetting->getValue(); + + list($optionHtml, $optionBindings) = ChoiceControlOption::generateSelectOptions( + $units, + $selectedUnit, + $unitSetting + ); + + if ( $includeKoBindings ) { + $elementAttributes['data-bind'] = $this->makeKoDataBind(array_merge( + $optionBindings, + ['value' => $this->getKoObservableExpression($selectedUnit, $unitSetting)], + $this->getKoEnableBinding() + )); + } + + echo HtmlHelper::tag('select', $elementAttributes); + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $optionHtml; + echo ''; + } + + protected function getKoComponentParams() { + $params = parent::getKoComponentParams(); + $params['min'] = $this->min; + $params['max'] = $this->max; + $params['step'] = $this->getDefaultStep(); + + $sliderRanges = $this->getSliderRanges(); + if ( !empty($sliderRanges) ) { + $params['sliderRanges'] = $sliderRanges; + } + + return $params; + } +} \ No newline at end of file diff --git a/customizables/Controls/AlignmentSelector.php b/customizables/Controls/AlignmentSelector.php new file mode 100644 index 0000000..31ff897 --- /dev/null +++ b/customizables/Controls/AlignmentSelector.php @@ -0,0 +1,39 @@ + array( + 'description' => 'None', + 'icon' => 'dashicons-editor-justify', + ), + 'left' => array( + 'description' => 'Align left', + 'icon' => 'dashicons-editor-alignleft', + ), + 'center' => array( + 'description' => 'Align center', + 'icon' => 'dashicons-editor-aligncenter', + ), + 'right' => array( + 'description' => 'Align right', + 'icon' => 'dashicons-editor-alignright', + ), + ); + foreach ($this->options as $option) { + if ( isset($choices[$option->value]) ) { + //No label, just an icon and a description in a tooltip. + $option->label = ''; + $option->description = $choices[$option->value]['description']; + $option->icon = $choices[$option->value]['icon']; + } + } + } +} \ No newline at end of file diff --git a/customizables/Controls/CheckBox.php b/customizables/Controls/CheckBox.php new file mode 100644 index 0000000..cab5040 --- /dev/null +++ b/customizables/Controls/CheckBox.php @@ -0,0 +1,57 @@ +hasPrimaryInput = true; + parent::__construct($settings, $params); + } + + public function renderContent(Renderer $renderer) { + //buildInputElement() is safe, and we intentionally allow HTML in the label and description. + //phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped + echo ''; + //phpcs:enable + } + + public function isChecked() { + if ( $this->mainSetting instanceof Setting ) { + return boolval($this->mainSetting->getValue()); + } + return false; + } + + public function includesOwnLabel() { + return true; + } + + protected function getKoComponentParams() { + return array_merge( + parent::getKoComponentParams(), + [ + 'onValue' => true, + 'offValue' => false, + ] + ); + } +} \ No newline at end of file diff --git a/customizables/Controls/ChoiceControl.php b/customizables/Controls/ChoiceControl.php new file mode 100644 index 0000000..ef664b3 --- /dev/null +++ b/customizables/Controls/ChoiceControl.php @@ -0,0 +1,64 @@ + $item) { + if ( is_string($item) ) { + //List of [value => label] pairs. + $this->options[] = new ChoiceControlOption($key, $item); + } else if ( is_array($item) ) { + //List of arrays where each item is [value => X, label => Y, ...]. + //Alternatively, [value => [label => Y, ...]] pairs. + if ( !array_key_exists('value', $item) ) { + $item['value'] = $key; + } + $this->options[$key] = ChoiceControlOption::fromArray($item); + } else if ( $item instanceof ChoiceControlOption ) { + //List of nicely predefined option objects. + $this->options[] = $item; + } else { + throw new \InvalidArgumentException("Invalid option: $item"); + } + } + } else if ( $this->mainSetting instanceof EnumSetting ) { + $this->options = $this->mainSetting->generateChoiceOptions(); + } + } + + protected function getKoComponentParams() { + $params = parent::getKoComponentParams(); + $params['options'] = array_map( + function ($option) { + return $option->serializeForJs(); + }, + $this->options + ); + return $params; + } +} \ No newline at end of file diff --git a/customizables/Controls/ChoiceControlOption.php b/customizables/Controls/ChoiceControlOption.php new file mode 100644 index 0000000..4ca856c --- /dev/null +++ b/customizables/Controls/ChoiceControlOption.php @@ -0,0 +1,107 @@ +value = $value; + $this->label = ($label !== null) ? $label : $value; + if ( isset($params['description']) ) { + $this->description = $params['description']; + } + if ( array_key_exists('enabled', $params) ) { + $this->enabled = (bool)($params['enabled']); + } + if ( isset($params['icon']) ) { + $this->icon = $params['icon']; + } + } + + public function serializeForJs() { + $result = [ + 'value' => $this->value, + 'label' => $this->label, + ]; + if ( $this->description !== '' ) { + $result['description'] = $this->description; + } + if ( !$this->enabled ) { + $result['enabled'] = false; + } + if ( $this->icon !== null ) { + $result['icon'] = $this->icon; + } + return $result; + } + + public static function fromArray($array) { + return new static( + array_key_exists('value', $array) ? $array['value'] : null, + array_key_exists('label', $array) ? $array['label'] : null, + $array + ); + } + + /** + * @param ChoiceControlOption[] $options + * @param mixed $selectedValue + * @param Setting $setting + * @return array + */ + public static function generateSelectOptions($options, $selectedValue, Setting $setting) { + $htmlLines = []; + + foreach ($options as $option) { + $htmlLines[] = HtmlHelper::tag( + 'option', + [ + 'value' => $setting->encodeForForm($option->value), + 'selected' => ($selectedValue === $option->value), + 'disabled' => !$option->enabled, + + ], + $option->label + ); + } + + $koOptionData = self::generateKoOptions($options); + $optionBindings = array_map('wp_json_encode', $koOptionData); + + return [implode("\n", $htmlLines), $optionBindings]; + } + + /** + * @param ChoiceControlOption[] $choiceOptions + * @return array{options: array, optionsText: string, optionsValue: string} + */ + public static function generateKoOptions($choiceOptions) { + $koOptions = []; + foreach ($choiceOptions as $option) { + $koOptions[] = [ + 'value' => $option->value, + 'label' => $option->label, + 'disabled' => !$option->enabled, + ]; + } + + return [ + 'options' => $koOptions, + 'optionsText' => 'label', + 'optionsValue' => 'value', + ]; + } +} \ No newline at end of file diff --git a/customizables/Controls/ClassicControl.php b/customizables/Controls/ClassicControl.php new file mode 100644 index 0000000..940e83f --- /dev/null +++ b/customizables/Controls/ClassicControl.php @@ -0,0 +1,91 @@ +getDescription(); + if ( !empty($description) ) { + //HTML is intentionally allowed. The description should never contain user input. + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo "\n", '

', $description, '

'; + } + } + + /** + * Output description inside the control. This is primarily intended + * for controls that are wrapped in a label element. + */ + protected function outputNestedDescription() { + $description = $this->getDescription(); + if ( !empty($description) ) { + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo self::formatNestedDescription($description); + } + } + + /** + * Generate HTML for a description that is nested inside the control. + * + * @param string $content The description. HTML is allowed. + * @return string HTML code + */ + protected static function formatNestedDescription($content) { + return "\n" . '
' . $content . ''; + } + + protected static function enqueueDependencies() { + static $done = false; + if ( $done ) { + return; + } + $done = true; + + //Use the Pro version control stylesheet if it exists. + $proStylesheet = AME_ROOT_DIR . '/extras/pro-customizables/assets/controls.css'; + $proStylesheetExists = file_exists($proStylesheet); + $isProbablyPro = $proStylesheetExists; + if ( $proStylesheetExists ) { + $stylesheetUrl = plugins_url('controls.css', $proStylesheet); + } else { + $stylesheetUrl = plugins_url('assets/controls.css', AME_CUSTOMIZABLE_BASE_FILE); + } + + wp_enqueue_auto_versioned_style( + 'ame-combined-control-styles', + $stylesheetUrl, + ['wp-color-picker'] + ); + + $controlDependencies = ['jquery', 'wp-color-picker']; + if ( $isProbablyPro ) { + $controlDependencies[] = 'ame-ko-extensions'; + } + wp_enqueue_auto_versioned_script( + 'ame-combined-control-scripts', + plugins_url('assets/combined-controls.js', AME_CUSTOMIZABLE_BASE_FILE), + $controlDependencies + ); + + //Also enqueue the Pro version's combined controls if the file exists. + $proCombinedControls = AME_ROOT_DIR . '/extras/pro-customizables/assets/combined-pro-controls.js'; + if ( file_exists($proCombinedControls) ) { + wp_enqueue_auto_versioned_script( + 'ame-combined-pro-control-scripts', + plugins_url('combined-pro-controls.js', $proCombinedControls), + $controlDependencies + ); + } + } + + public function enqueueKoComponentDependencies() { + parent::enqueueKoComponentDependencies(); + + //Due to late static binding, this should properly call the method on + //the class that the current instance belongs to. + static::enqueueDependencies(); + } +} \ No newline at end of file diff --git a/customizables/Controls/CodeEditor.php b/customizables/Controls/CodeEditor.php new file mode 100644 index 0000000..2160557 --- /dev/null +++ b/customizables/Controls/CodeEditor.php @@ -0,0 +1,155 @@ +mimeType = $params['mimeType']; + } + } + + public function renderContent(Renderer $renderer) { + $id = '_acm_' . $this->id; + $this->editorId = $id; + + $stringValue = $this->getMainSettingValue(''); + if ( $stringValue === null ) { + $stringValue = ''; + } + + echo '
'; + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- buildInputElement() is safe. + echo $this->buildInputElement( + [ + 'id' => $id, + 'class' => 'large-text', + 'cols' => 100, + 'rows' => 5, + ], + 'textarea', + esc_textarea($stringValue) + ); + $this->outputSiblingDescription(); + echo '
'; + + if ( $this->enqueueCodeEditor() ) { + static::enqueueDependencies(); + if ( did_action(self::SCRIPT_ACTION) ) { + $this->outputInitScript(); + } else { + add_action(self::SCRIPT_ACTION, [$this, 'outputInitScript']); + } + } + } + + protected function enqueueCodeEditor() { + //Don't enqueue the editor more than once. + if ( $this->triedToEnqueueEditor ) { + return !empty($this->editorSettings); + } + $this->triedToEnqueueEditor = true; + + $additionalCodeMirrorOptions = []; + //Strangely, WordPress disables linting for CSS by default even though + //it is actually supported. Let's enable it explicitly. This needs to be + //done *before* calling wp_enqueue_code_editor() because that function + //uses the "lint" option to decide whether to enqueue the linter(s). + if ($this->mimeType === 'text/css') { + $additionalCodeMirrorOptions['lint'] = true; + } + + //This can return false if, for example, the user has disabled syntax highlighting. + $this->editorSettings = wp_enqueue_code_editor([ + 'type' => $this->mimeType, + 'codemirror' => $additionalCodeMirrorOptions, + ]); + if ( empty($this->editorSettings) ) { + return false; + } + + //Enable linting and a few other things for CSS and JavaScript. + if ( + isset($this->editorSettings['codemirror']) + && in_array( + $this->mimeType, + ['text/css', 'text/javascript', 'application/javascript'] + ) + ) { + $this->editorSettings['codemirror'] = array_merge( + $this->editorSettings['codemirror'], + [ + 'lint' => true, + 'autoCloseBrackets' => true, + 'matchBrackets' => true, + ] + ); + + if ( empty($this->editorSettings['codemirror']['gutters']) ) { + $this->editorSettings['codemirror']['gutters'][] = 'CodeMirror-lint-markers'; + } + + //For CSS in particular, setting mode to "css" appears to enable + //some additional linting, like warnings for unknown CSS properties. + if ( $this->mimeType === 'text/css' ) { + $this->editorSettings['codemirror']['mode'] = 'css'; + } + } + + return true; + } + + public function outputInitScript() { + if ( !$this->editorId || empty($this->editorSettings) ) { + //Code editor was not enqueued for some reason. + return; + } + ?> + + enqueueCodeEditor(); + } + + protected function getKoComponentParams() { + if ( !$this->triedToEnqueueEditor ) { + $this->enqueueCodeEditor(); + } + + $params = parent::getKoComponentParams(); + $params['mimeType'] = $this->mimeType; + $params['editorSettings'] = $this->editorSettings; + return $params; + } +} \ No newline at end of file diff --git a/customizables/Controls/ColorPicker.php b/customizables/Controls/ColorPicker.php new file mode 100644 index 0000000..2043ba8 --- /dev/null +++ b/customizables/Controls/ColorPicker.php @@ -0,0 +1,57 @@ +getMainSettingValue(); + if ( !is_string($value) ) { + $value = ''; + } + $settingId = ($this->mainSetting instanceof Setting) ? $this->mainSetting->getId() : null; + + //phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- buildInputElement() is safe + echo $this->buildInputElement([ + 'type' => 'text', + 'class' => array_merge( + ['ame-color-picker', 'ame-customizable-color-picker'], + $this->classes + ), + 'value' => $value, + 'style' => 'visibility: hidden', + 'data-ame-setting-id' => $settingId, + 'data-bind' => 'ameObservableChangeEvents: ' . $this->getKoObservableExpression($value), + ]); + //phpcs:enable + + static::enqueueDependencies(); + } + + protected static function enqueueDependencies() { + static $done = false; + if ( $done ) { + return; + } + $done = true; + + parent::enqueueDependencies(); + + wp_enqueue_script('wp-color-picker'); + } + + public function supportsLabelAssociation() { + //This currently doesn't work with the WordPress color picker. + return false; + } +} \ No newline at end of file diff --git a/customizables/Controls/Container.php b/customizables/Controls/Container.php new file mode 100644 index 0000000..1e446e0 --- /dev/null +++ b/customizables/Controls/Container.php @@ -0,0 +1,159 @@ +title = $title; + foreach ($children as $child) { + $this->add($child); + } + + if ( isset($params['tooltip']) ) { + $this->tooltip = $params['tooltip']; + } + } + + /** + * @param UiElement $child + * @return void + */ + public function add($child) { + $this->children[] = $child; + } + + /** + * @return UiElement[] + */ + public function getChildren() { + return $this->children; + } + + /** + * Sort the container's children using the specified comparison function. + * + * @param callable(UiElement,UiElement):int $compareFunction + * @return void + */ + public function sortChildren($compareFunction) { + usort($this->children, $compareFunction); + } + + /** + * @return string + */ + public function getTitle() { + return $this->title; + } + + /** + * @param string $newTitle + */ + public function setTitle($newTitle) { + $this->title = $newTitle; + } + + public function hasTitle() { + return !empty($this->title); + } + + /** + * @return \YahnisElsts\AdminMenuEditor\Customizable\Controls\Tooltip|null + */ + public function getTooltip() { + return $this->tooltip; + } + + /** + * @return bool + */ + public function hasTooltip() { + return ($this->tooltip !== null); + } + + /** + * Recursively search the container for a UI element that has the specified ID. + * + * @param string $id + * @return UiElement|null + */ + public function findChildById($id) { + foreach ($this->children as $child) { + if ( $child->getId() === $id ) { + return $child; + } else if ( $child instanceof Container ) { + $result = $child->findChildById($id); + if ( $result !== null ) { + return $result; + } + } + } + return null; + } + + public function isEmpty() { + return empty($this->children); + } + + public function hasChildren() { + return !empty($this->children); + } + + public function serializeForJs() { + $result = parent::serializeForJs(); + if ( $this->hasTitle() ) { + $result['title'] = $this->title; + } + + if ( !empty($this->children) ) { + $result['children'] = []; + foreach ($this->children as $child) { + $result['children'][] = $child->serializeForJs(); + } + } + return $result; + } + + public function enqueueKoComponentDependencies() { + parent::enqueueKoComponentDependencies(); + foreach ($this->children as $child) { + $child->enqueueKoComponentDependencies(); + } + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function getIterator() { + return new \ArrayIterator($this->children); + } + + /** + * Recursively get all descendants of this container. + * + * @return \Generator + */ + public function getAllDescendants() { + foreach ($this->children as $child) { + yield $child; + if ( $child instanceof ControlContainer ) { + yield from $child->getAllDescendants(); + } + } + } +} \ No newline at end of file diff --git a/customizables/Controls/ContentToggle.php b/customizables/Controls/ContentToggle.php new file mode 100644 index 0000000..eccc08b --- /dev/null +++ b/customizables/Controls/ContentToggle.php @@ -0,0 +1,57 @@ +itemSelector = $params['itemSelector']; + } else { + throw new \InvalidArgumentException('The "itemSelector" setting is required for ContentToggle controls.'); + } + if ( isset($params['toggleParentSelector']) ) { + $this->toggleParentSelector = $params['toggleParentSelector']; + } + if ( isset($params['hiddenByDefault']) ) { + $this->hiddenByDefault = $params['hiddenByDefault']; + } + if ( isset($params['visibleStateText']) ) { + $this->visibleStateText = $params['visibleStateText']; + } else if ( isset($params['label']) ) { + $this->visibleStateText = $params['label']; + } + if ( isset($params['hiddenStateText']) ) { + $this->hiddenStateText = $params['hiddenStateText']; + } + } + + public function renderContent(Renderer $renderer) { + echo HtmlHelper::tag( + 'a', + array( + 'href' => '#toggle', + 'class' => 'ame-content-toggle-control', + 'data-item-selector' => $this->itemSelector, + 'data-parent-selector' => $this->toggleParentSelector, + 'data-default-state' => $this->hiddenByDefault ? 'hidden' : null, + 'data-visible-state-text' => $this->visibleStateText, + 'data-hidden-state-text' => $this->hiddenStateText, + ), + esc_html($this->hiddenByDefault ? $this->hiddenStateText : $this->visibleStateText) + ); + + self::enqueueDependencies(); + } +} \ No newline at end of file diff --git a/customizables/Controls/Control.php b/customizables/Controls/Control.php new file mode 100644 index 0000000..de2533c --- /dev/null +++ b/customizables/Controls/Control.php @@ -0,0 +1,402 @@ +instanceNumber = ++static::$totalInstances; + + parent::__construct($params); + + $this->settings = $settings; + if ( !empty($this->settings) ) { + if ( isset($params['mainSetting']) ) { + $this->mainSetting = $this->settings[$params['mainSetting']]; + } else { + $this->mainSetting = reset($this->settings); + } + } + + //Label + if ( isset($params['label']) ) { + $this->label = $params['label']; + } else if ( !empty($this->mainSetting) ) { + $this->label = $this->mainSetting->getLabel(); + } + //Description + if ( isset($params['description']) ) { + $this->description = $params['description']; + } else if ( !empty($this->mainSetting) ) { + $this->description = $this->mainSetting->getDescription(); + } + //Enabled/disabled + $this->parseEnabledParam($params); + + //ID. Note that this is not necessarily the same as the ID attribute + //of the HTML element generated by this control. + if ( $this->id === '' ) { + if ( isset($this->mainSetting) ) { + $this->id = preg_replace('/[^a-z\-_]+/i', '_', $this->mainSetting->getId()); + } else { + $this->id = 'ame_' . $this->type . '_' . $this->instanceNumber; + } + } + + //Input classes + if ( isset($params['inputClasses']) ) { + $this->inputClasses = (array)$params['inputClasses']; + } + + //Input attributes + if ( isset($params['inputAttributes']) ) { + $this->inputAttributes = (array)$params['inputAttributes']; + } + } + + abstract public function renderContent(Renderer $renderer); + + /** + * @return string + */ + public function getLabel() { + return $this->label; + } + + public function getLabelTargetId() { + return $this->getPrimaryInputId(); + } + + /** + * Does this control have an input or other form-related element that + * could be meaningfully associated with the control's label? + * + * @return bool + */ + public function supportsLabelAssociation() { + return $this->hasPrimaryInput; + } + + /** + * Whether this control renders its own label as part of its content. + * + * For example, a checkbox control will usually wrap a label element around + * its input element, so it would return true here. + * + * @return boolean + */ + public function includesOwnLabel() { + return false; + } + + public function parentLabelEnabled() { + return $this->supportsLabelAssociation(); + } + + /** + * @return \YahnisElsts\AdminMenuEditor\Customizable\Settings\AbstractSetting[] + */ + public function getSettings() { + return $this->settings; + } + + /** + * @return string + */ + public function getType() { + return $this->type; + } + + /** + * @return bool + */ + public function isEnabled() { + $enabled = $this->traitIsEnabled(); + if ( !empty($this->mainSetting) ) { + $enabled = $enabled && $this->mainSetting->isEditableByUser(); + } + return $enabled; + } + + /** + * @return string + */ + public function getPrimaryInputId() { + if ( !$this->hasPrimaryInput ) { + return ''; + } + return '_ame-cntrl-' . $this->id; + } + + /** + * Generate an HTML tag based on the input settings of this control. + * + * This is mainly intended for creating input tags, but it also works for textarea tags. + * + * @param array $attributes + * @param string $tagName + * @param string|null $content + * @return string + */ + protected function buildInputElement( + $attributes = [], + $tagName = 'input', + $content = null + ) { + //Merge input classes and any classes specified in the $attributes array. + $classes = $this->inputClasses; + if ( isset($attributes['class']) ) { + $classes = array_merge($classes, (array)$attributes['class']); + unset($attributes['class']); + } + + $attributes = array_merge( + [ + 'id' => $this->hasPrimaryInput ? $this->getPrimaryInputId() : null, + 'class' => $classes, + 'name' => $this->getFieldName(), + 'disabled' => !$this->isEnabled(), + //Add the setting ID for the admin customizer module. + 'data-ac-setting-id' => $this->getAutoAcSettingId(), + ], + $attributes, + $this->inputAttributes + ); + + return $this->buildTag($tagName, $attributes, $content); + } + + protected function getMainSettingValue($default = null) { + if ( $this->mainSetting instanceof Setting ) { + return $this->mainSetting->getValue($default); + } + return $default; + } + + /** + * Generate the "name" attribute value for a form element associated with + * this control. + * + * @param string|null $compositeChildKey Only for composite settings: the key of the child setting. + * @param Settings\AbstractSetting|null $setting The setting shown in the field. Defaults to the main setting. + * @return string + */ + protected function getFieldName($compositeChildKey = null, $setting = null) { + if ( $setting === null ) { + $setting = $this->mainSetting; + } + if ( !($setting instanceof Settings\AbstractSetting) ) { + return $this->id; + } + + if ( ($compositeChildKey === null) || ($compositeChildKey === '') ) { + $id = $setting->getId(); + } else { + $id = $setting->getId() . '.' . $compositeChildKey; + if ( $setting instanceof Settings\CompositeSetting ) { + $child = $setting->getChild($compositeChildKey); + if ( $child ) { + $id = $child->getId(); + } + } + } + + //Convert dot notation to array notation. + $parts = explode('.', $id); + $root = array_shift($parts); + if ( empty($parts) ) { + return $root; + } + return $root . '[' . implode('][', $parts) . ']'; + } + + public function getAutoGroupTitle() { + if ( $this->mainSetting instanceof Settings\AbstractSetting ) { + $customTitle = $this->mainSetting->getCustomGroupTitle(); + if ( !empty($customTitle) ) { + return $customTitle; + } + } + return $this->getLabel(); + } + + /** + * Get a version of this control that is compatible with the Admin Customizer module. + * + * May return this object if it is already compatible or if there is + * no better alternative. + * + * @return Control + */ + public function getAdminCustomizerControl() { + return $this; + } + + protected function getAutoAcSettingId() { + if ( ($this->mainSetting instanceof Setting) && $this->hasPrimaryInput ) { + return $this->mainSetting->getId(); + } + return null; + } + + protected function getKoObservableExpression($defaultValue, AbstractSetting $setting = null) { + if ( $setting === null ) { + $setting = $this->mainSetting; + } + if ( !($setting instanceof AbstractSetting) ) { + return wp_json_encode($defaultValue); + } + + $settingId = $setting->getId(); + return sprintf( + '$root.getSettingObservable(%s, %s)', + wp_json_encode($settingId), + wp_json_encode($defaultValue) + ); + } + + protected function makeKoDataBind($bindingValues) { + if ( empty($bindingValues) ) { + return ''; + } + + $bindings = []; + foreach ($bindingValues as $binding => $value) { + $bindings[] = sprintf('%s: %s', $binding, $value); + } + return implode(', ', $bindings); + } + + protected function getJsUiElementType() { + return 'control'; + } + + public function serializeForJs() { + $result = parent::serializeForJs(); + + $label = $this->getLabel(); + if ( !empty($label) ) { + $result['label'] = $label; + } + + if ( $this->koComponentName === null ) { + return $result; + } + $result['component'] = $this->koComponentName; + + //Map setting names to IDs. + $settingIds = []; + $settings = $this->settings; + //The main setting is always available as "value". This means that a setting + //could be duplicated in the "settings" array, but that should be fine. + if ( $this->mainSetting instanceof AbstractSetting ) { + $settings['value'] = $this->mainSetting; + } + foreach ($settings as $name => $setting) { + //Skip non-string keys. Controls that care about accessing multiple + //distinct settings should have custom keys. + if ( !is_string($name) ) { + continue; + } + + //Recurse into structs and add all child settings separately as "name.child". + //However, don't split up composite settings. + if ( + ($setting instanceof Settings\AbstractStructSetting) + && !($setting instanceof Settings\CompositeSetting) + ) { + foreach ( + AbstractSetting::recursivelyIterateSettings($setting, false, $name) + as $key => $childSetting + ) { + $settingIds[$key] = $childSetting->getId(); + } + } else if ( $setting instanceof AbstractSetting ) { + $settingIds[$name] = $setting->getId(); + } + } + $result['settings'] = $settingIds; + + //Input classes. + if ( !empty($this->inputClasses) ) { + $result['inputClasses'] = $this->inputClasses; + } + + //Input attributes. + if ( !empty($this->inputAttributes) ) { + $result['inputAttributes'] = $this->inputAttributes; + } + + //Primary input ID. + if ( $this->hasPrimaryInput ) { + $result['primaryInputId'] = $this->getPrimaryInputId(); + } + + //Enabled state or condition. + $result['enabled'] = $this->serializeConditionForJs(); + + //Label association settings for components that display this control as a child. + $includesOwnLabel = $this->includesOwnLabel(); + if ( $includesOwnLabel ) { + $result['includesOwnLabel'] = true; + } + $labelTargetId = $this->getLabelTargetId(); + if ($this->supportsLabelAssociation() && !empty($labelTargetId) ) { + $result['labelTargetId'] = $labelTargetId; + } + + return $result; + } +} \ No newline at end of file diff --git a/customizables/Controls/ControlContainer.php b/customizables/Controls/ControlContainer.php new file mode 100644 index 0000000..66ff910 --- /dev/null +++ b/customizables/Controls/ControlContainer.php @@ -0,0 +1,26 @@ + + */ + public function getChildren(); + + /** + * Recursively get all descendants of this container. + * + * @return iterable + */ + public function getAllDescendants(); +} \ No newline at end of file diff --git a/customizables/Controls/ControlGroup.php b/customizables/Controls/ControlGroup.php new file mode 100644 index 0000000..f88e6d2 --- /dev/null +++ b/customizables/Controls/ControlGroup.php @@ -0,0 +1,142 @@ +isStacked = boolval($params['stacked']); + } + if ( array_key_exists('fieldset', $params) ) { + $this->wantsFieldset = $params['fieldset']; + } + if ( isset($params['fullWidth']) ) { + $this->isFullWidth = boolval($params['fullWidth']); + } + $this->parseEnabledParam($params); + } + + public function add($child) { + if ( $child instanceof Section ) { + throw new \InvalidArgumentException('Control groups cannot contain sections.'); + } + + parent::add($child); + } + + /** + * @return string|null + */ + public function getLabelFor() { + if ( $this->labelFor === null ) { + //Automatically choose a target for the label only if there is exactly + //one control that wants a label, and it is the first child. + $target = null; + $childNumber = 0; + + foreach ($this->children as $child) { + $childNumber++; + if ( ($child instanceof Control) && $child->parentLabelEnabled() ) { + if ( $childNumber === 1 ) { + $target = $child; + } else { + //Either this is not the first child, or more than one + //child wants a label. + $target = null; + break; + } + } + } + + if ( $target instanceof Control ) { + $this->labelFor = $target->getPrimaryInputId(); + } else { + $this->labelFor = false; + } + } + + return $this->labelFor ?: null; + } + + public function isStacked() { + return $this->isStacked; + } + + public function wantsFieldset() { + return $this->wantsFieldset; + } + + /** + * @return bool + */ + public function isFullWidth() { + return $this->isFullWidth; + } + + protected function getJsUiElementType() { + return 'control-group'; + } + + public function serializeForJs() { + $result = parent::serializeForJs(); + $labelFor = $this->getLabelFor(); + if ( $labelFor !== null ) { + $result['labelFor'] = $labelFor; + } + return $result; + } + + + protected function getKoComponentParams() { + $params = parent::getKoComponentParams(); + $params['enabled'] = $this->serializeConditionForJs(); + + //The "full width" flag is not directly relevant to group components because they + //are usually already full width, but it is sometimes used to disable the title + //(e.g. for groups that contain only a single checkbox). + if ( $this->isFullWidth() ) { + $params['isFullWidth'] = true; + $params['titleDisabled'] = true; + } + + return $params; + } +} \ No newline at end of file diff --git a/customizables/Controls/ImageSelector.php b/customizables/Controls/ImageSelector.php new file mode 100644 index 0000000..69ea8d9 --- /dev/null +++ b/customizables/Controls/ImageSelector.php @@ -0,0 +1,164 @@ +mainSetting->getChildValue('attachmentId', 0); + if ( !empty($attachmentId) ) { + //Verify that the attachment exists. + $attachmentUrl = wp_get_attachment_image_url($attachmentId, 'full'); + if ( !$attachmentUrl ) { + $attachmentId = 0; + } + } + + $externalUrlsAllowed = $this->mainSetting->areExternalUrlsAllowed(); + $externalUrl = $this->mainSetting->getChildValue('externalUrl', ''); + + $imageUrl = $this->mainSetting->getImageUrl(); + + $canSelectAttachment = $this->isEnabled() && current_user_can('upload_files'); + $canSelectExternalUrl = $this->isEnabled() && $externalUrlsAllowed; + + echo HtmlHelper::tag('div', [ + 'class' => 'ame-image-selector-v2', + 'data-bind' => 'ameObservableChangeEvents: ' . $this->getKoObservableExpression($imageUrl), + ]); + ?> + +

Sorry, this feature is not available in the demo because image upload is disabled.

+ +
%s', + empty($imageUrl) ? '' : 'display: none;', + esc_attr($noImageText), + esc_attr('Loading...'), + esc_html($noImageText) + ); + if ( !empty($imageUrl) ) { + /** @noinspection HtmlUnknownTarget */ + printf('Image preview', esc_attr($imageUrl)); + } + ?>
+ 'hidden', + 'name' => $this->getFieldName('attachmentId'), + 'value' => $attachmentId, + 'class' => 'ame-image-attachment-id', + )); + echo HtmlHelper::tag('input', array( + 'type' => 'hidden', + 'name' => $this->getFieldName('attachmentSiteId'), + 'value' => $this->mainSetting['attachmentSiteId']->getValue(0), + 'class' => 'ame-image-attachment-site-id', + )); + //Attachment URL will usually be overwritten on the server side, but it's useful + //for screens that don't trigger server-side post-processing (e.g. the "Style" dialog). + echo HtmlHelper::tag('input', array( + 'type' => 'hidden', + 'name' => $this->getFieldName('attachmentUrl'), + 'value' => $this->mainSetting['attachmentUrl']->getValue(''), + 'class' => 'ame-image-attachment-url', + )); + ?> + + + 'hidden', + 'name' => $this->getFieldName($dimension), + 'value' => $this->mainSetting->getChildValue($dimension, ''), + 'class' => 'ame-detected-image-' . $dimension, + )); + } + ?> + + + + outputSiblingDescription(); ?> + '; //Close the container div. + } + + protected static function enqueueDependencies() { + static $done = false; + if ( $done ) { + return; + } + $done = true; + + parent::enqueueDependencies(); + + wp_enqueue_media(); + + wp_enqueue_auto_versioned_script( + 'ame-image-selector-control-v2', + plugins_url('assets/image-selector.js', AME_CUSTOMIZABLE_BASE_FILE), + array('jquery', 'ame-lodash') + ); + + wp_add_inline_script( + 'ame-image-selector-control-v2', + sprintf("var AmeIscBlogId = %d;", get_current_blog_id()) + ); + } + + protected function getKoComponentParams() { + $params = parent::getKoComponentParams(); + $params['externalUrlsAllowed'] = $this->mainSetting->areExternalUrlsAllowed(); + $params['canSelectMedia'] = current_user_can('upload_files'); + return $params; + } +} \ No newline at end of file diff --git a/customizables/Controls/InterfaceStructure.php b/customizables/Controls/InterfaceStructure.php new file mode 100644 index 0000000..4bbe845 --- /dev/null +++ b/customizables/Controls/InterfaceStructure.php @@ -0,0 +1,34 @@ +children as $child) { + if ( $child instanceof Section ) { + $sections[] = $child; + $currentAnonymousSection = null; + } else { + //Put all non-section elements into an anonymous section. + if ( $currentAnonymousSection === null ) { + $currentAnonymousSection = new Section(''); + $sections[] = $currentAnonymousSection; + } + $currentAnonymousSection->add($child); + } + } + return $sections; + } + + protected function getJsUiElementType() { + return 'structure'; + } +} \ No newline at end of file diff --git a/customizables/Controls/NumberInput.php b/customizables/Controls/NumberInput.php new file mode 100644 index 0000000..82e843d --- /dev/null +++ b/customizables/Controls/NumberInput.php @@ -0,0 +1,227 @@ +hasPrimaryInput = true; + parent::__construct($settings, $params); + + //Units can be specified in a number of ways. + if ( isset($settings['unit']) ) { + $this->unitSetting = $settings['unit']; + } + + if ( !$this->unitSetting ) { + if ( array_key_exists('unit', $params) ) { + if ( is_string($params['unit']) ) { + $this->fixedUnit = $params['unit']; + } else if ( $params['unit'] instanceof Setting ) { + $this->unitSetting = $params['unit']; + } + } else if ( $this->mainSetting instanceof CssLengthSetting ) { + $unitSetting = $this->mainSetting->getUnitSetting(); + if ( $unitSetting instanceof Setting ) { + $this->unitSetting = $unitSetting; + } else { + $this->fixedUnit = $this->mainSetting->getUnit(); + } + } + } + + //Add the "ame-small-number-input" class to controls where the expected + //number of digits is 4 or less. This only applies if both the min and + //max values are known. + if ( is_numeric($this->min) && is_numeric($this->max) ) { + $digits = 1; + //Digits before the decimal point = greatest log10 of abs(min) and abs(max). + //Add 1 because log10 is one less than the number of digits (e.g. log10(1) = 0). + //Note the use of loose comparison to avoid "0 !== 0.0" issues. + if ( ($this->min != 0) ) { + $digits = max($digits, floor(log10(abs($this->min))) + 1); + } + if ( ($this->max != 0) ) { + $digits = max($digits, floor(log10(abs($this->max))) + 1); + } + + //Add the digits after the decimal point if the step is a decimal number. + $defaultStep = $this->getDefaultStep(); + $fraction = abs($defaultStep - floor($defaultStep)); + if ( ($fraction != 0) ) { + $digits += floor(abs(log10(abs($fraction)))); + } + + if ( ($digits <= 4) && !in_array('ame-small-number-input', $this->inputClasses) ) { + $this->inputClasses[] = 'ame-small-number-input'; + } + } + } + + public function renderContent(Renderer $renderer) { + $hasUnitDropdown = $this->unitSetting instanceof Settings\EnumSetting; + + $currentUnitValue = $this->getCurrentUnit(); + if ( $hasUnitDropdown || !empty($currentUnitValue) ) { + $unitElementId = $this->getUnitElementId(); + } else { + $unitElementId = null; + } + + $value = $this->getMainSettingValue(); + + $sliderRanges = $this->getSliderRanges(); + + $wrapperClasses = ['ame-number-input-control']; + if ( !empty($sliderRanges) ) { + $wrapperClasses[] = 'ame-container-with-popup-slider'; + } + $wrapperClasses = array_merge($wrapperClasses, $this->classes); + + echo HtmlHelper::tag( + 'fieldset', + [ + 'class' => $wrapperClasses, + 'data-bind' => $this->makeKoDataBind($this->getKoEnableBinding()), + ] + ); + if ( $hasUnitDropdown ) { + echo '
'; + } + + $attributes = $this->getBasicInputAttributes(); + $attributes['value'] = $value; + + $inputClasses = []; + if ( !empty($sliderRanges) ) { + $inputClasses[] = 'ame-input-with-popup-slider'; + } + $inputClasses[] = 'ame-number-input'; + //buildInputElement() will add $this->inputClasses, so no need to do it here. + $attributes['class'] = implode(' ', $inputClasses); + + if ( !empty($unitElementId) ) { + $attributes['data-unit-element-id'] = $unitElementId; + } + if ( !empty($sliderRanges) ) { + $attributes['data-slider-ranges'] = wp_json_encode($sliderRanges); + } + + $attributes['data-bind'] = $this->makeKoDataBind(array_merge([ + 'value' => $this->getKoObservableExpression($value), + 'ameObservableChangeEvents' => 'true', + ], $this->getKoEnableBinding())); + + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- buildInputElement() is safe + echo $this->buildInputElement($attributes); + + if ( $hasUnitDropdown && ($this->unitSetting instanceof Settings\EnumSetting) ) { + $this->renderUnitDropdown($this->unitSetting, [ + 'name' => $this->getFieldName(null, $this->unitSetting), + 'id' => $unitElementId, + 'class' => 'ame-input-group-secondary ame-number-input-unit', + 'data-ac-setting-id' => $this->unitSetting->getId(), + ]); + } else { + $unit = $this->getCurrentUnit(); + if ( !empty($unit) ) { + echo HtmlHelper::tag( + 'span', + [ + 'id' => $unitElementId, + 'class' => 'ame-number-input-unit', + 'data-number-unit' => $unit, + ], + ' ' . esc_html($unit) + ); + } + } + + if ( $hasUnitDropdown ) { + echo '
'; + } + + //Slider + if ( !empty($sliderRanges) ) { + PopupSlider::basic()->render(); + } + + echo ''; + + static::enqueueDependencies(); + } + + protected function getCurrentUnit() { + if ( $this->unitSetting instanceof Setting ) { + return $this->unitSetting->getValue(); + } + return $this->fixedUnit; + } + + protected function getUnitElementId() { + return $this->getPrimaryInputId() . '__unit'; + } + + protected function getKoComponentParams() { + $params = parent::getKoComponentParams(); + + $unitText = $this->getCurrentUnit(); + $hasUnitDropdown = false; + + if ( $this->unitSetting instanceof Settings\EnumSetting ) { + $hasUnitDropdown = true; + $params['hasUnitDropdown'] = true; + $params['unitDropdownOptions'] = ChoiceControlOption::generateKoOptions( + $this->unitSetting->generateChoiceOptions() + ); + } else if ( !empty($unitText) ) { + $params['unitText'] = $unitText; + } + + if ( $hasUnitDropdown || !empty($unitText) ) { + $params['unitElementId'] = $this->getUnitElementId(); + } + + return $params; + } + + public function serializeForJs() { + $result = parent::serializeForJs(); + + if ( $this->unitSetting instanceof Settings\EnumSetting ) { + if ( empty($result['settings']) ) { + $result['settings'] = []; + } + $result['settings']['unit'] = $this->unitSetting->getId(); + } + + return $result; + } + + public function enqueueKoComponentDependencies() { + parent::enqueueKoComponentDependencies(); + + //The slider automatically enqueues its dependencies when it's rendered + //via PopupSlider::render(), but KO components don't use that method. + //We need to enqueue the dependencies explicitly. + PopupSlider::enqueueDependencies(); + } + +} \ No newline at end of file diff --git a/customizables/Controls/PopupSlider.php b/customizables/Controls/PopupSlider.php new file mode 100644 index 0000000..4fa4289 --- /dev/null +++ b/customizables/Controls/PopupSlider.php @@ -0,0 +1,55 @@ +options = $options; + } + + public function render() { + ?> + + getFieldName(); + $currentValue = $this->mainSetting->getValue(); + + //phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped + echo $this->buildTag( + 'fieldset', + [ + 'class' => array_merge([$this->controlClass], $this->classes), + 'style' => $this->styles, + 'disabled' => !$this->isEnabled(), + 'data-bind' => $this->makeKoDataBind($this->getKoEnableBinding()), + ] + ); + foreach ($this->options as $option) { + $isChecked = ($currentValue === $option->value); + + echo $this->buildTag('label', array( + 'class' => 'ame-radio-bar-item', + 'title' => $option->description, + )); + + echo $this->buildTag( + 'input', + array_merge(array( + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $this->mainSetting->encodeForForm($option->value), + 'class' => $this->inputClasses, + 'checked' => $isChecked, + 'disabled' => !$option->enabled, + 'data-bind' => $this->makeKoDataBind([ + 'checked' => $this->getKoObservableExpression($option->value), + 'checkedValue' => wp_json_encode($option->value), + 'ameObservableChangeEvents' => 'true', + ]), + ), $this->inputAttributes) + ); + + $buttonContent = esc_html($option->label); + if ( is_string($option->icon) && (strpos($option->icon, 'dashicons-') !== false) ) { + $buttonContent = sprintf( + ' %s', + esc_attr($option->icon), + $buttonContent + ); + } + + $buttonClasses = ['button', 'ame-radio-bar-button']; + if ( !empty($option->label) ) { + $buttonClasses[] = 'ame-rb-has-label'; + } + + //Note that we can't use a "button" element because then the label + //won't correctly select the radio input when clicked. It's probably + //because a label can't be associated with two elements. + echo $this->buildTag( + 'span', + ['class' => $buttonClasses], + $buttonContent + ); + + echo ''; + } + echo ''; + //phpcs:enable + } +} \ No newline at end of file diff --git a/customizables/Controls/RadioGroup.php b/customizables/Controls/RadioGroup.php new file mode 100644 index 0000000..ea88b90 --- /dev/null +++ b/customizables/Controls/RadioGroup.php @@ -0,0 +1,198 @@ +choiceChildren = $params['choiceChildren']; + } + + $this->wrapStyle = isset($params['wrap']) ? $params['wrap'] : self::WRAP_PARAGRAPH; + switch ($this->wrapStyle) { + case self::WRAP_LINE_BREAK: + //A few WordPress settings pages use this. + $this->beforeOption = ''; + $this->afterOption = '
'; + break; + case self::WRAP_PARAGRAPH: + //"Settings -> Reading" uses this, and AME used it in the "Settings" tab. + $this->beforeOption = '

'; + $this->afterOption = '

'; + break; + default: + throw new \InvalidArgumentException("Invalid option wrap style: " . $this->wrapStyle); + } + + if ( isset($params['descriptionsAsTooltips']) ) { + $this->descriptionsAsTooltips = (bool)$params['descriptionsAsTooltips']; + } + } + + public function renderContent(Renderer $renderer) { + $fieldName = $this->getFieldName(); + $currentValue = $this->mainSetting->getValue(); + + $classes = $this->classes; + $hasNestedControls = !empty($this->choiceChildren); + if ( $hasNestedControls ) { + $classes[] = 'ame-rg-has-nested-controls'; + } + + $beforeOption = $this->beforeOption; + $afterOption = $this->afterOption; + if ( $hasNestedControls ) { + //Layout will be handled by CSS grid, so we don't need line breaks, + //and wrapping the options in

tags would mess up the grid. + $beforeOption = $afterOption = ''; + } + + //buildTag() is safe, and we intentionally allow HTML in the label and description. + //phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped + echo $this->buildTag( + 'fieldset', + [ + 'class' => $classes, + 'style' => $this->styles, + 'disabled' => !$this->isEnabled(), + 'data-bind' => $this->makeKoDataBind($this->getKoEnableBinding()), + ] + ); + foreach ($this->options as $option) { + $isChecked = ($currentValue === $option->value); + + echo $beforeOption; + $labelClasses = ['ame-rg-option-label']; + if ( isset($this->choiceChildren[$option->value]) ) { + $labelClasses[] = 'ame-rg-has-choice-child'; + } + echo $this->buildTag('label', ['class' => $labelClasses]); + + echo $this->buildTag( + 'input', + array_merge([ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $this->mainSetting->encodeForForm($option->value), + 'class' => $this->inputClasses, + 'checked' => $isChecked, + 'disabled' => !$option->enabled, + 'id' => $this->getRadioInputId($option), + 'data-bind' => $this->makeKoDataBind([ + 'checked' => $this->getKoObservableExpression($option->value), + 'checkedValue' => wp_json_encode($option->value), + ]), + ], $this->inputAttributes) + ); + echo ' ', $option->label; + + if ( !empty($option->description) ) { + if ( $this->descriptionsAsTooltips ) { + echo ' '; + $renderer->renderTooltipTrigger(new Tooltip( + $option->description, + Tooltip::INFO, + ['ame-understated-tooltip'] + )); + } else { + echo self::formatNestedDescription($option->description); + } + } + echo ''; + echo $afterOption; + + if ( isset($this->choiceChildren[$option->value]) ) { + $childControl = $this->choiceChildren[$option->value]; + echo HtmlHelper::tag('span', ['class' => 'ame-rg-nested-control']); + $renderer->renderControl($childControl); + echo ''; + } + } + echo ''; + //phpcs:enable + } + + /** + * @param ChoiceControlOption $option + * @return string + */ + protected function getRadioInputId($option) { + return $this->getRadioInputPrefix() . sanitize_key(strval($option->value)); + } + + protected function getRadioInputPrefix() { + return self::INPUT_ID_PREFIX . $this->instanceNumber . '-'; + } + + public function serializeForJs() { + $result = parent::serializeForJs(); + if ( !isset($result['children']) ) { + $result['children'] = []; + foreach ($this->choiceChildren as $child) { + $result['children'][] = $child->serializeForJs(); + } + } + return $result; + } + + protected function getKoComponentParams() { + $params = parent::getKoComponentParams(); + + $hasNestedControls = !empty($this->choiceChildren); + $params['wrapStyle'] = $hasNestedControls ? self::WRAP_NONE : $this->wrapStyle; + $params['radioInputPrefix'] = $this->getRadioInputPrefix(); + + if ( $hasNestedControls ) { + //Values can be things that aren't valid JS identifiers, so we'll serialize + //the value-to-child relationship as an array of value + child index pairs. + $valueChildIndexes = []; + $i = 0; + foreach ($this->choiceChildren as $value => $child) { + $valueChildIndexes[] = [$value, $i]; + $i++; + } + $params['valueChildIndexes'] = $valueChildIndexes; + } + + return $params; + } + + public function getChildren() { + return $this->choiceChildren; + } + + public function getAllDescendants() { + foreach ($this->choiceChildren as $child) { + yield $child; + if ( $child instanceof ControlContainer ) { + yield from $child->getAllDescendants(); + } + } + } +} \ No newline at end of file diff --git a/customizables/Controls/Section.php b/customizables/Controls/Section.php new file mode 100644 index 0000000..3fe0037 --- /dev/null +++ b/customizables/Controls/Section.php @@ -0,0 +1,46 @@ + true, + self::CONTENT_ROLE => true, + ]; + + /** + * Indicates how the section should be rendered in the UI. + * + * Note that this is a preference, not a strict requirement. A renderer may choose + * to ignore this setting and display the section in a different way. + * + * @var string + */ + protected $preferredRole = self::NAVIGATION_ROLE; + + public function __construct($title, $children = [], $params = []) { + parent::__construct($title, $children, $params); + + if ( isset($params['preferredRole']) ) { + if ( !array_key_exists($params['preferredRole'], self::VALID_ROLES) ) { + throw new \InvalidArgumentException('Invalid preferred role.'); + } + $this->preferredRole = $params['preferredRole']; + } + } + + protected function getJsUiElementType() { + return 'section'; + } + + public function serializeForJs() { + $result = parent::serializeForJs(); + if ( $this->preferredRole !== self::NAVIGATION_ROLE ) { + $result['preferredRole'] = $this->preferredRole; + } + return $result; + } +} \ No newline at end of file diff --git a/customizables/Controls/SelectBox.php b/customizables/Controls/SelectBox.php new file mode 100644 index 0000000..88dfe7d --- /dev/null +++ b/customizables/Controls/SelectBox.php @@ -0,0 +1,37 @@ +mainSetting->getValue(); + $classes = array_merge(['ame-select-box-control'], $this->classes); + + list($optionHtml, $optionBindings) = ChoiceControlOption::generateSelectOptions( + $this->options, + $currentValue, + $this->mainSetting + ); + + //phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped + echo $this->buildInputElement( + [ + 'class' => $classes, + 'style' => $this->styles, + 'data-bind' => $this->makeKoDataBind(array_merge( + $optionBindings, + ['value' => $this->getKoObservableExpression($currentValue)] + )), + ], + 'select' + ); + echo $optionHtml; + //phpcs:enable + echo ''; + } +} \ No newline at end of file diff --git a/customizables/Controls/StaticHtml.php b/customizables/Controls/StaticHtml.php new file mode 100644 index 0000000..0428be2 --- /dev/null +++ b/customizables/Controls/StaticHtml.php @@ -0,0 +1,46 @@ +html = $html; + if ( isset($params['componentContainer']) ) { + $this->koComponentContainerType = $params['componentContainer']; + } + } + + public function renderContent(Renderer $renderer) { + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML is the point. + echo $this->html; + } + + protected function getKoComponentParams() { + return array_merge( + parent::getKoComponentParams(), + [ + 'html' => $this->html, + 'container' => $this->koComponentContainerType, + ] + ); + } +} \ No newline at end of file diff --git a/customizables/Controls/TextArea.php b/customizables/Controls/TextArea.php new file mode 100644 index 0000000..eb42dd5 --- /dev/null +++ b/customizables/Controls/TextArea.php @@ -0,0 +1,50 @@ +hasPrimaryInput = true; + parent::__construct($settings, $params); + + if ( isset($params['rows']) ) { + $this->rows = max(intval($params['rows']), 1); + } + if ( isset($params['cols']) ) { + $this->cols = max(intval($params['cols']), 1); + } + } + + public function renderContent(Renderer $renderer) { + $value = $this->mainSetting->getValue(''); + + //phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- builtInputElement() is safe + echo $this->buildInputElement( + [ + 'rows' => (int)$this->rows, + 'cols' => (int)$this->cols, + 'class' => 'large-text', + 'data-bind' => $this->makeKoDataBind([ + 'value' => $this->getKoObservableExpression($value), + ]), + ], + 'textarea', + esc_textarea($value) + ); + //phpcs:enable + $this->outputSiblingDescription(); + } +} \ No newline at end of file diff --git a/customizables/Controls/TextInputControl.php b/customizables/Controls/TextInputControl.php new file mode 100644 index 0000000..7b7588b --- /dev/null +++ b/customizables/Controls/TextInputControl.php @@ -0,0 +1,65 @@ +hasPrimaryInput = true; + $this->isCode = !empty($params['isCode']); + if ( !empty($params['inputType']) ) { + $this->inputType = $params['inputType']; + } + } + + public function renderContent(Renderer $renderer) { + $classes = array('regular-text'); + if ( $this->isCode ) { + $classes[] = 'code'; + } + $classes[] = 'ame-text-input-control'; + $value = $this->getMainSettingValue(); + + //phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- buildInputElement() is safe + echo $this->buildInputElement( + array( + 'type' => $this->inputType, + 'value' => ($value === null) ? '' : $value, + 'class' => $classes, + 'style' => $this->styles, + 'data-bind' => $this->makeKoDataBind([ + 'value' => $this->getKoObservableExpression($value), + ]), + ) + ); + //phpcs:enable + $this->outputSiblingDescription(); + } + + protected function getKoComponentParams() { + $params = parent::getKoComponentParams(); + $params['isCode'] = $this->isCode; + $params['inputType'] = $this->inputType; + return $params; + } + +} \ No newline at end of file diff --git a/customizables/Controls/ToggleCheckbox.php b/customizables/Controls/ToggleCheckbox.php new file mode 100644 index 0000000..284ea71 --- /dev/null +++ b/customizables/Controls/ToggleCheckbox.php @@ -0,0 +1,122 @@ +hasPrimaryInput = true; + parent::__construct($settings, $params); + + if ( array_key_exists('onValue', $params) ) { + $this->onValue = $params['onValue']; + } + if ( array_key_exists('offValue', $params) ) { + $this->offValue = $params['offValue']; + } + } + + public function renderContent(Renderer $renderer) { + $isChecked = ($this->getMainSettingValue() === $this->onValue); + + //Encode non-scalar values as JSON. To simplify things for the script that + //receives this data, we'll either encode both values or none of them. + $useJson = !is_scalar($this->onValue) || !is_scalar($this->offValue); + if ( $useJson ) { + $onString = wp_json_encode($this->onValue); + $offString = wp_json_encode($this->offValue); + } else { + $onString = self::boxValueToString($this->onValue); + $offString = self::boxValueToString($this->offValue); + } + + echo HtmlHelper::tag( + 'input', + [ + 'type' => 'hidden', + 'name' => $this->getFieldName(), + 'value' => $offString, + 'class' => 'ame-toggle-checkbox-alternative', + ] + ); + + echo HtmlHelper::tag( + 'label', + ['class' => array_merge(['ame-toggle-checkbox-control'], $this->classes)] + ); + + $jsonOnValue = wp_json_encode($this->onValue); + $jsonOffValue = wp_json_encode($this->offValue); + + //phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Method is safe, and label is allowed to have HTML. + echo $this->buildInputElement( + [ + 'type' => 'checkbox', + 'name' => $this->getFieldName(), + 'value' => $onString, + 'checked' => $isChecked, + 'data-ame-on-value' => $jsonOnValue, + 'data-ame-off-value' => $jsonOffValue, + 'data-bind' => 'ameToggleCheckbox: { + checked: ' . $this->getKoObservableExpression($isChecked ? $this->onValue : $this->offValue) + . ', onValue: ' . $jsonOnValue + . ', offValue: ' . $jsonOffValue . ' }', + ] + ); + echo ' ', $this->label; + //phpcs:enable + + $this->outputNestedDescription(); + echo ''; + + self::enqueueDependencies(); + } + + /** + * Convert one of the checkbox values to a string that can be used in a "value" attribute. + * + * @param scalar $stateValue + * @return string + */ + protected static function boxValueToString($stateValue) { + if ( is_bool($stateValue) ) { + return $stateValue ? '1' : '0'; + } else { + return (string)$stateValue; + } + } + + public function includesOwnLabel() { + return true; + } + + protected function getKoComponentParams() { + return array_merge( + parent::getKoComponentParams(), + [ + 'onValue' => $this->onValue, + 'offValue' => $this->offValue, + ] + ); + } +} \ No newline at end of file diff --git a/customizables/Controls/Toggleable.php b/customizables/Controls/Toggleable.php new file mode 100644 index 0000000..0f48a9d --- /dev/null +++ b/customizables/Controls/Toggleable.php @@ -0,0 +1,49 @@ +enabled = $params['enabled'] ? '__return_true' : '__return_false'; + } else { + $this->enabled = $params['enabled']; + } + } else if ( isset($this->mainSetting) && !empty($this->mainSetting) ) { + $this->enabled = $this->mainSetting->isEditableByUser() ? '__return_true' : '__return_false'; + } + } + + /** + * @return bool + */ + public function isEnabled() { + return call_user_func($this->enabled); + } + + protected function getKoEnableBinding() { + if ( $this->enabled instanceof SettingCondition ) { + return ['enable' => $this->enabled->getJsKoExpression()]; + } + return $this->isEnabled() ? [] : ['enable' => false]; + } + + protected function serializeConditionForJs() { + if ( $this->enabled instanceof SettingCondition ) { + return $this->enabled->serializeForJs(); + } + return $this->isEnabled(); + } +} \ No newline at end of file diff --git a/customizables/Controls/Tooltip.php b/customizables/Controls/Tooltip.php new file mode 100644 index 0000000..2fa9f5c --- /dev/null +++ b/customizables/Controls/Tooltip.php @@ -0,0 +1,45 @@ +htmlContent = $htmlContent; + $this->type = $type; + $this->extraClasses = $extraClasses; + } + + /** + * @return string + */ + public function getType() { + return $this->type; + } + + /** + * @return string + */ + public function getHtmlContent() { + return $this->htmlContent; + } + + /** + * @return string[] + */ + public function getExtraClasses() { + return $this->extraClasses; + } +} \ No newline at end of file diff --git a/customizables/Controls/UiElement.php b/customizables/Controls/UiElement.php new file mode 100644 index 0000000..2d4b47c --- /dev/null +++ b/customizables/Controls/UiElement.php @@ -0,0 +1,146 @@ +id = $params['id']; + } + if ( !empty($params['description']) ) { + $this->description = $params['description']; + } + if ( !empty($params['classes']) ) { + $this->classes = (array)$params['classes']; + } + if ( !empty($params['styles']) ) { + $this->styles = (array)$params['styles']; + } + if ( isset($params['renderCondition']) ) { + $this->renderCondition = $params['renderCondition']; + } + } + + /** + * @return string + */ + public function getId() { + return $this->id; + } + + /** + * @return string + */ + public function getDescription() { + if ( is_string($this->description) ) { + return $this->description; + } elseif ( is_callable($this->description) ) { + return call_user_func($this->description); + } else { + return strval($this->description); + } + } + + /** + * @return array + */ + public function getClasses() { + return $this->classes; + } + + protected function buildTag($tagName, $attributes = array(), $content = null) { + return HtmlHelper::tag($tagName, $attributes, $content); + } + + /** + * @return bool + */ + public function declinesExternalLineBreaks() { + return $this->declinesExternalLineBreaks; + } + + public function shouldRender() { + if ( is_callable($this->renderCondition) ) { + return call_user_func($this->renderCondition); + } + return (bool)$this->renderCondition; + } + + public function serializeForJs() { + $description = $this->getDescription(); + $result = ['t' => $this->getJsUiElementType()]; + if ( !empty($this->classes) ) { + $result['classes'] = $this->classes; + } + if ( !empty($this->styles) ) { + $result['styles'] = $this->styles; + } + if ( !empty($description) ) { + $result['description'] = $description; + } + if ( !empty($this->id) ) { + $result['id'] = $this->id; + } + + $params = $this->getKoComponentParams(); + if ( !empty($params) ) { + $result['params'] = $params; + } + + return $result; + } + + abstract protected function getJsUiElementType(); + + /** + * Get additional parameters for the Knockout component that renders this element. + * + * @return array + */ + protected function getKoComponentParams() { + return []; + } + + public function enqueueKoComponentDependencies() { + //Do nothing by default. + } +} \ No newline at end of file diff --git a/customizables/Controls/WpEditor.php b/customizables/Controls/WpEditor.php new file mode 100644 index 0000000..f6924f7 --- /dev/null +++ b/customizables/Controls/WpEditor.php @@ -0,0 +1,73 @@ +hasPrimaryInput = true; + parent::__construct($settings, $params); + + if ( isset($params['rows']) ) { + $this->rows = max(intval($params['rows']), 1); + } + if ( isset($params['teeny']) ) { + $this->teeny = boolval($params['teeny']); + } + } + + public function renderContent(Renderer $renderer) { + wp_editor( + (string) $this->getMainSettingValue(), + $this->getPrimaryInputId(), + array( + 'textarea_name' => $this->getFieldName(), + 'textarea_rows' => $this->rows, + 'teeny' => $this->teeny, + 'wpautop' => true, + ) + ); + + $this->outputSiblingDescription(); + + static::enqueueDependencies(); + } + + public function getPrimaryInputId() { + //For wp_editor, the ID must only contain lowercase letters and underscores. + return preg_replace('/[^a-z_]/', '_', parent::getPrimaryInputId()); + } + + public function supportsLabelAssociation() { + //This control has a primary input and an ID, but a label element + //cannot move focus to a visual editor, so we don't need a label. + return false; + } + + protected function getAutoAcSettingId() { + //The visual editor probably needs additional care to detect + //changes in JavaScript. + return null; + } + + public function enqueueKoComponentDependencies() { + wp_enqueue_media(); //Required for the media button (tested in WP 6.1.1). + wp_enqueue_editor(); + + parent::enqueueKoComponentDependencies(); + } + + protected function getKoComponentParams() { + $params = parent::getKoComponentParams(); + $params['rows'] = $this->rows; + $params['teeny'] = $this->teeny; + return $params; + } +} \ No newline at end of file diff --git a/customizables/Customizable.php b/customizables/Customizable.php new file mode 100644 index 0000000..0e20b16 --- /dev/null +++ b/customizables/Customizable.php @@ -0,0 +1,72 @@ +id = $id; + $this->store = $store; + + $this->label = isset($params['label']) ? $params['label'] : (!empty($this->label) ? $this->label : $id); + if ( isset($params['description']) ) { + $this->description = $params['description']; + } + if ( isset($params['groupTitle']) ) { + $this->groupTitle = $params['groupTitle']; + } + } + + /** + * @return string + */ + public function getId() { + return $this->id; + } + + /** + * @return string + */ + public function getLabel() { + return $this->label; + } + + /** + * @return string + */ + public function getDescription() { + return $this->description; + } + + /** + * @return string|null + */ + public function getCustomGroupTitle() { + return $this->groupTitle; + } + + public function getStore() { + return $this->store;//todo: remove debug code + } +} \ No newline at end of file diff --git a/customizables/HtmlHelper.php b/customizables/HtmlHelper.php new file mode 100644 index 0000000..603b714 --- /dev/null +++ b/customizables/HtmlHelper.php @@ -0,0 +1,81 @@ + $attributes + * @param string|null $content + * @return string + */ + public static function tag($tagName, $attributes = array(), $content = null) { + $html = '<' . $tagName; + $charset = self::getCharset(); + + //Convert array of CSS classes to a space-separated string. + if ( isset($attributes['class']) && is_array($attributes['class']) ) { + $attributes['class'] = implode(' ', $attributes['class']); + } + + //Convert [property => value] to an inline style. + if ( isset($attributes['style']) && is_array($attributes['style']) ) { + if ( !empty($attributes['style']) ) { + $styleAttr = ''; + foreach ($attributes['style'] as $name => $value) { + $styleAttr .= $name . ':' . $value . ';'; + } + $attributes['style'] = $styleAttr; + } else { + unset($attributes['style']); + } + } + + $filteredAttributes = array_filter($attributes, array(self::class, 'isNonEmptyAttributeValue')); + + //Special case: The empty string is a valid value for the "value" attribute. + //For example, we want this for

+ renderSectionChildren($section); + echo ''; + + $this->renderPendingSections(); + } + + protected function renderChildSection($section) { + if ( $section->hasChildren() ) { + $this->pendingSections[] = $section; + } + + echo HtmlHelper::tag('li', [ + 'class' => 'ame-ac-section-link', + 'data-target-id' => $this->getSectionElementId($section), + ]); + printf( + '

%s

', + esc_html($section->getTitle()) + ); + echo ''; + } + + protected function renderControlGroup($group) { + $title = $group->getTitle(); + if ( !empty($title) ) { + echo '
  • '; + echo '

    '; + echo esc_html($title); + echo '

    '; + echo '
  • '; + } + + $this->renderGroupChildren($group); + } + + protected function renderUngroupedControl($control) { + $this->renderControl($control); + } + + public function renderControl($control, $parentContext = null) { + $settings = $control->getSettings(); + $settingIds = array_map(function ($setting) { + return $setting->getId(); + }, $settings); + + //The collection of settings IDs should be an associative array. If it's not, + //assign the first setting to the "default" key. + $isAssociative = false; + foreach ($settingIds as $key => $value) { + if ( is_string($key) ) { + $isAssociative = true; + break; + } + } + if ( !$isAssociative ) { + $firstId = array_shift($settingIds); + $settingIds['default'] = $firstId; + } + + echo HtmlHelper::tag('li', [ + 'class' => 'ame-ac-control', + 'data-ac-setting-ids' => !empty($settingIds) ? wp_json_encode($settingIds) : null, + 'data-ac-control-type' => $control->getType(), + ]); + if ( !$control->includesOwnLabel() ) { + $this->renderControlLabel($control); + } + + parent::renderControl($control, $parentContext); + echo ''; + } + + public function renderTooltipTrigger(Tooltip $tooltip) { + // TODO: Implement renderTooltipTrigger() method. + echo '[tooltip here]'; + } + + protected function getSectionElementId($section) { + return 'ame-ac-section-' . $section->getId(); + } + + /** + * @param \YahnisElsts\AdminMenuEditor\Customizable\Controls\Control $control + * @return void + */ + protected function renderControlLabel($control) { + $label = $control->getLabel(); + if ( empty($label) ) { + return; + } + + $labelForId = $control->getLabelTargetId(); + if ( $control->supportsLabelAssociation() && !empty($labelForId) ) { + printf( + '', + esc_attr($labelForId), + esc_html($label) + ); + } else { + printf( + '%s', + esc_html($label) + ); + } + } +} \ No newline at end of file diff --git a/customizables/Rendering/BoxyFormRenderer.php b/customizables/Rendering/BoxyFormRenderer.php new file mode 100644 index 0000000..db2bfbd --- /dev/null +++ b/customizables/Rendering/BoxyFormRenderer.php @@ -0,0 +1,118 @@ +getAsSections() as $section) { + if ( $section->getId() === 'sidebar' ) { + $sidebar[] = $section; + } else { + $main[] = $section; + } + } + + if ( empty($sidebar) ) { + parent::renderStructure($structure); + return; + } + + //Render the main column. + echo '
    '; + foreach ($main as $section) { + $this->renderSection($section); + } + echo '
    '; + + //Render the sidebar. + echo '
    '; + $this->fullWidthGroupsEnabled = true; + foreach ($sidebar as $section) { + $this->renderSection($section); + } + $this->fullWidthGroupsEnabled = false; + echo '
    '; + echo '
    '; + } + + public function renderSection($section) { + echo HtmlHelper::tag('div', ['class' => 'ame-form-section ame-form-box']); + + $title = $section->getTitle(); + if ( !empty($title) ) { + echo '
    '; + echo '

    '; + $this->renderSectionTitleContent($section); + echo '

    '; + } + + $description = $section->getDescription(); + if ( !empty($description) ) { + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML intentionally allowed. + echo "\n", '

    ', $description, '

    '; + } + + echo '
    '; + $this->renderSectionChildren($section); + echo "
    \n"; + + echo ''; + } + + /** + * @param \YahnisElsts\AdminMenuEditor\Customizable\Controls\ControlGroup $group + * @return void + */ + protected function renderControlGroup($group) { + $id = $group->getId(); + + $groupClasses = array_merge(['ame-form-box-group'], $group->getClasses()); + echo HtmlHelper::tag( + 'div', + [ + 'class' => $groupClasses, + 'id' => !empty($id) ? $id : null, + ] + ); + + if ( !$this->fullWidthGroupsEnabled ) { + echo '
    '; + $this->renderGroupTitleContent($group); + echo '
    '; + } + + $contentClasses = ['ame-form-box-group-content']; + if ( $this->fullWidthGroupsEnabled ) { + $contentClasses[] = 'ame-form-box-full-width-content'; + } + + echo HtmlHelper::tag('div', ['class' => $contentClasses]); + $this->renderGroupChildren($group); + echo ''; + + echo ''; + } + + public function enqueueDependencies($containerSelector = '') { + static $done = false; + if ( $done ) { + return; + } + $done = true; + parent::enqueueDependencies($containerSelector); + + wp_enqueue_style( + 'ame-boxy-form-renderer', + plugins_url('assets/form-box.css', AME_CUSTOMIZABLE_BASE_FILE), + array(), + '20220609-2' + ); + } +} \ No newline at end of file diff --git a/customizables/Rendering/ClassicRenderer.php b/customizables/Rendering/ClassicRenderer.php new file mode 100644 index 0000000..4b54908 --- /dev/null +++ b/customizables/Rendering/ClassicRenderer.php @@ -0,0 +1,124 @@ +needsTooltipDependencies = true; + + $linkClasses = array('ws_tooltip_trigger'); + $dashiconClasses = array('dashicons'); + + switch ($tooltip->getType()) { + case Tooltip::EXPERIMENTAL: + $linkClasses[] = 'ame-warning-tooltip'; + $dashiconClasses[] = 'dashicons-admin-tools'; + break; + case Tooltip::INFO: + default: + $dashiconClasses[] = 'dashicons-info'; + } + + $linkClasses = array_merge($linkClasses, $tooltip->getExtraClasses()); + + printf( + '', + esc_attr(implode(' ', $linkClasses)), + esc_attr($tooltip->getHtmlContent()), + esc_attr(implode(' ', $dashiconClasses)) + ); + } + + public function enqueueDependencies($containerSelector = '') { + static $done = false; + if ( $done ) { + return; + } + $done = true; + + parent::enqueueDependencies($containerSelector); + + if ( $this->needsTooltipDependencies ) { + if ( !(wp_script_is('jquery-qtip', 'done') || wp_script_is('jquery-qtip')) ) { + wp_enqueue_script('jquery-qtip'); + } + + $tooltipInitScript = /** @lang JavaScript */ + '(function(containerSelector) { + jQuery(function ($) { + $(containerSelector + \' .ws_tooltip_trigger\').qtip({ + style: { + classes: \'qtip qtip-rounded ws_tooltip_node ws_wide_tooltip\' + } + }); + }); + })'; + + $tooltipInitScript .= "('" . esc_js($containerSelector) . "');"; + $initScriptTag = ''; + + //Adding inline scripts only works if the script is in queue + //and not already done. + if ( wp_script_is('jquery-qtip') && !wp_script_is('jquery-qtip', 'done') ) { + wp_add_inline_script('jquery-qtip', $tooltipInitScript); + } else { + $fallbackAction = 'admin_print_footer_scripts'; + if ( !did_action($fallbackAction) ) { + add_action($fallbackAction, function () use ($initScriptTag) { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Outputs generated JS. + echo $initScriptTag; + }); + } else { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $initScriptTag; + } + } + } + } + + /** + * @param \YahnisElsts\AdminMenuEditor\Customizable\Controls\ControlGroup $group + * @return void + */ + protected function renderGroupTitleContent($group) { + $tooltip = $group->getTooltip(); + $title = $group->getTitle(); + if ( !empty($title) ) { + $labelTargetId = $group->getLabelFor(); + if ( !empty($labelTargetId) ) { + printf( + '', + esc_attr($labelTargetId), + esc_html($title) + ); + } else { + echo esc_html($title); + } + } + + if ( $tooltip ) { + echo ' '; //Add a space between the title and the tooltip. + $this->renderTooltipTrigger($tooltip); + } + } + + protected function renderSectionTitleContent(Section $section) { + if ( !$section->hasTitle() ) { + return; + } + + $title = $section->getTitle(); + echo esc_html($title); + + $tooltip = $section->getTooltip(); + if ( $tooltip ) { + echo ' '; + $this->renderTooltipTrigger($tooltip); + } + } +} \ No newline at end of file diff --git a/customizables/Rendering/FormTableRenderer.php b/customizables/Rendering/FormTableRenderer.php new file mode 100644 index 0000000..6cc5f59 --- /dev/null +++ b/customizables/Rendering/FormTableRenderer.php @@ -0,0 +1,142 @@ +shouldRender() ) { + //Should this even be allowed? + echo sprintf( + '', + esc_html($section->getTitle()), + esc_html($section->getId()) + ); + } + + echo '
    '; + $this->renderSectionHeading($section); + + $description = $section->getDescription(); + if ( !empty($description) ) { + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML intentionally allowed. + echo "\n", '

    ', $description, '

    '; + } + + //Render each section as a table. + echo ''; + $this->renderSectionChildren($section); + echo '
    '; + echo '
    '; + } + + protected function renderSectionHeading($section, $headingLevel = 2) { + $title = $section->getTitle(); + if ( !empty($title) ) { + echo sprintf('', (int)$headingLevel); + $this->renderSectionTitleContent($section); + echo sprintf('', (int)$headingLevel); + } + } + + protected function renderChildSection($section) { + $this->sectionNestingLevel++; + + if ( $section->hasTitle() ) { + $headingLevel = min(6, 2 + $this->sectionNestingLevel); + echo ''; + $this->renderSectionHeading($section, $headingLevel); + echo ''; + } + + $this->renderSectionChildren($section); + $this->sectionNestingLevel--; + } + + protected function renderControlGroup($group) { + $isStacked = $group->isStacked(); + + /* + * - Render top-level groups as table rows. Optionally, their contents + * can be wrapped in a fieldset element. + * - Render nested groups as either fieldset elements or a flat sequence + * of controls. + */ + $isFieldset = $group->wantsFieldset(); + if ( $isFieldset === null ) { + $isFieldset = $this->isInsideRow; + } + $renderAsTableRow = !$this->isInsideRow; + + $wasInsideRow = $this->isInsideRow; + if ( $renderAsTableRow ) { + $this->isInsideRow = true; + + printf('', esc_attr($group->getId())); + printf( + '', + esc_attr($group->hasTooltip() ? 'ame-customizable-cg-has-tooltip' : '') + ); + + $this->renderGroupTitleContent($group); + + echo ''; + echo ''; + } + + if ( $isFieldset ) { + /** @noinspection HtmlUnknownAttribute */ + printf('
    ', $group->isEnabled() ? '' : 'disabled'); + } + + $this->renderGroupChildren($group, $isStacked); + + if ( $isFieldset ) { + echo '
    '; + } + if ( $renderAsTableRow ) { + echo ''; + echo ''; + $this->isInsideRow = $wasInsideRow; + } + } + + public function renderControl($control, $parentContext = null) { + if ( !$control->shouldRender() ) { + //This should really never happen. + echo ''; + return; + } + + $addLineBreaks = ($parentContext && !$control->declinesExternalLineBreaks()); + if ( $addLineBreaks ) { + echo '

    '; + } + + parent::renderControl($control, $parentContext); + + if ( $addLineBreaks ) { + echo '

    '; + } + } + + public function enqueueDependencies($containerSelector = '') { + static $done = false; + if ( $done ) { + return; + } + $done = true; + + parent::enqueueDependencies($containerSelector); + + wp_enqueue_style( + 'ame-form-table-renderer', + plugins_url('assets/form-table.css', AME_CUSTOMIZABLE_BASE_FILE), + array(), + '20221129' + ); + } +} \ No newline at end of file diff --git a/customizables/Rendering/Renderer.php b/customizables/Rendering/Renderer.php new file mode 100644 index 0000000..5bbe40c --- /dev/null +++ b/customizables/Rendering/Renderer.php @@ -0,0 +1,119 @@ +getAsSections() as $section) { + if ( $section->shouldRender() ) { + $this->renderSection($section); + } + } + } + + /** + * @param Section $section + * @return void + */ + abstract public function renderSection($section); + + protected function renderSectionChildren(Section $section) { + foreach ($section->getChildren() as $child) { + if ( !$child->shouldRender() ) { + continue; + } + + if ( $child instanceof Section ) { + $this->renderChildSection($child); + } else if ( $child instanceof ControlGroup ) { + $this->renderControlGroup($child); + } else if ( $child instanceof Control ) { + $this->renderUngroupedControl($child); + } else { + throw new \RuntimeException( + 'Unexpected child type: ' . + (is_object($child) ? get_class($child) : gettype($child)) + ); + } + } + } + + /** + * @param Section $section + * @return void + */ + protected function renderChildSection($section) { + $this->renderSection($section); + } + + /** + * @param ControlGroup $group + * @return void + */ + abstract protected function renderControlGroup($group); + + protected function renderGroupChildren(ControlGroup $group, $parentContext = null) { + foreach ($group->getChildren() as $child) { + if ( !$child->shouldRender() ) { + continue; + } + + if ( $child instanceof Control ) { + $this->renderControl($child, $parentContext); + } else if ( $child instanceof ControlGroup ) { + $this->renderChildControlGroup($child); + } else { + throw new \RuntimeException( + 'Unexpected child type: ' . + (is_object($child) ? get_class($child) : gettype($child)) + ); + } + } + } + + protected function renderChildControlGroup(ControlGroup $group) { + $this->renderControlGroup($group); + } + + /** + * @param Control $control + */ + protected function renderUngroupedControl($control) { + $params = []; + $controlId = $control->getId(); + if ( $controlId ) { + $params['id'] = 'ame_control_group-' . $controlId; + } + + $tempGroup = new ControlGroup($control->getAutoGroupTitle(), [$control], $params); + $this->renderControlGroup($tempGroup); + } + + /** + * @param Control $control + * @param null|mixed $parentContext Arbitrary context data from the parent control. + */ + public function renderControl($control, $parentContext = null) { + $control->renderContent($this); + } + + abstract public function renderTooltipTrigger(Tooltip $tooltip); + + /** + * @param string $containerSelector The CSS selector for the element that contains + * the rendered controls. Typically, this is the form element. + * @return void + */ + public function enqueueDependencies($containerSelector = '') { + //No dependencies by default. + } +} \ No newline at end of file diff --git a/customizables/Rendering/TabbedPanelRenderer.php b/customizables/Rendering/TabbedPanelRenderer.php new file mode 100644 index 0000000..4ef0866 --- /dev/null +++ b/customizables/Rendering/TabbedPanelRenderer.php @@ -0,0 +1,138 @@ +additionalStructureClasses = $additionalStructureClasses; + } + + public function renderStructure($structure) { + $panelId = 'ame-tabbed-panel-' . (++self::$panelCounter); + $structureClasses = array_merge(['ame-tabbed-panel'], $this->additionalStructureClasses); + echo HtmlHelper::tag('div', ['class' => $structureClasses, 'id' => $panelId]); + + //Render a list of tabs. + echo HtmlHelper::tag('ul', ['class' => 'ame-tp-tabs']); + foreach ($structure->getAsSections() as $section) { + echo '
  • '; + echo HtmlHelper::tag( + 'a', + [ + 'href' => '#' . $this->getSectionElementId($section), + 'class' => 'ame-tp-tab-link', + ], + esc_html($section->getTitle()) + ); + echo '
  • '; + } + echo ''; + + echo HtmlHelper::tag('div', ['class' => 'ame-tp-content']); + parent::renderStructure($structure); + echo ''; + + echo ''; + } + + public function renderSection($section) { + echo HtmlHelper::tag( + 'div', + [ + 'id' => $this->getSectionElementId($section), + 'class' => array_merge(['ame-tp-section'], $section->getClasses()), + ] + ); + + echo '

    '; + $this->renderSectionTitleContent($section); + echo '

    '; + + + echo '
    '; + $this->renderSectionChildren($section); + echo '
    '; + echo ''; + } + + protected function renderControlGroup($group) { + $isFieldset = $group->wantsFieldset(); + if ( $isFieldset === null ) { + $isFieldset = false; + } + + $classes = array_merge(array('ame-tp-control-group'), $group->getClasses()); + if ( $group->isFullWidth() ) { + $classes[] = 'ame-tp-full-width-control-group'; + } + echo HtmlHelper::tag('div', ['class' => $classes]); + + if ( !$group->isFullWidth() ) { + echo HtmlHelper::tag('div', ['class' => 'ame-tp-control-group-title']); + $this->renderGroupTitleContent($group); + echo ''; + } + + echo HtmlHelper::tag('div', ['class' => 'ame-tp-control-group-children']); + if ( $isFieldset ) { + echo HtmlHelper::tag('fieldset', ['disabled' => !$group->isEnabled()]); + } + + $this->renderGroupChildren($group); + + if ( $isFieldset ) { + echo ''; + } + echo ''; + + echo ''; + } + + protected function getSectionElementId(Section $section) { + $suffix = $section->getId(); + if ( empty($suffix) ) { + $suffix = sanitize_key( + $section->getTitle() . '-' . substr(sha1(spl_object_hash($section)), 0, 8) + ); + } + return 'ame-tp-section-' . $suffix; + } + + public function enqueueDependencies($containerSelector = '') { + static $done = false; + if ( $done ) { + return; + } + $done = true; + + parent::enqueueDependencies($containerSelector); + + wp_enqueue_auto_versioned_style( + 'ame-tabbed-panels', + plugins_url('assets/tabbed-panels.css', AME_CUSTOMIZABLE_BASE_FILE) + ); + + if ( !(wp_script_is('jquery-ui-tabs', 'done') || wp_script_is('jquery-ui-tabs')) ) { + wp_enqueue_script('jquery-ui-tabs'); + } + + add_action('admin_print_footer_scripts', [$this, 'outputInitScript'], 100); + } + + public function outputInitScript() { + ?> + + getSettingsForm()->output(); + return true; + } + + public function handleSettingsForm($post = array()) { + $this->getSettingsForm()->handleUpdateRequest($post); + } + + private function getSettingsForm() { + if ( $this->form === null ) { + $this->form = SettingsForm::builder($this->settingsFormAction) + ->id('ws_plugin_settings_form') + ->structure($this->getInterfaceStructure()) + ->settings($this->getSettingsDictionary()->getRegisteredSettings()) + ->submitUrl($this->getTabUrl(array('noheader' => 1))) + ->redirectAfterSaving($this->getTabUrl(), array('message' => '1')) + ->treatMissingFieldsAsEmpty() + ->postProcessSettings( + /** + * @param $values + * @param array $settingsById + * @return void + */ + function ($values, $settingsById) { + //Update the allowed user ID when changing "who can access this plugin". + if ( isset($settingsById['plugin_access'], $settingsById['allowed_user_id']) ) { + if ( $settingsById['plugin_access']->getValue() === 'specific_user' ) { + $settingsById['allowed_user_id']->update(get_current_user_id()); + } else { + $settingsById['allowed_user_id']->update(null); + } + } + } + ) + ->build(); + } + return $this->form; + } + + private function getSettingsDictionary() { + if ( $this->settings === null ) { + $optionName = $this->menuEditor->is_pro_version() ? 'ws_menu_editor_pro' : 'ws_menu_editor'; + + $store = new ScopedOptionStorage( + $optionName, + //Core settings are always global even if menu settings are per-site. + ScopedOptionStorage::GLOBAL_SCOPE + ); + + $this->settings = new AmeCoreSettings($this->menuEditor, $store); + } + return $this->settings; + } + + private function getInterfaceStructure() { + $isProVersion = $this->menuEditor->is_pro_version(); + $settings = $this->getSettingsDictionary(); + + $b = new ElementBuilderFactory($settings); + $structure = $b->structure( + $b->group( + 'Who can access this plugin', + $b->radioGroup('plugin_access'), + $b->auto('hide_plugin_from_others') + )->stacked(), + + $b->auto('menu_config_scope'), + + $this->createModulesGroup($b), + + $b->group( + 'Interface', + $b->auto('hide_advanced_settings'), + $b->auto('show_deprecated_hide_button')->onlyIf($isProVersion) + )->stacked(), + + $b->group( + 'Editor colour scheme', + $b->radioGroup('ui_colour_scheme') + ), + + $b->auto('submenu_icons_enabled')->onlyIf($isProVersion), + + $b->group( + 'New menu visibility', + $b->radioGroup('unused_item_permissions') + ) + ->onlyIf($isProVersion) + ->tooltip( + "This setting controls the default permissions of menu items that are + not present in the last saved menu configuration. +

    + This includes new menus added by plugins and themes. + In Multisite, it also applies to menus that exist on some sites but not others. + It doesn't affect menu items that you add through the Admin Menu Editor interface." + ), + + $b->auto('unused_item_position') + ->tooltip( + "This setting controls the position of menu items that are not present + in the last saved menu configuration. +

    + This includes new menus added by plugins and themes. + In Multisite, it also applies to menus that exist only on certain sites but not on all sites. + It doesn't affect menu items that you add through the Admin Menu Editor interface." + ), + + $b->auto('deep_nesting_enabled') + ->tooltip( + "Caution: Experimental feature.
    + This feature might not work as expected, and it could cause conflicts with other plugins or themes.", + Tooltip::EXPERIMENTAL + ) + //The free version lacks the ability to render deeply nested menus in the dashboard, so the nesting + //options are hidden by default. However, if the user somehow acquires a configuration where this + //feature is enabled (e.g. by importing config from the Pro version), the free version can display + //and even edit that configuration to a limited extent. + ->onlyIf( + $isProVersion || $settings->get('was_nesting_ever_changed') + ), + + $b->group( + 'WPML support', + $b->auto('wpml_support_enabled') + )->stacked(), + + $b->group( + 'bbPress override', + $b->auto('bbpress_override_enabled') + )->stacked(), + + $b->auto('error_verbosity'), + + $b->group( + 'Debugging', + $b->auto('security_logging_enabled'), + $b->auto('force_custom_dashicons'), + $b->auto('compress_custom_menu') + )->stacked(), + + $b->group( + 'Server info', + $b->html( + "
    +
    PHP error log:
    + + " . esc_html(ini_get('error_log')) . " +
    + +
    +
    PHP memory usage:
    " + . sprintf( + '%.2f MiB of %s', + memory_get_peak_usage() / (1024 * 1024), + esc_html(ini_get('memory_limit')) + ) + . "
    " + ) + ) + ); + + return $structure->build(); + } + + private function createModulesGroup(ElementBuilderFactory $b) { + $activeModulesGroup = $b->group('Modules') + ->id('ame-available-modules') + ->stacked() + ->fieldset() + ->tooltip( + 'Modules are plugin features that can be turned on or off.
    ' + . 'Turning off unused features will slightly increase performance ' + . 'and may help with certain compatibility issues.' + ); + + foreach ($this->menuEditor->get_available_modules() as $id => $module) { + if ( !empty($module['isAlwaysActive']) ) { + continue; + } + + $isCompatible = $this->menuEditor->is_module_compatible($module); + $compatibilityNote = ''; + if ( !$isCompatible && !empty($module['requiredPhpVersion']) ) { + if ( version_compare(phpversion(), $module['requiredPhpVersion'], '<') ) { + $compatibilityNote = sprintf( + 'Required PHP version: %1$s or later. Installed PHP version: %2$s', + htmlspecialchars($module['requiredPhpVersion']), + htmlspecialchars(phpversion()) + ); + } + } + + $activeModulesGroup->add( + $b->checkbox() + ->label(htmlspecialchars(!empty($module['title']) ? $module['title'] : $id)) + ->description($compatibilityNote) + ->enabled($isCompatible) + ->params(array( + 'inputAttributes' => array( + 'name' => 'is_active_module[]', + 'value' => $id, + 'checked' => $this->menuEditor->is_module_active($id, $module), + ), + )) + ); + } + return $activeModulesGroup; + } +} + +class AmeCoreSettings extends AbstractSettingsDictionary { + /** + * @var WPMenuEditor + */ + protected $menuEditor; + + public function __construct(WPMenuEditor $menuEditor, StorageInterface $store = null) { + if ( !isset($store) ) { + $store = new ScopedOptionStorage( + 'ws_menu_editor_pro', + ScopedOptionStorage::GLOBAL_SCOPE + ); + } + $this->menuEditor = $menuEditor; + parent::__construct($store); + + if ( $this->store instanceof CompressedStorage ) { + $this->store->setCompressionEnabled($this->get('compress_custom_menu', false)); + } + } + + protected function createDefaults() { + return $this->menuEditor->get_default_options(); + } + + protected function createSettings() { + $factory = new SettingFactory($this->store, $this->defaults); + + $isMultisite = is_multisite(); + $isSuperAdmin = is_super_admin(); + $isProVersion = $this->menuEditor->is_pro_version(); + $currentUser = wp_get_current_user(); + $menuEditor = $this->menuEditor; + + $settings = array( + $factory->stringEnum( + 'plugin_access', + array('super_admin', 'manage_options', 'specific_user'), + 'Who can access this plugin' + ) + ->describeChoice( + 'super_admin', + 'Super Admin', + $isMultisite ? null : 'On a single site installation this is usually the same as the Administrator role.', + $isSuperAdmin + ) + ->describeChoice( + 'manage_options', + 'Anyone with the "manage_options" capability', + 'By default only Administrators have this capability.', + current_user_can('manage_options') + ) + ->describeChoice( + 'specific_user', + 'Only the current user', + 'Login: ' . esc_html($currentUser->user_login) . ', user ID: ' . esc_html(get_current_user_id()), + //In Multisite only Super Admins can choose this option. + $isSuperAdmin || !$isMultisite + ), + $factory->integer( + 'allowed_user_id', + '(Internal) ID of the user that can access the plugin when "plugin_access" is "specific_user".', + array( + 'default' => null, + 'isEditable' => '__return_false', //Not directly editable by the user. + ) + ), + new AmeHidePluginSetting('hide_plugin_from_others', $this->store), + $factory->stringEnum( + 'menu_config_scope', + array('global', 'site'), + 'Multisite settings', + array( + 'isEditable' => function () use ($isMultisite) { + return $isMultisite && is_super_admin(); + }, + ) + ) + ->describeChoice( + 'global', + 'Global — Use the same admin menu settings for all network sites.' + ) + ->describeChoice( + 'site', + 'Per-site — Use different admin menu settings for each site.' + ), + $factory->boolean( + 'hide_advanced_settings', + 'Hide advanced menu options by default' + ), + $factory->boolean( + 'security_logging_enabled', + 'Show menu access checks performed by the plugin on every admin page', + array( + 'description' => "This can help track down configuration problems + and figure out why your menu permissions don't work the way they should. + + Note: It's not recommended to use this option on a live site as + it can reveal information about your menu configuration.", + ) + ), + $factory->boolean( + 'show_deprecated_hide_button', + 'Enable the "Hide (cosmetic)" toolbar button', + array( + 'description' => "This button hides the selected menu item without making it inaccessible.", + ) + ), + $factory->stringEnum( + 'submenu_icons_enabled', + array('always', 'if_custom', 'never'), + 'Show submenu icons' + )->describeChoice('if_custom', 'Only when manually selected'), + $factory->boolean( + 'force_custom_dashicons', + 'Attempt to override menu icon CSS that was added by other plugins' + ), + $factory->stringEnum( + 'ui_colour_scheme', + array('classic', 'modern-one', 'wp-grey'), + 'Editor colour scheme' + ) + ->describeChoice('classic', 'Blue and yellow') + ->describeChoice('modern-one', 'Modern') + ->describeChoice('wp-grey', 'Grey'), + $factory->stringEnum( + 'unused_item_position', + array('relative', 'bottom'), + 'New menu position' + ) + ->describeChoice( + 'relative', + 'Maintain relative order', + 'Attempts to put new items in the same relative positions ' + . 'as they would be in in the default admin menu.' + ) + ->describeChoice( + 'bottom', + 'Bottom', + 'Puts new items at the bottom of the admin menu.' + ), + $factory->stringEnum( + 'unused_item_permissions', + array('unchanged', 'match_plugin_access'), + 'New menu visibility' + ) + ->describeChoice( + 'unchanged', + 'Leave unchanged (default)', + 'No special restrictions. Visibility will depend on the plugin that added the menus.' + ) + ->describeChoice( + 'match_plugin_access', + 'Show only to users who can access this plugin', + 'Automatically hides all new and unrecognized menus from regular users. ' + . 'To make new menus visible, you have to manually enable them in the menu editor.' + ), + $factory->enum( + 'error_verbosity', + array( + WPMenuEditor::VERBOSITY_LOW, + WPMenuEditor::VERBOSITY_NORMAL, + WPMenuEditor::VERBOSITY_VERBOSE, + ), + 'Error verbosity level' + ) + ->describeChoice( + WPMenuEditor::VERBOSITY_LOW, + 'Low', + 'Shows a generic error message without any details.' + ) + ->describeChoice( + WPMenuEditor::VERBOSITY_NORMAL, + 'Normal', + 'Shows a one or two sentence explanation. For example: "The current' + . ' user doesn\'t have the "manage_options" capability that is required' + . ' to access the "Settings" menu item."' + ) + ->describeChoice( + WPMenuEditor::VERBOSITY_VERBOSE, + 'Verbose', + 'Like "normal", but also includes a log of menu settings and permissions + that caused the current menu to be hidden. Useful for debugging.' + ), + $factory->boolean( + 'compress_custom_menu', + "Compress menu configuration data that's stored in the database", + array( + 'description' => sprintf( + "Significantly reduces the size of the %s DB option, + but adds decompression overhead to every page.", + esc_html($this->store->getStorageKey()) + ), + ) + ), + $factory->boolean( + 'wpml_support_enabled', + 'Make edited menu titles translatable with WPML', + array( + 'description' => 'The titles will appear in the "Strings" section in WPML. ' + . 'If you don\'t use WPML or a similar translation plugin, ' + . 'you can safely disable this option.', + ) + ), + $factory->boolean( + 'bbpress_override_enabled', + 'Prevent bbPress from resetting role capabilities', + array( + 'description' => 'By default, bbPress will automatically undo any ' + . 'changes that are made to dynamic bbPress roles. Enable this ' + . 'option to override that behaviour and make it possible to ' + . 'change bbPress role capabilities.', + ) + ), + $factory->enum( + 'deep_nesting_enabled', + array(null, true, false), + 'Three level menus', + array('default' => null) + ) + ->describeChoice(null, 'Ask on first use') + ->describeChoice(true, 'Enabled' . ($isProVersion ? '' : ' (only in editor)')) + ->describeChoice(false, 'Disabled'), + $factory->custom( + 'is_active_module', + 'array', + function ($inputValue) use ($menuEditor) { + if ( empty($inputValue) ) { + return array(); + } + + //Convert to [$moduleId => $enabled]. + $activeModules = (array)$inputValue; + $activeModules = array_fill_keys(array_map('strval', $activeModules), true); + + //Filter out modules that are invalid or not installed. + $availableModules = $menuEditor->get_available_modules(); + $activeModules = array_intersect_key($activeModules, $availableModules); + + //Explicitly set disabled modules to false. + return array_merge( + array_map('__return_false', $availableModules), + $activeModules + ); + }, + 'Modules' + ), + ); + + //Index settings by ID. + $result = array(); + foreach ($settings as $setting) { + $result[$setting->getId()] = $setting; + } + return $result; + } +} + +class AmeHidePluginSetting extends BooleanSetting { + const SETTING_KEY = 'plugins_page_allowed_user_id'; + + protected $defaultValue = false; + + public function __construct($id, StorageInterface $store = null, $params = array()) { + $isProVersion = self::isProVersion(); + if ( !isset($params['label']) ) { + $label = 'Hide "Admin Menu Editor' . ($isProVersion ? ' Pro' : '') . '"'; + if ( defined('WS_ADMIN_BAR_EDITOR_FILE') || defined('AME_BRANDING_ADD_ON_FILE') ) { + $label .= ' and its add-ons '; + } + $label .= ' from the "Plugins" page for other users'; + if ( !$isProVersion ) { + $label .= ' (Pro version only)'; + } + $params['label'] = $label; + } + + parent::__construct($id, $store, $params); + } + + public function getValue($customDefault = null) { + /** @noinspection PhpRedundantOptionalArgumentInspection */ + $userId = $this->store->getPath(self::SETTING_KEY, null); + return ($userId !== null); + } + + public function update($validValue) { + if ( !$this->store ) { + return false; + } + $success = $this->store->setPath( + self::SETTING_KEY, + $validValue ? get_current_user_id() : null + ); + $this->notifyUpdated(); + return $success; + } + + public function isEditableByUser() { + if ( !self::isProVersion() ) { + return false; + } + + if ( is_multisite() ) { + $allowed = is_super_admin(); + } else { + $allowed = current_user_can('manage_options'); + } + return $allowed && parent::isEditableByUser(); + } + + protected static function isProVersion() { + static $isPro = null; + if ( $isPro === null ) { + $isPro = apply_filters('admin_menu_editor_is_pro', false); + } + return $isPro; + } +} \ No newline at end of file diff --git a/customizables/SettingCondition.php b/customizables/SettingCondition.php new file mode 100644 index 0000000..94c241a --- /dev/null +++ b/customizables/SettingCondition.php @@ -0,0 +1,121 @@ + true, + '!=' => true, + '>' => true, + '<' => true, + '>=' => true, + '<=' => true, + 'truthy' => true, + 'falsy' => true, + ]; + + const EQUALS = '=='; + const IS_TRUTHY = 'truthy'; + const IS_FALSY = 'falsy'; + + /** + * @var AbstractSetting + */ + protected $setting; + + /** + * @var mixed + */ + protected $comparisonValue; + + /** + * @var string + */ + protected $comparisonOperator; + + public function __construct( + AbstractSetting $setting, + $comparisonOperator, + $comparisonValue + ) { + if ( !array_key_exists($comparisonOperator, self::VALID_OPERATORS) ) { + throw new \InvalidArgumentException('Invalid comparison operator: ' . $comparisonOperator); + } + + $this->setting = $setting; + $this->comparisonValue = $comparisonValue; + $this->comparisonOperator = $comparisonOperator; + } + + /** + * Perform the comparison and return true if the current value of the setting + * matches the specified comparison value. + * + * @return bool + */ + public function evaluate() { + $settingValue = $this->setting->getValue(); + switch ($this->comparisonOperator) { + case '==': + //Note: Intentionally using loose comparison here. + return $settingValue == $this->comparisonValue; + case '!=': + return $settingValue != $this->comparisonValue; + case '>': + return $settingValue > $this->comparisonValue; + case '<': + return $settingValue < $this->comparisonValue; + case '>=': + return $settingValue >= $this->comparisonValue; + case '<=': + return $settingValue <= $this->comparisonValue; + case 'truthy': + return !empty($settingValue); + case 'falsy': + return empty($settingValue); + default: + throw new \InvalidArgumentException('Invalid comparison operator: ' . $this->comparisonOperator); + } + } + + public function __invoke() { + return $this->evaluate(); + } + + /** + * Generate a JavaScript expression that performs the comparison. + * Intended to be used in KnockoutJS bindings. + * + * @return string + */ + public function getJsKoExpression() { + $observableExpr = sprintf( + '($root.getSettingObservable(%s, %s)())', + wp_json_encode($this->setting->getId()), + wp_json_encode(null) + ); + + if ( $this->comparisonOperator === self::IS_TRUTHY ) { + return sprintf('(!!%s)', $observableExpr); + } else if ( $this->comparisonOperator === self::IS_FALSY ) { + return sprintf('(!%s)', $observableExpr); + } + + return sprintf( + '(%s %s %s)', + $observableExpr, + $this->comparisonOperator, + wp_json_encode($this->comparisonValue) + ); + } + + public function serializeForJs() { + return [ + 'settingId' => $this->setting->getId(), + 'op' => $this->comparisonOperator, + 'value' => $this->comparisonValue, + ]; + } +} \ No newline at end of file diff --git a/customizables/Settings/AbstractSetting.php b/customizables/Settings/AbstractSetting.php new file mode 100644 index 0000000..847ded0 --- /dev/null +++ b/customizables/Settings/AbstractSetting.php @@ -0,0 +1,543 @@ + List of tags that can be used to group settings. + */ + protected $tags = []; + + public function __construct($id, StorageInterface $store = null, $params = []) { + parent::__construct($id, $store, $params); + + if ( isset($params['isEditable']) && is_callable($params['isEditable']) ) { + $this->isEditableCallback = $params['isEditable']; + } + if ( isset($params['deleteWhenBlank']) ) { + $this->deleteWhenBlank = (bool)$params['deleteWhenBlank']; + } + if ( isset($params['supportsPostMessage']) ) { + $this->supportsPostMessage = (bool)$params['supportsPostMessage']; + } + if ( isset($params['tags']) ) { + $this->tags = $params['tags']; + } + } + + /** + * Validate and sanitize a setting value. + * + * On success, this method returns the sanitized value. If there is + * a validation error, it returns a WP_Error instance instead. + * + * @param \WP_Error $errors + * @param array|mixed $value + * @param bool $stopOnFirstError Only applies to settings that have children. Other settings may ignore this parameter. + * @return \WP_Error|mixed + */ + abstract public function validate($errors, $value, $stopOnFirstError = false); + + /** + * Update the value of this setting. + * + * This method assumes that the value has already been validated and sanitized, + * and that any applicable permissions have been checked. + * + * This may not immediately write the new value to the database. Call the save() + * method on the underlying storage to ensure that the value is saved. + * + * @param $validValue + * @return boolean + */ + abstract public function update($validValue); + + /** + * @param mixed $customDefault + * @return mixed + */ + abstract public function getValue($customDefault = null); + + /** + * @return mixed + */ + abstract public function getDefaultValue(); + + /** + * Enable preview mode for the current request. This will make the setting + * return the specified value instead of its actual value. + * + * Note: Usually, the preview value will be validated before it's passed + * to this method. However, in some cases, a value can be sent to the preview + * frame before it's saved in the changeset, so it will only have undergone + * JS-based validation, not full server-side validation. This means it's + * a good idea to validate the preview value even if that will sometimes + * duplicate work that has already been done. + * + * Also, even a value that has already been validated and saved in a changeset + * can occasionally become invalid later. For example, an image could be deleted + * from the media library. In exceptional cases, validation rules themselves + * could change as part of a plugin update. + * + * @param $unsafeValue + * @param \WP_Error|null $errors Optional. To avoid the creation of temporary + * WP_Error instances during value validation, you can provide an existing error + * object to this method. + * + * @return void + */ + public function preview($unsafeValue, $errors = null) { + if ( $errors === null ) { + $errors = new WP_Error(); + } + + $validationResult = $this->validate($errors, $unsafeValue, true); + if ( is_wp_error($validationResult) ) { + $previewValue = $this->getDefaultValue(); + } else { + $previewValue = $validationResult; + } + + if ( $this->store ) { + $this->store->setPreviewValue($previewValue); + } + } + + /** + * Validate a value that has been submitted via an HTML form. + * + * For most settings, this is simply an alias for the `validate()` method. + * However, some settings may contain values that can't be directly represented + * in an HTML form, like `null` or objects. Values like that will need to be + * encoded/decoded when used in HTML. This method provides a way to decode + * and validate submitted form values. + * + * When the data comes from a form, you should use this method instead + * of `validate()` to ensure that the data is properly decoded. + * + * @param \WP_Error $errors + * @param array|mixed $value + * @param bool $stopOnFirstError + * @return \WP_Error|mixed + */ + public function validateFormValue($errors, $value, $stopOnFirstError = false) { + return $this->validate($errors, $value, $stopOnFirstError); + } + + public function getDataType() { + return $this->dataType; + } + + public function getRecommendedControls() { + return $this->recommendedControls; + } + + /** + * Is the current user allowed to change this setting? + * + * @return bool + */ + public function isEditableByUser() { + if ( isset($this->isEditableCallback) ) { + return call_user_func($this->isEditableCallback); + } + return true; + } + + /** + * Is it currently OK to delete this setting from storage? + * + * For example, some settings may choose to be removed when their value + * is empty, NULL, or equal to the default value. + * + * @return bool + */ + public function canBeDeleted() { + if ( $this->deleteWhenBlank ) { + $value = $this->getValue(); + if ( ($value === null) || ($value === '') ) { + return true; + } else if ( is_array($value) && empty($value) ) { + return true; + } + } + return false; + } + + /** + * Apply one or more validators to a setting value. + * + * Each validator should be a callable that takes two arguments: + * - The value to validate + * - A WP_Error object to which validation errors should be added. + * + * The callable should return the validated value, or a WP_Error instance + * if there was a validation error. + * + * @param callable[] $validators + * @param mixed $value + * @param \WP_Error $errors + * @param boolean $stopOnFirstError + * @return mixed|\WP_Error + */ + protected static function applyValidators($validators, $value, $errors, $stopOnFirstError = false) { + $convertedValue = $value; + $hasErrors = false; + + foreach ($validators as $validator) { + $result = call_user_func($validator, $convertedValue, $errors); + if ( is_wp_error($result) ) { + $hasErrors = true; + if ( $stopOnFirstError ) { + return $result; + } + } else { + $convertedValue = $result; + } + } + + return $hasErrors ? $errors : $convertedValue; + } + + /** + * Notify the setting that its value has been updated. + * + * Usually, the setting itself will call this method from its update() method. + * If you change the value without calling the update() method, such as by + * directly updating a child of a struct, you should call this method manually. + * + * @return void + */ + public function notifyUpdated() { + if ( $this->store && $this->canBeDeleted() ) { + $this->store->deleteValue(); + } + + //Disallow recursive notifications as it could lead to infinite loops. + //If necessary, we can change this later. + if ( !$this->isNotifyingSubscribers ) { + $this->isNotifyingSubscribers = true; + + //We'll notify subscribers now, so remove the setting from the notification + //queue. This can be redundant if this method gets called while processing + //the queue, but it's probably not a big deal. + if ( static::$updateNotificationQueue ) { + static::$updateNotificationQueue->remove($this); + } + + foreach ($this->updateSubscribers as $callback) { + call_user_func($callback, $this); + } + $this->isNotifyingSubscribers = false; + } + } + + /** + * @param callable $callback + * @return void + */ + public function subscribe($callback) { + $this->updateSubscribers[] = $callback; + } + + /** + * @return bool + */ + public function supportsPostMessage() { + return $this->supportsPostMessage; + } + + public function enablePostMessageSupport() { + $this->supportsPostMessage = true; + return $this; + } + + //region Tags + + /** + * @return string[] + */ + public function getTags() { + return $this->tags; + } + + /** + * @param string[] $tags + * @return $this + */ + public function addTags(...$tags) { + $this->tags = array_unique(array_merge($this->tags, $tags)); + return $this; + } + + /** + * @param string $tag + * @return bool + */ + public function hasTag($tag) { + return in_array($tag, $this->tags); + } + + //endregion + + /** + * @return array|null + */ + public function serializeValidationRules() { + return null; //It's up to subclasses to implement this. + } + + protected static function getNotificationQueue() { + if ( !static::$updateNotificationQueue ) { + static::$updateNotificationQueue = new UniqueSettingQueue(); + } + return static::$updateNotificationQueue; + } + + public static function sendPendingNotifications() { + if ( !static::$updateNotificationQueue ) { + return; + } + + $queue = static::$updateNotificationQueue; + $queue->processAll(); + } + + /** + * @param AbstractSetting[] $settings + * @return void + */ + public static function saveAll($settings) { + static::sendPendingNotifications(); + + //Find and deduplicate the stores that contain these settings. + $stores = new \SplObjectStorage(); + foreach ($settings as $setting) { + if ( $setting->store ) { + $stores->attach($setting->store->getSmallestSavable()); + } + } + + //Tell each store to save its data. + foreach ($stores as $store) { + /** @var StorageInterface $store */ + $store->save(); + } + } + + /** + * @param AbstractSetting[] $settingsToWatch + * @param callable $callback Expected signature: function($updatedSettings) => void + */ + public static function subscribeDeferred($settingsToWatch, $callback) { + if ( empty($settingsToWatch) ) { + return; + } + + $queue = static::getNotificationQueue(); + new DeferredUpdateSubscriber($queue, $settingsToWatch, $callback); + //The subscriber object doesn't need to be stored anywhere because it will + //automatically add itself as a subscriber to each setting. + } + + /** + * Recursively iterate over a collection of settings. + * + * This method will not recurse into composite settings, but it will return + * the children of regular structs. + * + * @param iterable $settings + * @param bool $leavesOnly When encountering a struct setting, only return its + * children and not the struct itself. Does not apply + * to composite settings. + * @param string|int|null $parentKey The key of the parent setting. Used to generate + * an iterator key for each setting. + * @return \Generator + */ + public static function recursivelyIterateSettings($settings, $leavesOnly = false, $parentKey = null) { + foreach ($settings as $key => $setting) { + if ( $parentKey !== null ) { + $effectiveKey = ((string)$parentKey) . '.' . $key; + } else { + $effectiveKey = $key; + } + + //Descend into structs and arrays, except composite settings. + //WP 4.9.6+ includes a polyfill for is_iterable(). + $isContainer = is_iterable($setting) && !($setting instanceof CompositeSetting); + + if ( ($setting instanceof AbstractSetting) && (!$isContainer || !$leavesOnly) ) { + yield $effectiveKey => $setting; + } + + if ( $isContainer ) { + /** @var iterable $setting */ + yield from self::recursivelyIterateSettings($setting, $leavesOnly, $effectiveKey); + } + } + } + + /** + * Recursively serialize a collection of settings for use in JavaScript. + * + * Optionally, you can provide a callback that will be called for each setting. + * It can be used to modify the serialized data. The callback will be called + * with two arguments: + * - The serialized data as an associative array. + * - The setting object. + * + * The callback should return an associative array. Alternatively, it can return + * `null` to exclude the setting from the result. + * + * @param AbstractSetting[] $settings + * @param int|null $flags + * @param callable $customizer Optional. A callback that can be used to modify each + * setting's serialized data. + * @return array A map of setting IDs to serialized settings. + */ + public static function serializeSettingsForJs( + $settings, + $flags = self::SERIALIZE_INCLUDE_ALL, + $customizer = null + ) { + //Right now, the serialization process is fairly straightforward, so we + //just do it here. If it becomes more complex, we could add a serializeForJs() + //method to individual settings, or add a separate serializer class. + if ( $flags === null ) { + $flags = self::SERIALIZE_INCLUDE_ALL; + } + + $leavesOnly = (bool)($flags & self::SERIALIZE_LEAVES_ONLY); + + $serialized = []; + foreach (self::recursivelyIterateSettings($settings, $leavesOnly) as $setting) { + $emptyArraysAsObjects = ($setting->getDataType() === 'map'); + + $data = []; + if ( $flags & self::SERIALIZE_INCLUDE_ID ) { + $data['id'] = $setting->id; + } + if ( $flags & self::SERIALIZE_INCLUDE_DEFAULT ) { + $data['default'] = $setting->getDefaultValue(); + if ( $emptyArraysAsObjects && is_array($data['default']) && empty($data['default']) ) { + $data['default'] = (object)$data['default']; + } + } + if ( $flags & self::SERIALIZE_INCLUDE_VALUE ) { + //Note: This will use the previewed value if one is available. + $data['value'] = $setting->getValue(); + if ( $emptyArraysAsObjects && is_array($data['value']) && empty($data['value']) ) { + $data['value'] = (object)$data['value']; + } + } + + if ( $flags & self::SERIALIZE_INCLUDE_GROUP_TITLE ) { + $groupTitle = $setting->getCustomGroupTitle(); + if ( ($groupTitle !== null) && ($groupTitle !== '') ) { + $data['groupTitle'] = $groupTitle; + } + } + + if ( $flags & self::SERIALIZE_INCLUDE_POST_MESSAGE_SUPPORT ) { + if ( $setting->supportsPostMessage() ) { + $data['supportsPostMessage'] = true; + } + } + + if ( $flags & self::SERIALIZE_INCLUDE_TAGS ) { + $tags = $setting->getTags(); + if ( !empty($tags) ) { + $data['tags'] = $tags; + } + } + + if ( $flags & self::SERIALIZE_INCLUDE_VALIDATION ) { + $validationRules = $setting->serializeValidationRules(); + if ( !empty($validationRules) ) { + $data['validation'] = $validationRules; + } + } + + if ( $customizer ) { + $data = call_user_func($customizer, $data, $setting); + //Skip settings excluded by the callback. + if ( $data === null ) { + continue; + } + } + + $serialized[$setting->id] = $data; + } + return $serialized; + } +} \ No newline at end of file diff --git a/customizables/Settings/AbstractStructSetting.php b/customizables/Settings/AbstractStructSetting.php new file mode 100644 index 0000000..b134e56 --- /dev/null +++ b/customizables/Settings/AbstractStructSetting.php @@ -0,0 +1,276 @@ + + */ + protected $settings = []; + + /** + * @var callable + */ + protected $childUpdateCallback; + protected $childSubscriptionsAdded = false; + protected $isInUpdateLoop = false; + + public function __construct($id, StorageInterface $store = null, $params = []) { + //Minor optimization: Create the callback array once and reuse it for every child. + $this->childUpdateCallback = [$this, 'notifyChildWasUpdated']; + + if ( $store === null ) { + $store = new Storage\NullStorage(); + } + parent::__construct($id, $store, $params); + } + + /** + * @param \WP_Error $errors + * @param array $value + * @return \WP_Error|array + */ + public function validate($errors, $value, $stopOnFirstError = false) { + if ( !is_array($value) ) { + $errors->add('struct_value_invalid', 'Struct value must be an associative array'); + return $errors; + } + + $validatedValues = []; + $foundErrors = false; + foreach ($this->settings as $key => $setting) { + if ( array_key_exists($key, $value) ) { + $validity = $setting->validate($errors, $value[$key], $stopOnFirstError); + if ( is_wp_error($validity) ) { + $foundErrors = true; + if ( $stopOnFirstError ) { + break; + } + } else { + $validatedValues[$key] = $validity; + } + } + } + + if ( $foundErrors ) { + return $errors; + } else { + return $validatedValues; + } + } + + public function update($validValue) { + $this->isInUpdateLoop = true; + + $isSuccess = true; + foreach ($this->settings as $key => $setting) { + if ( array_key_exists($key, $validValue) ) { + $isSuccess = $isSuccess && $setting->update($validValue[$key]); + } + } + + $this->isInUpdateLoop = false; + $this->notifyUpdated(); + + return $isSuccess; + } + + public function getValue($customDefault = []) { + $result = is_array($customDefault) ? $customDefault : []; + foreach ($this->settings as $key => $setting) { + $result[$key] = $setting->getValue(); + } + return $result; + } + + public function preview($unsafeValue, $errors = null) { + if ( !is_array($unsafeValue) ) { + return; + } + if ( $errors === null ) { + $errors = new \WP_Error(); + } + + foreach ($this->settings as $key => $setting) { + if ( array_key_exists($key, $unsafeValue) ) { + $setting->preview($unsafeValue[$key], $errors); + } + } + } + + /** + * @return array + */ + public function getDefaultValue() { + return []; + } + + public function canBeDeleted() { + if ( $this->deleteWhenBlank ) { + //In addition to other "blank" states, a struct can also be deleted + //if it has no children or if all of its children can be deleted. + $isDeletable = true; + foreach ($this->settings as $setting) { + if ( !$setting->canBeDeleted() ) { + $isDeletable = false; + break; + } + } + if ( $isDeletable ) { + return true; + } + } + return parent::canBeDeleted(); + } + + public function subscribe($callback) { + parent::subscribe($callback); + + //Optimization: Listen for child updates only if we have subscribers. + if ( !empty($this->updateSubscribers) && !$this->childSubscriptionsAdded ) { + $this->childSubscriptionsAdded = true; + foreach ($this->settings as $setting) { + $setting->subscribe($this->childUpdateCallback); + } + } + } + + /** + * @internal This method needs to be public because it's used as a callback, + * but you should not call it directly. + * + * @noinspection PhpUnusedParameterInspection In the current implementation, + * we don't care which specific child was updated, only that one was. + * + * @param AbstractSetting $childSetting + * @return void + */ + public function notifyChildWasUpdated(AbstractSetting $childSetting) { + //If we're inside the foreach loop in update(), we don't need to notify + //our subscribers here - update() will do it after the loop. + //Also, we currently don't support recursive notifications, so if a child + //is updated while we're sending notifications, we'll just ignore it. + if ( $this->isInUpdateLoop || $this->isNotifyingSubscribers ) { + return; + } + + //Optimization: Multiple children can be updated at the same time, and + //it would be inefficient to notify the parent's subscribers every time. + //Instead, we'll put the parent in a queue. The code performing the update + //should send pending notifications when it's done. + static::getNotificationQueue()->enqueue($this); + } + + /** + * @param string $key + * @return AbstractSetting|null + */ + public function getChild($key) { + if ( array_key_exists($key, $this->settings) ) { + return $this->settings[$key]; + } + return null; + } + + public function getChildValue($childSettingKey, $defaultValue = null) { + if ( array_key_exists($childSettingKey, $this->settings) ) { + return $this->settings[$childSettingKey]->getValue($defaultValue); + } + return $defaultValue; + } + + public function makeChildId($childKey) { + return $this->id . '.' . $childKey; + } + + /** + * Create a child setting of the specified type. + * + * @param string $childKey + * @param class-string $className + * @param ...$constructorParams + * @return AbstractSetting + */ + protected function createChild($childKey, $className, ...$constructorParams) { + $child = new $className( + $this->makeChildId($childKey), + $this->store->buildSlot($childKey), + ...$constructorParams + ); + if ( $this->shouldEnablePostMessageForChildren() ) { + $child->enablePostMessageSupport(); + } + + //Children inherit the parent's tags. + $child->addTags(...$this->tags); + + $this->registerChild($childKey, $child); + + return $child; + } + + protected function registerChild($childKey, AbstractSetting $child) { + $this->settings[$childKey] = $child; + + if ( $this->childSubscriptionsAdded ) { + $child->subscribe($this->childUpdateCallback); + } + } + + public function enablePostMessageSupport() { + parent::enablePostMessageSupport(); + + if ( $this->shouldEnablePostMessageForChildren() ) { + foreach ($this->settings as $setting) { + $setting->enablePostMessageSupport(); + } + } + } + + protected function shouldEnablePostMessageForChildren() { + return $this->supportsPostMessage; + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) { + return array_key_exists($offset, $this->settings); + } + + /** + * @param string $offset + * @return AbstractSetting|null + * @noinspection PhpLanguageLevelInspection + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) { + return $this->getChild($offset); + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) { + throw new \LogicException( + 'Cannot add or replace a child of a struct. The setting list is read-only.' + ); + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset) { + throw new \LogicException( + 'Cannot remove a child of a struct. The setting list is read-only.' + ); + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function getIterator() { + return new \ArrayIterator($this->settings); + } +} \ No newline at end of file diff --git a/customizables/Settings/BooleanSetting.php b/customizables/Settings/BooleanSetting.php new file mode 100644 index 0000000..52cb839 --- /dev/null +++ b/customizables/Settings/BooleanSetting.php @@ -0,0 +1,34 @@ +canTreatAsNull($value) ) { + return null; + } + + $value = $this->tryConvertToBool($value); + if ( $value === null ) { + $errors->add('not_boolean', 'Value must be a boolean (true or false)'); + return $errors; + } + return $value; + } + + /** + * @param mixed $value + * @return bool|null + */ + protected function tryConvertToBool($value) { + if ( is_string($value) ) { + //Handle values like "on", "off", "false", etc. + return filter_var(strtolower($value), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } else if ( $value !== null ) { + return boolval($value); + } + return null; + } +} \ No newline at end of file diff --git a/customizables/Settings/ColorSetting.php b/customizables/Settings/ColorSetting.php new file mode 100644 index 0000000..4e394b1 --- /dev/null +++ b/customizables/Settings/ColorSetting.php @@ -0,0 +1,31 @@ +canTreatAsNull($value) ) { + return null; + } + + if ( !is_string($value) ) { + $errors->add('invalid_color_string', 'Value must be a string'); + return $errors; + } + + $value = trim($value); + //Allow either 3 or 6 hex digits, but nothing in between. + //Alpha is technically allowed, but the WP color picker doesn't support it. + if ( !preg_match('/^#(?:[\da-f]{6}|[\da-f]{3})$/i', $value) ) { + $errors->add('invalid_hex_color', 'Value must be a valid CSS hex color'); + return $errors; + } + return $value; + } +} \ No newline at end of file diff --git a/customizables/Settings/CompositeSetting.php b/customizables/Settings/CompositeSetting.php new file mode 100644 index 0000000..e59eb84 --- /dev/null +++ b/customizables/Settings/CompositeSetting.php @@ -0,0 +1,53 @@ +filterNewValues($validValue); + return parent::update($validValue); + } + + /** + * This should be called after validation and sanitization but before the underlying + * settings are updated or saved. + * + * @param array $values + * @return array + */ + protected function filterNewValues($values) { + return $values; + } + + public function preview($unsafeValue, $errors = null) { + if ( $errors === null ) { + $errors = new \WP_Error(); + } + + //Unlike a general struct, composite settings are all-or-nothing: if any + //children fail validation, all preview values are disregarded. + $validationResult = $this->validate($errors, $unsafeValue, true); + if ( is_wp_error($validationResult) ) { + $previewValues = []; + } else { + $previewValues = $validationResult; + } + $previewValues = $this->filterNewValues($previewValues); + + foreach ($this->settings as $key => $setting) { + //Note: In this implementation, child settings might get validated twice. + //because preview() will typically validate the value again. + if ( array_key_exists($key, $previewValues) ) { + $setting->preview($previewValues[$key], $errors); + } else { + $setting->preview(null, $errors); + } + } + } + + protected function shouldEnablePostMessageForChildren() { + //A composite settings should be updated or previewed as a whole, + //so we don't need to enable postMessage for its children. + return false; + } +} \ No newline at end of file diff --git a/customizables/Settings/DeferredUpdateSubscriber.php b/customizables/Settings/DeferredUpdateSubscriber.php new file mode 100644 index 0000000..3452a67 --- /dev/null +++ b/customizables/Settings/DeferredUpdateSubscriber.php @@ -0,0 +1,57 @@ + + */ + protected $updatedSettings; + + /** + * @var callable + */ + protected $callback; + + /** + * @var UniqueSettingQueue + */ + protected $ownerQueue; + + /** + * @param UniqueSettingQueue $ownerQueue + * @param AbstractSetting[] $watchedSettings + * @param callable $callback + */ + public function __construct(UniqueSettingQueue $ownerQueue, $watchedSettings, callable $callback) { + $this->updatedSettings = new \SplObjectStorage(); + $this->callback = $callback; + $this->ownerQueue = $ownerQueue; + + $settingUpdateHandler = [$this, 'receiveNotification']; + foreach ($watchedSettings as $setting) { + $setting->subscribe($settingUpdateHandler); + } + } + + public function receiveNotification(AbstractSetting $setting) { + if ( !$this->updatedSettings->contains($setting) ) { + $this->updatedSettings->attach($setting, true); + $this->ownerQueue->enqueueDeferred($this); + } + } + + public function notifyUpdated() { + $settingsAsArray = []; + foreach ($this->updatedSettings as $setting) { + $settingsAsArray[] = $setting; + } + $this->updatedSettings = new \SplObjectStorage(); + + //Remove this object from the queue in case notifyUpdated() was called + //directly instead of via the queue. + $this->ownerQueue->remove($this); + + call_user_func($this->callback, $settingsAsArray); + } +} \ No newline at end of file diff --git a/customizables/Settings/EnumSetting.php b/customizables/Settings/EnumSetting.php new file mode 100644 index 0000000..cb56c64 --- /dev/null +++ b/customizables/Settings/EnumSetting.php @@ -0,0 +1,149 @@ +enumValues = array_values($enumValues); + + if ( !in_array($this->defaultValue, $this->enumValues) ) { + $this->defaultValue = reset($this->enumValues); + } + } + + public function encodeForForm($value) { + return wp_json_encode($value); + } + + public function decodeSubmittedValue($value) { + if ( is_string($value) ) { + return @json_decode($value, true); + } + return parent::decodeSubmittedValue($value); + } + + public function validate($errors, $value, $stopOnFirstError = false) { + if ( $this->canTreatAsNull($value) ) { + return null; + } + + if ( !in_array($value, $this->enumValues) ) { + $errors->add( + 'invalid_value', + 'Value must be one of: ' . implode(', ', $this->enumValues) + . '. Received: ' . wp_json_encode($value) + ); + return $errors; + } + + if ( !$this->isChoiceEnabled($value) ) { + $errors->add('disabled_value', 'That option is currently not allowed'); + return $errors; + } + + return $value; + } + + public function isChoiceEnabled($value) { + if ( !in_array($value, $this->enumValues) ) { + return false; + } + + $safeValue = $this->encodeForForm($value); + if ( isset($this->valueEnabled[$safeValue]) ) { + $decider = $this->valueEnabled[$safeValue]; + if ( is_scalar($decider) ) { + return (bool)$decider; + } elseif ( is_callable($decider) ) { + return call_user_func($decider, $value); + } + } + + if ( isset($this->valueStateCallback) ) { + return call_user_func($this->valueStateCallback, $value); + } + return true; + } + + /** + * @param mixed $value + * @param string|null $label + * @param string|null $description + * @param bool|callable|null $state + * @param string|null $icon + * @return EnumSetting + */ + public function describeChoice($value, $label, $description = '', $state = null, $icon = null) { + $safeValue = $this->encodeForForm($value); + $this->choiceDetails[$safeValue] = array( + 'label' => $label, + 'description' => $description, + 'icon' => $icon, + ); + if ( $state !== null ) { + $this->valueEnabled[$safeValue] = $state; + } + return $this; + } + + /** + * Automatically generate dropdown/radio/etc options from the setting's + * possible values. + * + * Will use custom labels/descriptions if available. + * + * @return \YahnisElsts\AdminMenuEditor\Customizable\Controls\ChoiceControlOption[] + */ + public function generateChoiceOptions() { + $results = array(); + foreach ($this->enumValues as $value) { + $encodedValue = $this->encodeForForm($value); + if ( array_key_exists($encodedValue, $this->choiceDetails) ) { + $results[] = new ChoiceControlOption( + $value, + $this->choiceDetails[$encodedValue]['label'], + array( + 'description' => $this->choiceDetails[$encodedValue]['description'], + 'enabled' => $this->isChoiceEnabled($value), + 'icon' => $this->choiceDetails[$encodedValue]['icon'], + ) + ); + } else { + if ( $value === null ) { + $label = 'Default'; + } else { + $label = is_string($value) ? $value : wp_json_encode($value); + $label = ucwords(preg_replace('/[_-]+/', ' ', $label)); + } + $results[] = new ChoiceControlOption($value, $label, array( + 'enabled' => $this->isChoiceEnabled($value), + )); + } + } + + return $results; + } +} \ No newline at end of file diff --git a/customizables/Settings/FloatSetting.php b/customizables/Settings/FloatSetting.php new file mode 100644 index 0000000..41bdb08 --- /dev/null +++ b/customizables/Settings/FloatSetting.php @@ -0,0 +1,7 @@ +externalUrlsAllowed = $params['externalUrlsAllowed']; + } + + //Image attachment ID. + $this->createChild( + 'attachmentId', + IntegerSetting::class, + array('default' => 0, 'minValue' => 0) + ); + + //Site ID for Multisite. + $this->createChild( + 'attachmentSiteId', + IntegerSetting::class, + array('default' => 0, 'minValue' => 0) + ); + + //Cached attachment URL. + $this->createChild( + 'attachmentUrl', + UrlSetting::class, + array('default' => null) + ); + + //TODO: Allow shortcodes in the URL. This will require more lenient validation (also in JS). + //External image URL. Alternative to attachments. + $this->createChild( + 'externalUrl', + UrlSetting::class, + array('default' => null) + ); + + //Cached width and height for features that need them. + $this->createChild( + 'width', + IntegerSetting::class, + array('default' => null, 'minValue' => 0) + ); + $this->createChild( + 'height', + IntegerSetting::class, + array('default' => null, 'minValue' => 0) + ); + } + + /** + * Get a fully qualified URL for the image. + * + * @return string|null + */ + public function getImageUrl($useCachedDetails = true) { + //Try the external URL. + $externalUrl = $this->settings['externalUrl']->getValue(); + if ( !empty($externalUrl) ) { + return $externalUrl; + } + + if ( $useCachedDetails ) { + //Try the cached attachment URL. + $attachmentUrl = $this->settings['attachmentUrl']->getValue(); + if ( !empty($attachmentUrl) ) { + return $attachmentUrl; + } + } + + $result = $this->getImage($useCachedDetails); + return $result['url']; + } + + /** + * Get the URL and dimensions of the image. + * + * If there is no image, returns an array of NULLs. + * + * @return array{url: string, width: int, height: int} + */ + public function getImage($useCachedDetails = true) { + //Prioritize external URLs over attachments. + $externalUrl = $this->settings['externalUrl']->getValue(); + if ( !empty($externalUrl) ) { + return array( + 'url' => $externalUrl, + 'width' => $this->settings['width']->getValue(0), + 'height' => $this->settings['height']->getValue(0), + ); + } + + $attachmentId = $this->settings['attachmentId']->getValue(0); + $siteId = $this->settings['attachmentSiteId']->getValue(0); + if ( ($attachmentId > 0) ) { + if ( $useCachedDetails ) { + $attachmentUrl = $this->settings['attachmentUrl']->getValue(); + $width = $this->settings['width']->getValue(0); + $height = $this->settings['height']->getValue(0); + } + + if ( + //If caching is disabled + !$useCachedDetails + //Or any of the cached details are missing... + || (empty($attachmentUrl) || empty($width) || empty($height)) + ) { + //Load the attachment from the database. + list($attachmentUrl, $width, $height) = $this->fetchImageAttachment($attachmentId, $siteId); + } + if ( !empty($attachmentUrl) ) { + return array( + 'url' => $attachmentUrl, + 'width' => $width, + 'height' => $height, + ); + } + } + + return array('url' => null, 'width' => null, 'height' => null,); + } + + public function validate($errors, $value, $stopOnFirstError = false) { + $validatedValues = parent::validate($errors, $value); + if ( is_wp_error($validatedValues) || ($validatedValues === null) ) { + return $validatedValues; + } + + //Validate an image attachment. + $attachmentId = isset($validatedValues['attachmentId']) ? intval($validatedValues['attachmentId']) : 0; + $siteId = isset($validatedValues['attachmentSiteId']) ? intval($validatedValues['attachmentSiteId']) : 0; + if ( $attachmentId > 0 ) { + $switched = false; + if ( is_multisite() && ($siteId > 0) && ($siteId !== get_current_blog_id()) ) { + switch_to_blog($siteId); + $switched = true; + } + + if ( !wp_attachment_is_image($attachmentId) ) { + $errors->add('invalid_attachment_type', 'Attachment must be a valid image'); + } + + if ( $switched ) { + restore_current_blog(); + } + } + + if ( $errors->has_errors() ) { + return $errors; + } + return $validatedValues; + } + + /** + * @param int $attachmentId + * @param int $siteId + * @return array{0: string, 1: int, 2: int} URL, width, height. All NULLs if no image. + */ + protected function fetchImageAttachment($attachmentId, $siteId) { + if ( !is_numeric($attachmentId) || ($attachmentId < 0) ) { + return array(null, null, null); + } + + $switched = false; + if ( is_multisite() && ($siteId > 0) && ($siteId !== get_current_blog_id()) ) { + switch_to_blog($siteId); + $switched = true; + } + + $attachment = wp_get_attachment_image_src($attachmentId, 'full'); + + if ( $switched ) { + restore_current_blog(); + } + + if ( empty($attachment) || (count($attachment) < 3) ) { + return array(null, null, null); + } + return array_slice($attachment, 0, 3); + } + + public function filterNewValues($values) { + $values = parent::filterNewValues($values); + + //If we're using an attachment, cache its size and URL. + $attachmentId = isset($values['attachmentId']) ? intval($values['attachmentId']) : 0; + $siteId = isset($values['attachmentSiteId']) ? intval($values['attachmentSiteId']) : 0; + if ( $attachmentId > 0 ) { + list($url, $width, $height) = $this->fetchImageAttachment($attachmentId, $siteId); + if ( !empty($url) && isset($width, $height) ) { + $values['width'] = $width; + $values['height'] = $height; + $values['attachmentUrl'] = $url; + } + } + + return $values; + } + + public function areExternalUrlsAllowed() { + return $this->externalUrlsAllowed; + } +} \ No newline at end of file diff --git a/customizables/Settings/IntegerSetting.php b/customizables/Settings/IntegerSetting.php new file mode 100644 index 0000000..3e45c47 --- /dev/null +++ b/customizables/Settings/IntegerSetting.php @@ -0,0 +1,31 @@ +add('not_integer', 'Value must be an integer'); + return $errors; + } + + return intval($value); + } + + public function serializeValidationRules() { + $result = parent::serializeValidationRules(); + if ( !isset($result['parsers']) ) { + $result['parsers'] = []; + } + $result['parsers'][] = ['int']; + return $result; + } +} \ No newline at end of file diff --git a/customizables/Settings/MapSetting.php b/customizables/Settings/MapSetting.php new file mode 100644 index 0000000..8364c34 --- /dev/null +++ b/customizables/Settings/MapSetting.php @@ -0,0 +1,58 @@ +keyValidators = $params['keyValidators']; + } + if ( array_key_exists('value_validators', $params) ) { + $this->valueValidators = $params['valueValidators']; + } + } + + public function validate($errors, $value, $stopOnFirstError = false) { + if ( $this->canTreatAsNull($value) ) { + return null; + } + + $validatedItems = []; + $hasErrors = false; + + foreach($value as $key => $item) { + $validatedKey = self::applyValidators($this->keyValidators, $key, $errors, $stopOnFirstError); + if ( is_wp_error($validatedKey) ) { + $hasErrors = true; + $errors = $validatedKey; + if ( $stopOnFirstError ) { + return $errors; + } + continue; + } + + $validatedItem = self::applyValidators($this->valueValidators, $item, $errors, $stopOnFirstError); + if ( is_wp_error($validatedItem) ) { + $hasErrors = true; + $errors = $validatedItem; + if ( $stopOnFirstError ) { + return $errors; + } + } else { + $validatedItems[$key] = $item; + } + } + + return $hasErrors ? $errors : $validatedItems; + } +} \ No newline at end of file diff --git a/customizables/Settings/NotificationSenderQueue.php b/customizables/Settings/NotificationSenderQueue.php new file mode 100644 index 0000000..17569c9 --- /dev/null +++ b/customizables/Settings/NotificationSenderQueue.php @@ -0,0 +1,56 @@ + + */ + protected $isInQueue; + /** + * @var \SplQueue + */ + protected $queue; + + public function __construct() { + $this->queue = new \SplQueue(); + $this->isInQueue = new \SplObjectStorage(); + } + + public function enqueue(UpdateNotificationSender $setting) { + if ( $this->isInQueue->contains($setting) ) { + //Already in the queue. Let's just mark it as valid. + $this->isInQueue[$setting] = true; + } else { + //Add to the queue. + $this->isInQueue->attach($setting, true); + $this->queue->enqueue($setting); + } + } + + public function dequeue() { + //Find and return the first valid (non-removed) item. + while (!$this->queue->isEmpty()) { + $sender = $this->queue->dequeue(); + if ( $this->isInQueue[$sender] ) { + $this->isInQueue->detach($sender); + return $sender; + } + } + + return null; + } + + public function remove(UpdateNotificationSender $setting) { + if ( $this->isInQueue->contains($setting) ) { + //There's not a quick way to remove an element from a SplQueue, + //so we'll just mark the item as invalid. It will be removed + //in dequeue(). + $this->isInQueue[$setting] = false; + } + } + + public function isEmpty() { + return ($this->queue->isEmpty() || ($this->isInQueue->count() < 1)); + } +} \ No newline at end of file diff --git a/customizables/Settings/NumericSetting.php b/customizables/Settings/NumericSetting.php new file mode 100644 index 0000000..d0ba2fa --- /dev/null +++ b/customizables/Settings/NumericSetting.php @@ -0,0 +1,81 @@ +minValue = isset($params['minValue']) ? $params['minValue'] : $this->minValue; + $this->maxValue = isset($params['maxValue']) ? $params['maxValue'] : $this->maxValue; + } + + public function validate($errors, $value, $stopOnFirstError = false) { + if ( $this->canTreatAsNull($value) ) { + return null; + } + + if ( !is_numeric($value) ) { + $errors->add('not_numeric', 'Value must be a number'); + return $errors; + } + + $numValue = floatval($value); + if ( ($this->minValue !== null) && ($numValue < $this->minValue) ) { + $errors->add('min_value', 'Value must be ' . $this->minValue . ' or greater'); + } + if ( ($this->maxValue !== null) && ($numValue > $this->maxValue) ) { + $errors->add('max_value', 'Value must be ' . $this->maxValue . ' or less'); + } + + if ( $errors->has_errors() ) { + return $errors; + } + return $numValue; + } + + /** + * @return float|int|null + */ + public function getMinValue() { + return $this->minValue; + } + + /** + * @return float|int|null + */ + public function getMaxValue() { + return $this->maxValue; + } + + public function serializeValidationRules() { + $result = []; + if ( $this->isNullable() ) { + $result['isNullable'] = true; + $result['convertEsToNull'] = true; + } + + $parserConfig = []; + if ( $this->minValue !== null ) { + $parserConfig['min'] = $this->minValue; + } + if ( $this->maxValue !== null ) { + $parserConfig['max'] = $this->maxValue; + } + $result['parsers'] = [['numeric', $parserConfig]]; + + return $result; + } +} \ No newline at end of file diff --git a/customizables/Settings/PlainTextSetting.php b/customizables/Settings/PlainTextSetting.php new file mode 100644 index 0000000..eadc2af --- /dev/null +++ b/customizables/Settings/PlainTextSetting.php @@ -0,0 +1,17 @@ + + */ + public function createControls(Builders\ElementBuilderFactory $b); +} \ No newline at end of file diff --git a/customizables/Settings/Setting.php b/customizables/Settings/Setting.php new file mode 100644 index 0000000..1e35a09 --- /dev/null +++ b/customizables/Settings/Setting.php @@ -0,0 +1,100 @@ +defaultValue = $params['default']; + } + $this->dataType = isset($params['type']) ? $params['type'] : $this->dataType; + } + + public function getValue($customDefault = null) { + $currentDefault = ($customDefault !== null) ? $customDefault : $this->defaultValue; + if ( $this->store ) { + return $this->store->getValue($currentDefault); + } + return $currentDefault; + } + + /** + * Update the value of this setting. + * + * @param $validValue + * @return boolean + */ + public function update($validValue) { + if ( $this->store ) { + $isSuccess = $this->store->setValue($validValue); + $this->notifyUpdated(); + return $isSuccess; + } + return false; + } + + /** + * Convert a setting value to a string usable in HTML forms. + * + * This does NOT encode special HTML characters. It is only intended to convert + * non-string values - like booleans and NULLs - to a format suitable for form field. + * + * @param $value + * @return string + */ + public function encodeForForm($value) { + //Subclasses that require more advanced encoding should override this method. + return (string)$value; + } + + /** + * Convert submitted form data to a type suitable for validation. + * This is not necessarily the same as the setting data type. + * + * @param string $value + */ + public function decodeSubmittedValue($value) { + //The default implementation is a no-op. Subclasses can override this. + return $value; + } + + /** + * @param \WP_Error $errors + * @param $value + * @param bool $stopOnFirstError + * @return \WP_Error|mixed + */ + public function validate($errors, $value, $stopOnFirstError = false) { + //Should be overridden by subclasses. + return $value; + } + + public function validateFormValue($errors, $value, $stopOnFirstError = false) { + $decodedValue = $this->decodeSubmittedValue($value); + return $this->validate($errors, $decodedValue, $stopOnFirstError); + } + + protected function canTreatAsNull($inputValue) { + if ( $this->isNullable() && (($inputValue === null) || ($inputValue === '')) ) { + return true; + } + return false; + } + + public function isNullable() { + return ($this->defaultValue === null); + } + + /** + * @return mixed|null + */ + public function getDefaultValue() { + return $this->defaultValue; + } +} \ No newline at end of file diff --git a/customizables/Settings/SettingGeneratorInterface.php b/customizables/Settings/SettingGeneratorInterface.php new file mode 100644 index 0000000..29c5b9d --- /dev/null +++ b/customizables/Settings/SettingGeneratorInterface.php @@ -0,0 +1,15 @@ +isNullable() ) { + return wp_json_encode($value); + } else { + return (string)$value; + } + } + + public function decodeSubmittedValue($value) { + if ( $this->isNullable() ) { + return json_decode($value, true); + } else { + return $value; + } + } +} \ No newline at end of file diff --git a/customizables/Settings/StringSetting.php b/customizables/Settings/StringSetting.php new file mode 100644 index 0000000..ebf80c6 --- /dev/null +++ b/customizables/Settings/StringSetting.php @@ -0,0 +1,54 @@ +minLength = ($params['minlength'] === null) ? null : (int)$params['minlength']; + } + if ( array_key_exists('maxlength', $params) ) { + $this->maxLength = ($params['maxlength'] === null) ? null : (int)$params['maxlength']; + } + + $this->validators[] = new StringValidator( + $this->minLength, + $this->maxLength, + false, + isset($params['regex']) ? $params['regex'] : null, + array_key_exists('trimmed', $params) && $params['trimmed'] + ); + } + + public function validate($errors, $value, $stopOnFirstError = false) { + if ( $this->canTreatAsNull($value) ) { + return null; + } + + $convertedValue = $value; + foreach ($this->validators as $validator) { + $result = call_user_func($validator, $convertedValue, $errors); + if ( is_wp_error($result) ) { + $errors = $result; + if ( $stopOnFirstError ) { + return $errors; + } + } else { + $convertedValue = $result; + } + } + + return $convertedValue; + } +} \ No newline at end of file diff --git a/customizables/Settings/UniqueSettingQueue.php b/customizables/Settings/UniqueSettingQueue.php new file mode 100644 index 0000000..0943c4c --- /dev/null +++ b/customizables/Settings/UniqueSettingQueue.php @@ -0,0 +1,57 @@ +internalQueues = [ + 'basic' => new NotificationSenderQueue(), + 'deferred' => new NotificationSenderQueue(), + ]; + } + + public function enqueue(AbstractSetting $setting) { + $this->internalQueues['basic']->enqueue($setting); + } + + public function enqueueDeferred(UpdateNotificationSender $setting) { + $this->internalQueues['deferred']->enqueue($setting); + } + + public function dequeue() { + foreach ($this->internalQueues as $queue) { + $sender = $queue->dequeue(); + if ( $sender !== null ) { + return $sender; + } + } + return null; + } + + public function remove(UpdateNotificationSender $setting) { + foreach ($this->internalQueues as $queue) { + $queue->remove($setting); + } + } + + public function isEmpty() { + foreach ($this->internalQueues as $queue) { + if ( !$queue->isEmpty() ) { + return false; + } + } + return true; + } + + public function processAll() { + while (!$this->isEmpty()) { + $sender = $this->dequeue(); + $sender->notifyUpdated(); + } + } +} \ No newline at end of file diff --git a/customizables/Settings/UpdateNotificationSender.php b/customizables/Settings/UpdateNotificationSender.php new file mode 100644 index 0000000..42cc28e --- /dev/null +++ b/customizables/Settings/UpdateNotificationSender.php @@ -0,0 +1,12 @@ +minLength === 0) ) { + return $convertedValue; + } + + //TODO: Optionally, allow protocol-relative URLs. Also in the JS validator. + //TODO: Optionally, allow shortcodes. + + $filteredValue = filter_var($convertedValue, FILTER_VALIDATE_URL); + if ( $filteredValue === false ) { + $errors->add('invalid_url', 'Value must be a valid URL'); + return $errors; + } + + $convertedValue = esc_url_raw($filteredValue); + if ( empty($convertedValue) ) { + //esc_url() documentation says it returns an empty string if the protocol + //is not one of the allowed protocols, but I'm not 100% sure if that is + //the *only* situation where it might return an empty string. + $errors->add('invalid_protocol', 'Invalid protocol or a malformed URL'); + return $errors; + } + + return $convertedValue; + } +} \ No newline at end of file diff --git a/customizables/Settings/UserDefinedSetting.php b/customizables/Settings/UserDefinedSetting.php new file mode 100644 index 0000000..ca7c622 --- /dev/null +++ b/customizables/Settings/UserDefinedSetting.php @@ -0,0 +1,26 @@ +validationCallback = $params['validationCallback']; + } else { + throw new \InvalidArgumentException('UserDefinedSetting must have a validationCallback parameter'); + } + } + + public function validate($errors, $value, $stopOnFirstError = false) { + return call_user_func($this->validationCallback, $value, $errors); + } +} \ No newline at end of file diff --git a/customizables/Settings/UserDefinedStruct.php b/customizables/Settings/UserDefinedStruct.php new file mode 100644 index 0000000..aa182e4 --- /dev/null +++ b/customizables/Settings/UserDefinedStruct.php @@ -0,0 +1,45 @@ +getId() . '.'; + $idPrefixLength = strlen($expectedIdPrefix); + + foreach ($children as $child) { + //This is a hack. There's no convenient way to pass the child key + //from the factory to this constructor. So the factory keeps a list + //of IDs and keys, and we use that or fall back to stripping our + //prefix from the ID. + $id = $child->getId(); + $childKey = $childFactory->getChildKeyFromId($id); + if ( $childKey === null ) { + if ( substr($id, 0, $idPrefixLength) === $expectedIdPrefix ) { + $childKey = substr($id, $idPrefixLength); + } else { + throw new \InvalidArgumentException('Child setting ID must be prefixed with the parent ID'); + } + } + + $this->registerChild($childKey, $child); + } + } + } + } + + //Make this public, allowing external code to add children. + public function createChild($childKey, $className, ...$constructorParams) { + return parent::createChild($childKey, $className, ...$constructorParams); + } +} \ No newline at end of file diff --git a/customizables/Settings/UserSanitizedStringSetting.php b/customizables/Settings/UserSanitizedStringSetting.php new file mode 100644 index 0000000..df67420 --- /dev/null +++ b/customizables/Settings/UserSanitizedStringSetting.php @@ -0,0 +1,62 @@ +sanitizationMode = $params['sanitizationMode']; + } + } + + public function validate($errors, $value, $stopOnFirstError = false) { + $convertedValue = parent::validate($errors, $value); + if ( is_wp_error($convertedValue) || ($convertedValue === null) ) { + return $convertedValue; + } + + if ( current_user_can('unfiltered_html') ) { + return $convertedValue; + } else { + switch ($this->sanitizationMode) { + case self::SANITIZE_POST_HTML: + return wp_kses_post($convertedValue); + case self::SANITIZE_STRIP_HTML: + return wp_kses($convertedValue, 'strip'); + case self::SANITIZE_ESCAPE_HTML: + return esc_html($convertedValue); + default: + return new WP_Error( + 'invalid_filter_mode', + 'Invalid filter mode set for this setting' + ); + } + } + } +} \ No newline at end of file diff --git a/customizables/SettingsForm.php b/customizables/SettingsForm.php new file mode 100644 index 0000000..4597747 --- /dev/null +++ b/customizables/SettingsForm.php @@ -0,0 +1,162 @@ + + */ + protected $settings = null; + + /** + * @var string|null ID attribute of the form element. + */ + protected $id = null; + + protected $defaultSubmitButtonEnabled = true; + + protected $errorReporting = UpdateRequestHandler::DIE_ON_ERRORS; + protected $errorTransientName = null; + + protected $redirectUrl = ''; + protected $successParams = array('updated' => 1); + protected $passThroughParams = array(); + + /** + * @var null|string + */ + protected $requiredCapability = null; + /** + * @var null|callable + */ + protected $permissionCallback = null; + + /** + * @var null|callable + */ + protected $postProcessingCallback = null; + + /** + * @var array + */ + protected $configurationParams; + + public function __construct($params = array()) { + $this->configurationParams = $params; + + $copyProperties = array( + 'action', + 'submitUrl', + 'method', + 'structure', + 'settings', + 'id', + 'defaultSubmitButtonEnabled', + 'errorReporting', + 'errorTransientName', + 'redirectUrl', + 'successParams', + 'passThroughParams', + 'requiredCapability', + 'permissionCallback', + 'postProcessingCallback', + ); + foreach ($copyProperties as $property) { + if ( isset($params[$property]) ) { + $this->$property = $params[$property]; + } + } + + if ( isset($params['renderer']) ) { + $this->renderer = $params['renderer']; + } else { + $this->renderer = new FormTableRenderer(); + } + } + + public function output() { + if ( $this->id !== null ) { + $formId = $this->id; + } else { + $formId = 'ame-struct-form-' . time(); + } + + echo HtmlHelper::tag('form', array( + 'action' => $this->submitUrl, + 'method' => $this->method, + 'id' => $formId, + )); + + $this->renderer->renderStructure($this->structure); + + if ( !empty($this->action) ) { + echo HtmlHelper::tag('input', array( + 'type' => 'hidden', + 'name' => 'action', + 'value' => $this->action, + )); + wp_nonce_field($this->action); + } + + if ( $this->defaultSubmitButtonEnabled ) { + submit_button('Save Changes'); + } + + echo ''; + + $this->renderer->enqueueDependencies('#' . $formId); + } + + public function handleUpdateRequest($requestParams, $queryParams = []) { + $handler = new UpdateRequestHandler( + $this->settings, + array_merge( + //Pass through most parameters. + $this->configurationParams, + [ + 'errorReporting' => $this->errorReporting, + 'errorTransientName' => $this->errorTransientName, + 'redirectUrl' => $this->redirectUrl, + 'successParams' => $this->successParams, + 'passThroughParams' => $this->passThroughParams, + 'requiredCapability' => $this->requiredCapability, + 'permissionCallback' => $this->permissionCallback, + 'postProcessingCallback' => $this->postProcessingCallback, + ] + ) + ); + + $handler->handleRequest($requestParams, $queryParams); + } + + public static function builder($action = null) { + return (new FormBuilder())->actionName($action); + } +} \ No newline at end of file diff --git a/customizables/Storage/AbstractSettingsDictionary.php b/customizables/Storage/AbstractSettingsDictionary.php new file mode 100644 index 0000000..e3d35c7 --- /dev/null +++ b/customizables/Storage/AbstractSettingsDictionary.php @@ -0,0 +1,416 @@ +store = $store; + $this->idPrefix = $idPrefix; + $this->lastModifiedTimeEnabled = $lastModifiedTimeEnabled; + $this->defaults = $this->createDefaults(); + $this->undefinedMarker = new \StdClass(); + } + + /** + * @return array + */ + abstract protected function createDefaults(); + + /** + * @return array Settings indexed by their ID. + */ + abstract protected function createSettings(); + + /** + * Get the value of a setting. + * + * Note that NULLs are treated as valid values. The fallback value will only + * be used if the setting is actually missing, not if it's set to NULL. + * + * @param string|string[] $path + * @param mixed $fallback + * @return mixed + */ + public function get($path, $fallback = null) { + //Try the storage. + $result = $this->store->getPath($path, $this->undefinedMarker); + if ( $result !== $this->undefinedMarker ) { + return $result; + } + //Try predefined defaults. + return $this->getDefault($path, $fallback); + } + + public function set($path, $value) { + $this->store->setPath($path, $value); + } + + /** + * @param string $path + * @param mixed $fallback + * @return mixed + */ + protected function getDefault($path, $fallback = null) { + return ameMultiDictionary::get($this->defaults, $path, $fallback); + } + + /** + * @return array + */ + public function getRegisteredSettings() { + if ( $this->registeredSettings === null ) { + $this->populateSettingInstances(); + } + return $this->registeredSettings; + } + + /** + * @return array + */ + public function getRegisteredSets() { + if ( $this->registeredSets === null ) { + $this->populateSettingInstances(); + } + return $this->registeredSets; + } + + private function populateSettingInstances() { + list($this->registeredSettings, $this->registeredSets) + = $this->flattenSettingsCollection($this->createSettings()); + + if ( $this->lastModifiedTimeEnabled ) { + $settingsWithoutLastModified = $this->registeredSettings; + + $path = self::LAST_MODIFIED_KEY; + $this->lastModifiedSetting = new StringSetting( + $this->idPrefix . $path, + $this->store->buildSlot($path), + [] + ); + $this->registeredSettings[$this->lastModifiedSetting->getId()] = $this->lastModifiedSetting; + + AbstractSetting::subscribeDeferred($settingsWithoutLastModified, function () { + $this->lastModifiedSetting->update(gmdate('c')); + }); + } + } + + /** + * Flatten a collection of settings and index it by ID. + * + * Also detects predefined sets present in the collection and adds them + * to a separate array indexed by ID. + * + * @param array|\Traversable $settings + * @return array{0: array, 1: array} + */ + private function flattenSettingsCollection($settings) { + $foundSettings = []; + $foundSets = []; + $this->addSettingsToCollection($foundSettings, $foundSets, $settings); + return [$foundSettings, $foundSets]; + } + + /** + * @param array $outputCollection + * @param array $detectedSets + * @param array|\Traversable $inputCollection + * @return void + */ + private function addSettingsToCollection(&$outputCollection, &$detectedSets, $inputCollection) { + foreach ($inputCollection as $item) { + if ( empty($item) ) { + continue; + } + if ( $item instanceof PredefinedSet ) { + $detectedSets[$item->getId()] = $item; + } + + if ( $item instanceof AbstractSetting ) { + $outputCollection[$item->getId()] = $item; + } else if ( is_array($item) || ($item instanceof SettingGeneratorInterface) ) { + $this->addSettingsToCollection($outputCollection, $detectedSets, $item); + } else { + throw new \InvalidArgumentException( + 'Unexpected item type in a setting collection: ' + . is_object($item) ? get_class($item) : gettype($item) + ); + } + } + } + + /** + * Like findSetting(), but throws an exception if the setting doesn't exist. + * + * @param string $settingIdOrPath + * @return AbstractSetting + */ + public function getSetting($settingIdOrPath) { + $result = $this->findSetting($settingIdOrPath); + if ( $result !== null ) { + return $result; + } + + throw new \InvalidArgumentException("Unknown setting: $settingIdOrPath"); + } + + /** + * Find a setting by ID or path. + * + * @param $settingIdOrPath + * @return AbstractSetting|null + */ + public function findSetting($settingIdOrPath) { + $settings = $this->getRegisteredSettings(); + + //Try the plain ID. + /** @noinspection PhpRedundantOptionalArgumentInspection */ + $result = ameMultiDictionary::get($settings, $settingIdOrPath, null); + if ( $result !== null ) { + return $result; + } + + //Try the ID with the prefix. + if ( !empty($this->idPrefix) && is_string($settingIdOrPath) ) { + /** @noinspection PhpRedundantOptionalArgumentInspection */ + $result = ameMultiDictionary::get($settings, $this->idPrefix . $settingIdOrPath, null); + if ( $result !== null ) { + return $result; + } + } + + return null; + } + + /** + * @param string $setIdOrPath + * @return PredefinedSet + */ + public function getPredefinedSet($setIdOrPath) { + if ( isset($this->registeredSets[$setIdOrPath]) ) { + return $this->registeredSets[$setIdOrPath]; + } + + if ( !empty($this->idPrefix) ) { + $idWithPrefix = $this->idPrefix . $setIdOrPath; + if ( isset($this->registeredSets[$idWithPrefix]) ) { + return $this->registeredSets[$idWithPrefix]; + } + } + + $setting = ameMultiDictionary::get($this->getRegisteredSettings(), $setIdOrPath); + if ( $setting instanceof PredefinedSet ) { + return $setting; + } + + throw new \InvalidArgumentException("Unknown set: $setIdOrPath"); + } + + /** + * Get the default values of all registered settings (recursive). + * + * Note: The intent is to return the defaults in a format that can be safely + * JSON-encoded and passed to JavaScript. This means that empty associative + * arrays and structs are converted to empty objects. + * + * @return array A map of setting IDs to their default values. + */ + public function getRecursiveDefaultsForJs() { + //Generate a map of all supported settings and their defaults. + $settings = $this->getRegisteredSettings(); + $defaults = []; + foreach (AbstractSetting::recursivelyIterateSettings($settings) as $setting) { + $defaultValue = $setting->getDefaultValue(); + + //wp_json_encode() encodes empty associative arrays as plain JS arrays, + //but we need empty objects. We can't distinguish between an empty associative + //array and a normal array, so we also need to check the setting's data type. + if ( is_array($defaultValue) && empty($defaultValue) && ($setting->getDataType() === 'map') ) { + $defaultValue = new \stdClass(); + } + + $defaults[$setting->getId()] = $defaultValue; + } + return $defaults; + } + + public function save() { + $this->store->save(); + } + + /** + * @param array $aliases + * @return void + */ + public function addReadAliases($aliases) { + $this->store->addReadAliases($aliases); + } + + /** + * Merge the elements of this setting collection and an associative array. + * + * This is not a recursive merge. The input array will simply overwrite any + * settings that have the same keys. + * + * @param array $newSettings + * @return void + */ + public function mergeWith($newSettings) { + $oldSettings = $this->toArray(); + $this->store->setValue(array_merge($oldSettings, $newSettings)); + } + + /** + * @noinspection PhpLanguageLevelInspection + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) { + return $this->get($offset); + } + + /** + * @noinspection PhpLanguageLevelInspection + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) { + $this->set($offset, $value); + } + + /** + * @noinspection PhpLanguageLevelInspection + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset) { + $this->store->deletePath($offset); + } + + /** + * @noinspection PhpLanguageLevelInspection + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) { + /* + * Caution: This implementation breaks the implied contract for NULL values. + * PHP seems to assume that offsetExists() will return false when the offset + * exists but the value is NULL. For example, isset() doesn't bother calling + * offsetGet() to check the actual value when offsetExists() returns true. + * + * This version may return true instead (depending on the underlying storage + * implementation). + * + * Unlike isset(), empty() still works correctly. + */ + return ($this->get($offset, $this->undefinedMarker) !== $this->undefinedMarker); + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function jsonSerialize() { + $data = $this->store->getValue(); + if ( empty($data) ) { + //Usually, json_encode() will serialize an empty array as "[]", but + //we want "{}" in case it gets used in JavaScript. + return new \StdClass(); + } + return $data; + } + + public function toArray() { + $value = $this->store->getValue(); + if ( empty($value) ) { + return array(); + } + return (array)$value; + } + + /** + * Does this collection have custom values for any settings? + * + * A true result does not necessarily mean that the custom values are different + * from the defaults, only that some settings have been set/changed. + * + * @return bool + */ + public function hasCustomValues() { + $data = $this->store->getValue(); + return !empty($data); + } + + /** + * @return int|null + */ + public function getLastModifiedTimestamp() { + if ( !$this->lastModifiedTimeEnabled ) { + return null; + } + $isoTimestamp = $this->get(self::LAST_MODIFIED_KEY); + if ( empty($isoTimestamp) ) { + return null; + } + return strtotime($isoTimestamp); + } + + public function elementBuilder() { + return new ElementBuilderFactory($this); + } + + public function settingFactory() { + return new SettingFactory($this->store, $this->defaults, $this->idPrefix); + } +} \ No newline at end of file diff --git a/customizables/Storage/ActiveMenuConfiguration.php b/customizables/Storage/ActiveMenuConfiguration.php new file mode 100644 index 0000000..f31c3ee --- /dev/null +++ b/customizables/Storage/ActiveMenuConfiguration.php @@ -0,0 +1,110 @@ +menuEditor = $menuEditor; + } + + /** + * @param \WPMenuEditor $menuEditor + * @return self + */ + public static function getInstance($menuEditor = null) { + static $instance = null; + if ( $instance === null ) { + if ( $menuEditor === null ) { + $menuEditor = $GLOBALS['wp_menu_editor']; + } + $instance = new self($menuEditor); + } + return $instance; + } + + /** + * @return MenuConfigurationWrapper + */ + private function lazyInit() { + if ( $this->wrapper !== null ) { + return $this->wrapper; + } + + $actualConfigId = $this->menuEditor->get_loaded_menu_config_id(); + if ( empty($actualConfigId) ) { + $this->menuEditor->load_custom_menu(); + $actualConfigId = $this->menuEditor->get_loaded_menu_config_id(); + if ( empty($actualConfigId) ) { + $actualConfigId = 'site'; + } + } + + $this->wrapper = MenuConfigurationWrapper::getStoreByConfigId( + $actualConfigId, + $this->menuEditor + ); + return $this->wrapper; + } + + public function getValue($defaultValue = null) { + return $this->lazyInit()->getValue($defaultValue); + } + + public function setValue($value) { + return $this->lazyInit()->setValue($value); + } + + public function getPath($path, $defaultValue = null) { + return $this->lazyInit()->getPath($path, $defaultValue); + } + + public function setPath($path, $value) { + return $this->lazyInit()->setPath($path, $value); + } + + public function deleteValue() { + $this->lazyInit()->deleteValue(); + } + + public function deletePath($path) { + $this->lazyInit()->deletePath($path); + } + + public function buildSlot($path) { + return $this->lazyInit()->buildSlot($path); + } + + public function save() { + $this->lazyInit()->save(); + } + + public function getStorageKey() { + return $this->lazyInit()->getStorageKey(); + } + + public function addReadAliases($aliases) { + $this->lazyInit()->addReadAliases($aliases); + } + + public function getSmallestSavable() { + return $this->lazyInit()->getSmallestSavable(); + } + + public function setPreviewValue($value) { + $this->lazyInit()->setPreviewValue($value); + } + + public function setPreviewByPath($path, $value) { + $this->lazyInit()->setPreviewByPath($path, $value); + } +} \ No newline at end of file diff --git a/customizables/Storage/CompressedStorage.php b/customizables/Storage/CompressedStorage.php new file mode 100644 index 0000000..f742527 --- /dev/null +++ b/customizables/Storage/CompressedStorage.php @@ -0,0 +1,11 @@ +data = $initialData; + $this->doneLoading = true; + } + } + + /** + * @inheritDoc + */ + public function setValue($value) { + $this->data = $value; + $this->doneLoading = true; + $this->isMarkedForDeletion = false; + return true; + } + + public function deleteValue() { + $this->data = null; + $this->doneLoading = true; + $this->isMarkedForDeletion = true; + } + + /** + * @inheritDoc + */ + public function setPath($path, $value) { + $this->lazyLoad(); + $this->isMarkedForDeletion = false; + + $path = $this->parsePath($path); + + //Data could be NULL if it was previously deleted. + if ( $this->data === null ) { + $this->data = []; + } + + return ameMultiDictionary::set($this->data, $path, $value); + } + + public function deletePath($path) { + $this->lazyLoad(); + + $path = $this->parsePath($path); + //An empty path is invalid. This method can't delete the entire storage. + if ( empty($path) ) { + throw new \InvalidArgumentException('Path cannot be empty'); + } + + ameMultiDictionary::delete($this->data, $path); + } + + /** + * @inheritDoc + */ + public function buildSlot($path) { + return new Slot($this, $path); + } + + /** + * @inheritDoc + */ + protected function rawGetPath($path, $defaultValue = null) { + $this->lazyLoad(); + return ameMultiDictionary::get($this->data, $path, $defaultValue); + } + + protected function rawGetValue($defaultValue = null) { + $this->lazyLoad(); + return ($this->data !== null) ? $this->data : $defaultValue; + } + + protected function lazyLoad() { + if ( $this->doneLoading ) { + return; + } + + $this->doneLoading = true; + $this->isMarkedForDeletion = false; + $this->data = $this->loadData(); + } + + public function save() { + if ( $this->isMarkedForDeletion && ($this->data === null) ) { + $this->deleteStoredData(); + } else { + $this->lazyLoad(); + $this->storeData($this->data); + } + } + + /** + * Save data to the database or other persistent storage. + * + * @param array $newData + * @return void + */ + protected function storeData($newData) { + //Override in subclasses. + } + + /** + * Load data from persistent storage. + * + * @return array|null + */ + protected function loadData() { + //Override in subclasses. + return []; + } + + /** + * Delete data in persistent storage. + * + * @return void + */ + protected function deleteStoredData() { + //Override in subclasses. + } +} \ No newline at end of file diff --git a/customizables/Storage/MenuConfigurationWrapper.php b/customizables/Storage/MenuConfigurationWrapper.php new file mode 100644 index 0000000..390fa5b --- /dev/null +++ b/customizables/Storage/MenuConfigurationWrapper.php @@ -0,0 +1,76 @@ +menuEditor = $menuEditor; + $this->menuConfigId = $menuConfigId; + parent::__construct(); + } + + protected function loadData() { + $data = $this->menuEditor->load_custom_menu($this->menuConfigId); + if ( $data === null ) { + $data = []; + } + return $data; + } + + protected function storeData($newData) { + //Caution: Currently, the underlying implementation doesn't support configs + //without a "tree" key. This may need to be changed to allow configurations + //that only specify menu styles, not menu items. + $this->menuEditor->set_custom_menu($newData, $this->menuConfigId); + } + + protected function deleteStoredData() { + throw new \LogicException('Cannot delete the menu configuration via this interface.'); + } + + public function setValue($value) { + throw new \LogicException('Cannot replace the menu configuration via this interface.'); + } + + /** + * @param string $menuConfigId + * @param \WPMenuEditor|null $menuEditor + * @return self + */ + public static function getStoreByConfigId($menuConfigId, $menuEditor = null) { + if ( isset(self::$wrappersById[$menuConfigId]) ) { + return self::$wrappersById[$menuConfigId]; + } + if ( $menuEditor === null ) { + $menuEditor = $GLOBALS['wp_menu_editor']; + } + + $wrapper = new self($menuEditor, $menuConfigId); + self::$wrappersById[$menuConfigId] = $wrapper; + return $wrapper; + } + + /** + * @param string|null $optionalConfigId + * @param \WPMenuEditor|null $menuEditor + * @return \YahnisElsts\AdminMenuEditor\Customizable\Storage\StorageInterface + */ + public static function getStore($optionalConfigId = null, $menuEditor = null) { + if ( $optionalConfigId === null ) { + return ActiveMenuConfiguration::getInstance($menuEditor); + } else { + return self::getStoreByConfigId($optionalConfigId, $menuEditor); + } + } +} \ No newline at end of file diff --git a/customizables/Storage/ModuleSettings.php b/customizables/Storage/ModuleSettings.php new file mode 100644 index 0000000..9f5794d --- /dev/null +++ b/customizables/Storage/ModuleSettings.php @@ -0,0 +1,49 @@ +setJsonSerialization($jsonSerializationEnabled); + + $this->defaults = $defaults; + $this->settingCreationCallback = $settingCreationCallback; + + parent::__construct($store, $optionName . '--', $lastModifiedTimeEnabled); + } + + protected function createDefaults() { + return $this->defaults; + } + + protected function createSettings() { + if ( $this->settingCreationCallback !== null ) { + $settings = call_user_func($this->settingCreationCallback, $this); + //Index by ID. + $results = array(); + foreach ($settings as $setting) { + $results[$setting->getId()] = $setting; + } + return $results; + } + return array(); + } + + /** + * @return StorageInterface + */ + public function getStore() { + return $this->store; + } +} diff --git a/customizables/Storage/NullStorage.php b/customizables/Storage/NullStorage.php new file mode 100644 index 0000000..2628c05 --- /dev/null +++ b/customizables/Storage/NullStorage.php @@ -0,0 +1,74 @@ +isPreviewing ? $this->previewValue : $defaultValue; + } + + public function getPath($path, $defaultValue = null) { + if ( $this->isPreviewing && is_array($this->previewValue) ) { + return ameMultiDictionary::get($this->previewValue, $path, $defaultValue); + } + return $defaultValue; + } + + public function setValue($value) { + //Do nothing. + return true; + } + + public function setPath($path, $value) { + //Do nothing. + return true; + } + + public function deleteValue() { + //Also do nothing here. + } + + public function deletePath($path) { + //Do nothing. + } + + public function buildSlot($path) { + return new Slot($this, $path); + } + + public function save() { + //Do nothing. + } + + public function getStorageKey() { + return '{NullStorage}'; + } + + public function addReadAliases($aliases) { + //We store nothing, so we don't need aliases. + } + + public function getSmallestSavable() { + return $this; + } + + public function setPreviewValue($value) { + $this->isPreviewing = true; + $this->previewValue = $value; + } + + public function setPreviewByPath($path, $value) { + $this->isPreviewing = true; + + if ( !is_array($this->previewValue) ) { + $this->previewValue = []; + } + + ameMultiDictionary::set($this->previewValue, $path, $value); + } +} \ No newline at end of file diff --git a/customizables/Storage/ScopedOptionStorage.php b/customizables/Storage/ScopedOptionStorage.php new file mode 100644 index 0000000..e5088af --- /dev/null +++ b/customizables/Storage/ScopedOptionStorage.php @@ -0,0 +1,131 @@ +optionName = $optionName; + $this->scope = $scope; + $this->compressionEnabled = $compressionEnabled; + } + + protected function loadData() { + $defaultValue = null; + + if ( $this->scope === self::SITE_SCOPE ) { + $value = get_option($this->optionName, $defaultValue); + } else { + $value = get_site_option($this->optionName, $defaultValue); + } + + //Decompress gzipped data. + if ( + $this->compressionSupported + && is_string($value) + && (substr($value, 0, strlen(self::COMPRESSED_VALUE_PREFIX)) === self::COMPRESSED_VALUE_PREFIX) + && function_exists('gzuncompress') + ) { + //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- For back-compat with older plugin versions. + $value = unserialize(gzuncompress(base64_decode( + substr($value, strlen(self::COMPRESSED_VALUE_PREFIX) + )))); + } + + //Parse JSON. + if ( $this->jsonSerializationEnabled && is_string($value) ) { + $value = json_decode($value, true); + } + + return $value; + } + + protected function storeData($newData) { + $storedData = $newData; + + //Optionally, serialize to JSON. + if ( $this->jsonSerializationEnabled ) { + $storedData = wp_json_encode($storedData); + } + + //Compress the data. + if ( + $this->compressionSupported + && $this->compressionEnabled + && function_exists('gzcompress') + ) { + //This presents a migration risk: if the database is migrated from a site + //that has the zlib extension to one that does not, the plugin won't be able + //to load the compressed data. + //phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize + $storedData = self::COMPRESSED_VALUE_PREFIX + . base64_encode(gzcompress(serialize($storedData))); + //phpcs:enable + } + + if ( ($this->scope === self::GLOBAL_SCOPE) && is_multisite() ) { + if ( class_exists('\\WPMenuEditor', false) ) { + return WPMenuEditor::atomic_update_site_option($this->optionName, $storedData); + } + return update_site_option($this->optionName, $storedData); + } else { + return update_option($this->optionName, $storedData); + } + } + + protected function deleteStoredData() { + if ( ($this->scope === self::GLOBAL_SCOPE) && is_multisite() ) { + return delete_site_option($this->optionName); + } else { + return delete_option($this->optionName); + } + } + + /** + * Toggle gzip compression. + * + * This only works if the zlib extension is available. If it's not, the method will + * always return false. + * + * @param boolean $enabled + * @return boolean Whether compression was actually enabled or not. + */ + public function setCompressionEnabled($enabled) { + if ( !function_exists('gzcompress') ) { + $this->compressionEnabled = false; + } else { + $this->compressionEnabled = $enabled; + } + return $this->compressionEnabled; + } + + public function setJsonSerialization($enabled) { + $this->jsonSerializationEnabled = $enabled; + } + + public function getStorageKey() { + return $this->optionName; + } +} \ No newline at end of file diff --git a/customizables/Storage/Slot.php b/customizables/Storage/Slot.php new file mode 100644 index 0000000..06c7baa --- /dev/null +++ b/customizables/Storage/Slot.php @@ -0,0 +1,101 @@ +store = $store; + $this->pathPrefix = $this->parsePath($path); + } + + protected function rawGetValue($defaultValue = null) { + return $this->store->getPath($this->pathPrefix, $defaultValue); + } + + public function setValue($value) { + return $this->store->setPath($this->pathPrefix, $value); + } + + protected function rawGetPath($path, $defaultValue = null) { + return $this->store->getPath( + $this->addPrefixToPath($this->pathPrefix, $path), + $defaultValue + ); + } + + public function setPath($path, $value) { + return $this->store->setPath( + $this->addPrefixToPath($this->pathPrefix, $path), + $value + ); + } + + public function deleteValue() { + $this->store->deletePath($this->pathPrefix); + } + + public function deletePath($path) { + $this->store->deletePath( + $this->addPrefixToPath($this->pathPrefix, $path) + ); + } + + public function buildSlot($path) { + //We could simply return "new Slot($this, $path)", but that would lead to + //unnecessary nested getPath() calls when retrieving the value. Using + //the underlying storage should be more efficient. + return new Slot( + $this->store, + $this->addPrefixToPath($this->pathPrefix, $path) + ); + } + + public function addReadAliases($aliases) { + /* + * Forward the aliases to the underlying storage, but prefix them with + * the path of this slot. + * + * We could store aliases on the slot, but then buildSlot() would not be able + * to use the underlying storage because that storage would not know about + * the aliases. Using the $store only when there are no aliases also wouldn't + * work sometimes because aliases could be added later. + */ + foreach($aliases as $alias => $path) { + $nestedAlias = $this->addPrefixToPath($this->pathPrefix, $alias); + $nestedPath = $this->addPrefixToPath($this->pathPrefix, $path); + + $this->store->addReadAliases([ + implode(self::PATH_SEPARATOR, $nestedAlias) => implode(self::PATH_SEPARATOR, $nestedPath), + ]); + } + } + + public function getSmallestSavable() { + return $this->store->getSmallestSavable(); + } + + public function setPreviewValue($value) { + return $this->store->setPreviewByPath($this->pathPrefix, $value); + } + + public function setPreviewByPath($path, $value) { + return $this->store->setPreviewByPath( + $this->addPrefixToPath($this->pathPrefix, $path), + $value + ); + } +} \ No newline at end of file diff --git a/customizables/Storage/StorageInterface.php b/customizables/Storage/StorageInterface.php new file mode 100644 index 0000000..070ff56 --- /dev/null +++ b/customizables/Storage/StorageInterface.php @@ -0,0 +1,80 @@ + $aliases + */ + public function addReadAliases($aliases); + + /** + * Get the closest StorageInterface in this instance's hierarchy that + * can be efficiently saved. + * + * For example, you can have a Slot that represents a specific key in + * an associative array, and the array is stored as a WP option. Technically, + * you can "save" the individual Slot, but it will actually cause the whole + * array to be written to the database. This is inefficient if you need to + * update multiple slots that share the same underlying array because you + * will end up rewriting the same option multiple times. + * + * Instead, this method should return the underlying StorageInterface, + * and you should call save() on that. + * + * If you have a deep hierarchy, this method should not search for the root, + * but return the closest storage that can be saved as a unit (which could + * be $this), or at least the one that would trigger the lowest number of writes. + * + * Implementations that don't have a parent should just return $this. + * + * @return StorageInterface + */ + public function getSmallestSavable(); + + public function setPreviewValue($value); + + public function setPreviewByPath($path, $value); +} \ No newline at end of file diff --git a/customizables/Storage/StorageMethods.php b/customizables/Storage/StorageMethods.php new file mode 100644 index 0000000..d829c0b --- /dev/null +++ b/customizables/Storage/StorageMethods.php @@ -0,0 +1,177 @@ + When one path doesn't exist, try reading another path. + */ + private $readAliases = array(); + + protected $isPreviewing = false; + protected $previewData = []; + + protected function __construct() { + $this->undefinedMarker = new \StdClass(); + } + + protected function parsePath($path) { + return AmeMultiDictionary::parsePath($path, StorageInterface::PATH_SEPARATOR); + } + + /** + * @param array $prefix + * @param string|array $path + * @return array + */ + protected function addPrefixToPath($prefix, $path) { + return AmeMultiDictionary::addPrefixToPath($prefix, $path, StorageInterface::PATH_SEPARATOR); + } + + public function save() { + //Does nothing. Subclasses can override this to save changes. + } + + public function getStorageKey() { + return 'not_applicable'; + } + + public function getPath($path, $defaultValue = null) { + $path = $this->parsePath($path); + //Empty path = return all data. + if ( empty($path) ) { + return $this->getValue($defaultValue); + } + + //Try the specified path. + $result = $this->rawGetPath($path, $this->undefinedMarker); + if ( ($result === $this->undefinedMarker) && !empty($this->readAliases) ) { + //Try aliases. + $stringPath = is_array($path) + ? implode(StorageInterface::PATH_SEPARATOR, $path) + : $path; + + if ( isset($this->readAliases[$stringPath]) ) { + $result = $this->rawGetPath( + $this->readAliases[$stringPath], + $defaultValue + ); + } + } + + if ( $this->isPreviewing ) { + $previewValue = ameMultiDictionary::get($this->previewData, $path, $this->undefinedMarker); + if ( $previewValue !== $this->undefinedMarker ) { + if ( is_array($previewValue) && is_array($result) ) { + //Merge preview data with existing settings. + $result = $this->mergeArraysRecursively($result, $previewValue); + } else { + //Just override the setting. + return $previewValue; + } + } + } + + if ( $result !== $this->undefinedMarker ) { + return $result; + } + //Fall back to default value. + return $defaultValue; + } + + /** + * Get the value at a path without using aliases. + * + * @param string[] $path Should always be a pre-parsed array, not a string. + * @param mixed|null $defaultValue + * @return mixed + */ + abstract protected function rawGetPath($path, $defaultValue = null); + + public function getValue($defaultValue = null) { + $result = $this->rawGetValue($defaultValue); + if ( $this->isPreviewing ) { + if ( is_array($result) && is_array($this->previewData) ) { + $result = array_merge($result, $this->previewData); + } else { + $result = $this->previewData; + } + } + return $result; + } + + abstract protected function rawGetValue($defaultValue = null); + + /** + * @param array $aliases + * @return void + */ + public function addReadAliases($aliases) { + $this->readAliases = array_merge($this->readAliases, $aliases); + } + + public function getSmallestSavable() { + return $this; + } + + public function setPreviewByPath($path, $value) { + $this->isPreviewing = true; + + $path = $this->parsePath($path); + if ( empty($path) ) { + $this->previewData = $value; + } else { + ameMultiDictionary::set($this->previewData, $path, $value); + } + } + + public function setPreviewValue($value) { + $this->isPreviewing = true; + $this->previewData = $value; + } + + /** + * Merge two arrays recursively. + * + * This method differs from array_merge_recursive() in how it handles elements + * that have the same key in both arrays. array_merge_recursive() will combine + * the values into a new nested array. This method will do the same if both values + * are arrays. However, if either value is not an array, it will just overwrite + * the first value with the second. + * + * Example: + * + * $a = array('foo' => 1); + * $b = array('foo' => 2); + * $merged = array_merge_recursive($a, $b); + * //Result: array('foo' => array(1, 2)) + * + * $merged = $this->mergeArraysRecursively($a, $b); + * //Result: array('foo' => 2) + * + * @param array $base + * @param array $input + * @return array + */ + protected static function mergeArraysRecursively($base, $input) { + if ( is_array($base) && is_array($input) ) { + foreach ($input as $key => $value) { + if ( + array_key_exists($key, $base) + && is_array($base[$key]) + && is_array($value) + ) { + $base[$key] = self::mergeArraysRecursively($base[$key], $value); + } else { + $base[$key] = $value; + } + } + return $base; + } + return $input; + } +} \ No newline at end of file diff --git a/customizables/UpdateRequestHandler.php b/customizables/UpdateRequestHandler.php new file mode 100644 index 0000000..039c869 --- /dev/null +++ b/customizables/UpdateRequestHandler.php @@ -0,0 +1,400 @@ + 1); + protected $passThroughParams = array(); + + /** + * @var null|callable + */ + protected $permissionCallback = null; + + /** + * @var null|callable + */ + protected $postProcessingCallback = null; + + /** + * @var array + */ + protected $settingsById = array(); + + /** + * Skip fields that are not present in the update request. The corresponding + * settings won't be changed. + */ + const SKIP_MISSING_FIELDS = 10; + /** + * When a setting doesn't have a corresponding field in the update request, + * use an empty string in place of the missing field. + */ + const TREAT_MISSING_FIELDS_AS_EMPTY = 20; + + /** + * @var mixed How to handle missing fields - that is, settings that don't have + * a matching request parameter. + */ + protected $missingFieldHandling = self::SKIP_MISSING_FIELDS; + + public function __construct($settingsById, $params = array()) { + $this->settingsById = $settingsById; + + $copyProperties = array( + 'errorReporting', + 'errorTransientName', + 'redirectUrl', + 'successParams', + 'passThroughParams', + 'permissionCallback', + 'postProcessingCallback', + 'expectedAction', + 'nonceCheckEnabled', + 'missingFieldHandling', + 'stopOnFirstError', + 'partialUpdatesAllowed', + ); + foreach ($copyProperties as $property) { + if ( isset($params[$property]) ) { + $this->$property = $params[$property]; + } + } + } + + /** + * Beware: This method will stop execution one way or another. + * + * @param array $requestParams + * @param array $queryParams + */ + public function handleRequest($requestParams, $queryParams = []) { + //Check action. + $action = ''; + if ( isset($requestParams['action']) ) { + $action = $requestParams['action']; + } + if ( isset($this->expectedAction) && ($action !== $this->expectedAction) ) { + $this->handleError(new WP_Error( + 'ame_invalid_action', + sprintf( + 'The action parameter has an invalid value. Expected: "%s", actual value: "%s".', + esc_html($this->expectedAction), + esc_html($action) + ) + )); + } + + //Check nonce. + if ( $this->nonceCheckEnabled ) { + if ( wp_doing_ajax() ) { + check_ajax_referer($action, '_ajax_nonce'); + } else { + check_admin_referer($action); + } + } + + //Check request permissions. + if ( !empty($this->permissionCallback) ) { + $permissionStatus = call_user_func($this->permissionCallback, $requestParams); + if ( !$permissionStatus ) { + $this->handleError(new WP_Error( + 'ame_permission_denied', + 'You do not have sufficient permissions to perform this operation.' + )); + } else if ( is_wp_error($permissionStatus) ) { + $this->handleError($permissionStatus); + } + } + + //Extract relevant fields from request parameters. For example, "action" + //and "_wpnonce" are usually reserved and do not contain setting values. + //We only want parameters that match setting IDs. + $inputValues = []; + foreach ($requestParams as $key => $value) { + if ( in_array($key, $this->reservedFields) ) { + continue; + } + if ( isset($this->settingsById[$key]) ) { + $inputValues[$key] = $value; + } + } + + //Optionally, substitute missing fields with empty values. + //Settings that are not editable are excluded. + if ( $this->missingFieldHandling === self::TREAT_MISSING_FIELDS_AS_EMPTY ) { + $inputValues = $this->substituteEmptyValues($this->settingsById, $inputValues); + } + + list($errors, $sanitizedValues) = $this->checkAllInputs($inputValues, $this->stopOnFirstError); + + //Can we update any settings? + $settingsUpdated = false; + if ( !empty($sanitizedValues) && (empty($errors) || $this->partialUpdatesAllowed) ) { + //Update settings. + $updatedSettings = []; + foreach ($sanitizedValues as $settingId => $value) { + $this->settingsById[$settingId]->update($value); + $updatedSettings[] = $this->settingsById[$settingId]; + } + + //Send any queued update notifications. + Settings\AbstractSetting::sendPendingNotifications(); + + //Run the post-processing callback. + if ( !empty($this->postProcessingCallback) ) { + call_user_func($this->postProcessingCallback, $sanitizedValues, $this->settingsById); + } + + //Save settings. + Settings\AbstractSetting::saveAll($updatedSettings); + $settingsUpdated = true; + } + + if ( !empty($errors) ) { + //Error! But could also be a partial success. + $this->handleError($errors, $settingsUpdated, $requestParams, $queryParams); + } else if ( $settingsUpdated ) { + //Success! + $this->handleSuccess($requestParams, $queryParams); + } else { + //No errors and no changes. This is probably an error in itself because the user + //wouldn't have submitted the form if they didn't intend to save something. + $this->handleError(new WP_Error( + 'ame_no_changes', + 'There were no validation errors, but no changes were made to the settings.' + . ' This is unexpected and may be a bug.' + )); + } + } + + /** + * @param array|\Traversable $settingsById + * @param array $inputValues + * @return array + */ + protected function substituteEmptyValues($settingsById, $inputValues) { + foreach ($settingsById as $settingId => $setting) { + if ( !array_key_exists($settingId, $inputValues) && $setting->isEditableByUser() ) { + if ( $setting instanceof Settings\AbstractStructSetting ) { + $inputValues[$settingId] = array(); + } else { + $inputValues[$settingId] = ''; + } + } + + if ( $setting instanceof Settings\AbstractStructSetting ) { + $inputValues[$settingId] = $this->substituteEmptyValues( + $setting, + $inputValues[$settingId] + ); + } + } + return $inputValues; + } + + /** + * @param \WP_Error|\WP_Error[] $error + * @return void + */ + protected function handleError($error, $isPartialSuccess = false, $requestParams = [], $queryParams = []) { + if ( $this->errorReporting === self::DIE_ON_ERRORS ) { + + if ( is_array($error) ) { + $messageLines = []; + foreach ($error as $settingId => $singleError) { + foreach ($singleError->get_error_messages() as $singleMessage) { + $messageLines[] = esc_html(sprintf( + '%s: %s', + //Add setting names to error messages. + isset($this->settingsById[$settingId]) + ? $this->settingsById[$settingId]->getLabel() + : (!empty($settingId) ? $settingId : 'Error'), + $singleMessage + )); + } + } + + $message = implode("
    \n", $messageLines); + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Individual lines are escaped above. + wp_die($message); + } else { + $displayError = $error; + wp_die(wsAmeEscapeWpError($displayError)); + } + + } else if ( $this->errorReporting === self::STORE_ERRORS ) { + + $errors = is_array($error) ? $error : array($error); + $serializedErrors = wp_json_encode(array_map([self::class, 'errorToArray'], $errors)); + set_transient($this->errorTransientName, $serializedErrors, 120); + + $this->redirectToNextPage( + $requestParams, + $queryParams, + $isPartialSuccess ? $this->successParams : array() + ); + exit; + + } else { + throw new \LogicException("Invalid error mode: $this->errorReporting"); + } + } + + protected function checkAllInputs($inputValues, $stopOnError = false) { + $errors = []; + $sanitizedValues = []; + + foreach ($inputValues as $settingId => $value) { + if ( !isset($this->settingsById[$settingId]) ) { + continue; + } + + $setting = $this->settingsById[$settingId]; + + //Validate and sanitize. + $validationResult = $setting->validateFormValue(new WP_Error(), $value, $stopOnError); + if ( is_wp_error($validationResult) && ($validationResult->has_errors()) ) { + $errors[$settingId] = $validationResult; + if ( $stopOnError ) { + break; + } + } else { + $sanitizedValues[$settingId] = $validationResult; + } + + //Check setting permissions. + if ( !$setting->isEditableByUser() ) { + $errors[$settingId] = new WP_Error( + 'ame_permission_denied', + 'You do not have permission to change this setting.' + ); + if ( $stopOnError ) { + break; + } + } + } + + return [$errors, $sanitizedValues]; + } + + protected function redirectToNextPage($requestParams, $queryParams, $addQueryParams = []) { + if ( empty($this->redirectUrl) ) { + throw new \LogicException('No redirect URL was specified.'); + } + //Redirect to the next page. + + //Typically, there will be a parameter like "message" or "updated" that + //indicates settings were saved successfully. + $redirectParams = $addQueryParams; + + //Optionally, you can pass through other parameters, e.g. to reselect + //the previously selected item after saving changes. + foreach ($this->passThroughParams as $name) { + //Do not overwrite success parameters. + if ( array_key_exists($name, $redirectParams) ) { + continue; + } + + //Prefer request parameters, then query parameters. + //These could be the same if it's a GET request. + if ( array_key_exists($name, $requestParams) ) { + $redirectParams[$name] = $requestParams[$name]; + } else if ( array_key_exists($name, $queryParams) ) { + $redirectParams[$name] = $queryParams[$name]; + } + } + + $url = add_query_arg($redirectParams, $this->redirectUrl); + if ( wp_redirect($url) ) { + exit; + } else { + wp_die(wsAmeEscapeWpError(new WP_Error( + 'ame_redirect_failed', + 'Failed to redirect to the next page.' + ))); + } + } + + protected function handleSuccess($requestParams, $queryParams) { + if ( !empty($this->redirectUrl) ) { + $this->redirectToNextPage($requestParams, $queryParams, $this->successParams); + } else { + wp_die('Settings updated.'); + } + } + + /** + * Convert a WP_Error instance to an associative array. + * + * @param WP_Error $error + * @return array + */ + public static function errorToArray($error) { + $canGetAllData = method_exists($error, 'get_all_error_data'); //WP 5.6+ + + $errorArray = []; + foreach ($error->get_error_codes() as $code) { + $errorArray[$code] = ['messages' => $error->get_error_messages($code)]; + + if ( $canGetAllData ) { + $dataItems = $error->get_all_error_data($code); + } else { + $data = $error->get_error_data($code); + if ( $data !== null ) { + $dataItems = array($data); + } else { + $dataItems = array(); + } + } + $errorArray[$code]['data'] = $dataItems; + } + return $errorArray; + } + + /** + * Create a WP_Error instance from an array that was produced by errorToArray(). + * + * @param array $errorArray + * @return WP_Error + */ + public static function arrayToError($errorArray) { + $error = new WP_Error(); + foreach ($errorArray as $code => $details) { + foreach ($details['messages'] as $message) { + $error->add($code, $message); + } + if ( isset($details['data']) ) { + foreach ($details['data'] as $data) { + $error->add_data($data, $code); + } + } + } + return $error; + } +} \ No newline at end of file diff --git a/customizables/Validation/ColorValidator.php b/customizables/Validation/ColorValidator.php new file mode 100644 index 0000000..0f52a7a --- /dev/null +++ b/customizables/Validation/ColorValidator.php @@ -0,0 +1,21 @@ +add('invalid_color_string', 'Value must be a string'); + return $errors; + } + + $value = trim($value); + //Allow either 3 or 6 hex digits, but nothing in between. + //Alpha is technically allowed, but the WP color picker doesn't support it. + if ( !preg_match('/^#(?:[\da-f]{6}|[\da-f]{3})$/i', $value) ) { + $errors->add('invalid_hex_color', 'Value must be a valid CSS hex color'); + return $errors; + } + return $value; + } +} \ No newline at end of file diff --git a/customizables/Validation/StringValidator.php b/customizables/Validation/StringValidator.php new file mode 100644 index 0000000..0596dd7 --- /dev/null +++ b/customizables/Validation/StringValidator.php @@ -0,0 +1,70 @@ +minLength = $minLength; + $this->maxLength = $maxLength; + $this->truncateWithoutError = $truncateWithoutError; + $this->validationRegex = $regex; + $this->trimWhitespace = $trimWhitespace; + } + + public function __invoke($value, \WP_Error $errors) { + $convertedValue = strval($value); + if ( $this->trimWhitespace ) { + $convertedValue = trim($convertedValue); + } + + $length = strlen($convertedValue); + + if ( ($this->minLength !== null) && ($length < $this->minLength) ) { + $errors->add('min_length', 'Value is too short, minimum length is ' . $this->minLength); + return $errors; + } + + if ( ($this->maxLength !== null) && ($length > $this->maxLength) ) { + if ( $this->truncateWithoutError ) { + $convertedValue = substr($convertedValue, 0, $this->maxLength); + } else { + $errors->add('max_length', 'Value is too long, maximum length is ' . $this->maxLength); + return $errors; + } + } + + if ( $this->validationRegex !== null ) { + if ( !preg_match($this->validationRegex, $convertedValue) ) { + $errors->add( + 'regex_match_failed', + 'Value must match the following regex: ' . $this->validationRegex + ); + return $errors; + } + } + + return $convertedValue; + } + + public static function sanitizeStripTags($value) { + return wp_strip_all_tags((string) $value); + } +} \ No newline at end of file diff --git a/customizables/assets/_code-editor.scss b/customizables/assets/_code-editor.scss new file mode 100644 index 0000000..885b94a --- /dev/null +++ b/customizables/assets/_code-editor.scss @@ -0,0 +1,11 @@ +.ame-code-editor-control-wrap { + //Automatically resize CodeMirror to fit the contents. + .CodeMirror { + height: auto; + } + .CodeMirror-scroll { + overflow-y: hidden; + overflow-x: auto; + min-height: 100px; + } +} \ No newline at end of file diff --git a/customizables/assets/_combined-base-controls.scss b/customizables/assets/_combined-base-controls.scss new file mode 100644 index 0000000..321b43c --- /dev/null +++ b/customizables/assets/_combined-base-controls.scss @@ -0,0 +1,6 @@ +@import "image-selector"; +@import "code-editor"; +@import "number-input"; +@import "popup-slider"; +@import "radio-button-bar"; +@import "radio-group"; \ No newline at end of file diff --git a/customizables/assets/_image-selector.scss b/customizables/assets/_image-selector.scss new file mode 100644 index 0000000..9434f7e --- /dev/null +++ b/customizables/assets/_image-selector.scss @@ -0,0 +1,70 @@ +.ame-image-selector-v2 { + .ame-image-preview { + border: 1px dashed #b4b9be; + text-align: center; + background: #f1f1f1; + line-height: 0; + + display: inline-block; + box-sizing: border-box; + + img { + max-width: 100%; + } + } + + .ame-image-preview-placeholder { + display: inline-block; + padding: 10px; + line-height: normal; + } + + .ame-external-image-url { + margin-top: 0.4em; + } + + .ame-image-selector-actions { + display: block; + margin-top: 0.8em; + } + + .ame-remove-image-link { + line-height: 30px; //Must match the height of a secondary button. + margin-left: 1em; + } +} + +//Adjustments for the Admin Customizer module. +.ame-ac-control .ame-image-selector-v2 { + box-sizing: border-box; + width: 100%; + + //Keep the action buttons in one row. + .ame-image-selector-actions { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: baseline; + } + + //Add a small space between the "image" and "url" buttons. Usually, this happens + //automatically due to whitespace, but we need an explicit margin for a flex child. + .ame-set-external-image-url { + margin-left: 0.30769231em; //About 4px. + } + + //Align the "remove" action to the right. + .ame-remove-image-link { + margin-left: auto; + } + + .ame-image-preview { + box-sizing: border-box; + } + + //Make the image preview fill the available space when it's empty. + //This is for consistency with the image control in the Customizer. + .ame-image-preview-empty { + width: 100%; + } +} \ No newline at end of file diff --git a/customizables/assets/_number-input.scss b/customizables/assets/_number-input.scss new file mode 100644 index 0000000..1539303 --- /dev/null +++ b/customizables/assets/_number-input.scss @@ -0,0 +1,17 @@ +@use "sass:math"; +@import "../../css/forms"; + +.ame-number-input-control { + @include ame-invalid-input-styles; +} + +$smallInputWidth: 5.1em; +.ame-small-number-input { + width: $smallInputWidth; +} + +//Also make font size inputs smaller since the number will usually be only +//2-3 digits. It still needs to be big enough to fit a "Default" placeholder (if present). +.ame-font-size-input { + width: $smallInputWidth; +} \ No newline at end of file diff --git a/customizables/assets/_popup-slider.scss b/customizables/assets/_popup-slider.scss new file mode 100644 index 0000000..41758e5 --- /dev/null +++ b/customizables/assets/_popup-slider.scss @@ -0,0 +1,123 @@ +@use "sass:math"; + +.ame-container-with-popup-slider { + overflow: visible; //Let the slider show up fully even if the container is too small. +} + +.ame-popup-slider { + &, * { + box-sizing: border-box; + } + + $handleSize: 16px; + $tipHeight: 8px; + + //To make it easier to click the bar, the actual size of the clickable area + //is larger than the part that's colored in. + $barSize: $handleSize; + $barVisualSize: 4px; + + $topSpacing: math.max(0, math.round(($barSize - $barVisualSize) / 2)); + $sideSpacing: 0; + $barBorderRadius: 3px; + + $outerPadding: 8px; + $sliderBorderColor: #ccd0d4; + + position: absolute; + + border: 1px solid $sliderBorderColor; + border-radius: 4px; + background: white; + padding: $outerPadding ($outerPadding + math.max($barVisualSize, $handleSize/2)); + box-shadow: 0 3px 10px 0 rgba(0, 0, 0, 0.20); + + //Clickable area. The handle moves inside this area. + .ame-popup-slider-bar { + display: block; + height: $barSize; + position: relative; + overflow: visible; + + cursor: pointer; + } + + //Visual line inside the clickable area. + .ame-popup-slider-groove { + display: block; + height: 100%; + min-width: $handleSize; + //Note: This uses padding instead of margin to avoid margin collapsing. + //Setting the parent to "overflow: hidden" would also work, but we want + //the handle to stay completely visible even if it's bigger than the bar. + padding: $topSpacing $sideSpacing; + + &:before { + display: block; + content: " "; + width: 100%; + height: 100%; + background-color: #ebebeb; + border-radius: $barBorderRadius; + } + } + + .ame-popup-slider-handle { + cursor: pointer; + width: $handleSize; + height: $handleSize; + + position: absolute; + top: ($barSize - $handleSize); + margin-left: -($handleSize / 2); + + border-radius: 50%; + + background-color: #fff; + border: 2px solid #3582c4; + + //box-shadow: 0 1px 3px rgba(0, 0, 0, 0.20); + } + + //Note: Consider using SVG instead. This is too much work for a simple triangle with a border. + $tipSide: math.ceil(math.sqrt(2) * $tipHeight); + .ame-popup-slider-tip { + display: block; + + width: $tipSide * 2; + height: $tipSide; + + padding-right: $tipSide; + padding-left: 1px; + padding-bottom: 0; + + position: absolute; + top: -1 * math.ceil($tipSide); + overflow: hidden; + + pointer-events: none; + + &:after { + display: block; + content: " "; + width: $tipSide; + height: $tipSide; + background-color: white; + + box-shadow: 0 0 0 0.9px $sliderBorderColor; + + transform-origin: left bottom; + transform: rotate(45deg); + } + } + + .ame-popup-slider-top-tip { + top: -1 * math.ceil($tipSide); + } + + .ame-popup-slider-bottom-tip { + top: 100%; + //Flip it vertically. + transform: scaleY(-1); + } +} \ No newline at end of file diff --git a/customizables/assets/_radio-button-bar.scss b/customizables/assets/_radio-button-bar.scss new file mode 100644 index 0000000..ac84bee --- /dev/null +++ b/customizables/assets/_radio-button-bar.scss @@ -0,0 +1,69 @@ +@import "../../css/forms"; + +.ame-radio-button-bar-control { + display: flex; + flex-direction: row; + + input[type="radio"], input[type="checkbox"] { + @include ame-visually-hide-input; + } + + > label { + display: inline-block; + } + + //Selected option. Emulates the default .active style for buttons. + input[type="radio"]:checked ~ .button { + background-color: #dcdcde; + color: #135e96; + border-color: #0a4b78; + box-shadow: inset 0 2px 5px -3px #0a4b78; + z-index: 1; + } + + > .ame-radio-bar-item:not(:first-child) > .button { + margin-left: -1px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + > .ame-radio-bar-item:not(:last-child) > .button { + margin-right: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + //If the input is disabled, make the button look disabled too. + input[type="radio"]:disabled ~ .button { + color: #a7aaad; + border-color: #dcdcde; + background: #f6f7f7; + box-shadow: none; + cursor: default; + } + + .ame-radio-bar-button { + display: flex; + align-items: center; + + &.ame-rb-has-label { + .dashicons { + margin-right: 0.2em; + } + } + + //Unfortunately, Dashicons are not all the same size. Let's resize some + //individual icons to make them look more consistent. + .dashicons-image-rotate { + font-size: 17px; + line-height: 17px; + height: 16px; + } + + .dashicons-no, .dashicons-no-alt { + font-size: 22px; + line-height: 22px; + height: 22px; + } + } +} \ No newline at end of file diff --git a/customizables/assets/_radio-group.scss b/customizables/assets/_radio-group.scss new file mode 100644 index 0000000..2873548 --- /dev/null +++ b/customizables/assets/_radio-group.scss @@ -0,0 +1,71 @@ +.ame-rg-has-nested-controls { + //When a radio group has nested controls, use a two-column grid layout. + //The first column contains the radio buttons and their labels, the second + //column contains the nested controls. + + display: grid; + grid-template-columns: repeat(2, minmax(auto, max-content)); + column-gap: (16/13) * 1em; + row-gap: (8/13) * 1em; + align-items: center; + + .ame-rg-option-label { + grid-column: 1; + } + + .ame-rg-nested-control { + grid-column: 2; + } + + &.ame-rg-no-center-items { + align-items: normal; + } +} + +//Options that include a color picker need a special case because the color picker +//changes height when the user expands it. The radio buttons and their labels should +//be aligned with the default height of the color picker, and not move when the picker +//expands. +.ame-rg-with-color-pickers { + align-items: normal; + + //Hack: Add a vertical margin to the label so that it's aligned with the color picker. + .ame-rg-has-choice-child { + margin-top: (5/13) * 1em; + } + + //Also, the color picker should reserve space for the expanded state so that + //the radio buttons and their labels don't move when the picker expands. + //The default color picker width is 255px. It's set in /wp-admin/js/color-picker.js. + //The border adds 2px to the width, so we'll use 257px. + .ame-rg-nested-control .wp-picker-container { + min-width: 257px; + } +} + +//Layout adjustments for the customizer. +.ame-ac-control { + .ame-radio-group-component > p { + margin-top: 8px; + margin-bottom: 8px; + + &:first-of-type { + margin-top: 0; + } + + &:last-of-type { + margin-bottom: 0; + } + } + + //Nested color pickers don't fit on the same row as the radio buttons because + //there just isn't enough space. So, we'll move them to the next row. + .ame-rg-with-color-pickers { + .ame-rg-nested-control { + grid-column: 1; + .wp-picker-container { + min-width: unset; + } + } + } +} \ No newline at end of file diff --git a/customizables/assets/combined-controls.js b/customizables/assets/combined-controls.js new file mode 100644 index 0000000..0e3fadf --- /dev/null +++ b/customizables/assets/combined-controls.js @@ -0,0 +1,95 @@ +'use strict'; +/// +/* + * To avoid loading many small files, simple controls are combined into this single file. + */ +jQuery(function ($) { + //region Toggle checkbox + { + $('.ame-toggle-checkbox-control input[type="checkbox"]').each(function () { + //When this box is checked, disable the hidden field that has the same name. + const $box = $(this), $container = $box.closest('.ame-toggle-checkbox-control'), $alternative = $container.prev('input[name="' + $box.attr('name') + '"]'); + //The first update happens when the page is loaded. + $alternative.prop('disabled', $box.is(':checked')); + //Then enable/disable the hidden field whenever the box is checked or unchecked. + $box.on('change', function () { + $alternative.prop('disabled', $box.is(':checked')); + }); + }); + } + //endregion + //region Color picker + { + //Initialize color pickers. + jQuery(function ($) { + let $pickers = $('.ame-customizable-color-picker'); + //We don't need to initialize color pickers in the Admin Customizer + //module because the control class will do that. + //TODO: This could be unified. The same observable updater would work for both. + const $adminCustomizer = $('#ame-ac-admin-customizer'); + if ($adminCustomizer.length > 0) { + $pickers = $pickers.filter(function () { + return ($(this).parents('#ame-ac-admin-customizer').length < 1); + }); + } + $pickers.css('visibility', 'visible').each(function () { + //Trigger custom change events on the input element. We need to + //store a reference to the input because the "clear" event will + //run in the context of the "Clear" button, not the input. + const $picker = $(this); + $picker.wpColorPicker({ + change: function (event, ui) { + $picker.trigger('adminMenuEditor:colorPickerChange', [ui.color.toString()]); + $picker.trigger('adminMenuEditor:controlValueChanged', [ui.color.toString()]); + }, + clear: function () { + $picker.trigger('adminMenuEditor:colorPickerChange', ['']); + $picker.trigger('adminMenuEditor:controlValueChanged', ['']); + } + }); + //Update the color picker when the observable changes. + $picker.on('adminMenuEditor:observableValueChanged', function (event, newValue) { + if (typeof newValue !== 'string') { + newValue = ''; + } + if (newValue === '') { + //Programmatically click the "Clear" button. + $picker.closest('.wp-picker-input-wrap').find('.wp-picker-clear').trigger('click'); + } + else { + $picker.iris('color', newValue); + } + }); + }); + }); + } + //endregion + //region Content toggle + { + function findAssociatedItems($toggle) { + const itemSelector = $toggle.data('item-selector'); + const commonParentSelector = $toggle.data('parent-selector'); + return (commonParentSelector + ? $toggle.closest(commonParentSelector).find(itemSelector) + : $(itemSelector)); + } + //Toggle the visibility of other content when the user clicks a link or button. + $('.ame-content-toggle-control').on('click', function (event) { + event.preventDefault(); + const $link = $(this); + const $items = findAssociatedItems($link); + const newStateIsVisible = !$items.first().is(':visible'); + $items.toggle(newStateIsVisible); + const visibleText = $link.data('visible-state-text') || 'Hide details'; + const hiddenText = $link.data('hidden-state-text') || 'Show details'; + $link.text(newStateIsVisible ? visibleText : hiddenText); + }).filter('[data-default-state="hidden"]').each(function () { + //Hide content where it's set to be hidden by default. + const $link = $(this); + const $items = findAssociatedItems($link); + $items.hide(); + }); + } + //endregion +}); +//# sourceMappingURL=combined-controls.js.map \ No newline at end of file diff --git a/customizables/assets/combined-controls.js.map b/customizables/assets/combined-controls.js.map new file mode 100644 index 0000000..99bc204 --- /dev/null +++ b/customizables/assets/combined-controls.js.map @@ -0,0 +1 @@ +{"version":3,"file":"combined-controls.js","sourceRoot":"","sources":["combined-controls.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/customizables/assets/combined-controls.ts b/customizables/assets/combined-controls.ts new file mode 100644 index 0000000..ca60a13 --- /dev/null +++ b/customizables/assets/combined-controls.ts @@ -0,0 +1,111 @@ +'use strict'; +/// + +/* + * To avoid loading many small files, simple controls are combined into this single file. + */ + +jQuery(function ($: JQueryStatic) { + //region Toggle checkbox + { + $('.ame-toggle-checkbox-control input[type="checkbox"]').each(function (this: HTMLElement) { + //When this box is checked, disable the hidden field that has the same name. + const $box = $(this), + $container = $box.closest('.ame-toggle-checkbox-control'), + $alternative = $container.prev('input[name="' + $box.attr('name') + '"]') + + //The first update happens when the page is loaded. + $alternative.prop('disabled', $box.is(':checked')); + + //Then enable/disable the hidden field whenever the box is checked or unchecked. + $box.on('change', function () { + $alternative.prop('disabled', $box.is(':checked')); + }); + }); + } + //endregion + + //region Color picker + { + //Initialize color pickers. + jQuery(function ($: JQueryStatic) { + let $pickers = $('.ame-customizable-color-picker'); + + //We don't need to initialize color pickers in the Admin Customizer + //module because the control class will do that. + //TODO: This could be unified. The same observable updater would work for both. + const $adminCustomizer = $('#ame-ac-admin-customizer'); + if ($adminCustomizer.length > 0) { + $pickers = $pickers.filter(function (this: HTMLElement) { + return ($(this).parents('#ame-ac-admin-customizer').length < 1); + }); + } + + $pickers.css('visibility', 'visible').each(function (this: HTMLElement) { + //Trigger custom change events on the input element. We need to + //store a reference to the input because the "clear" event will + //run in the context of the "Clear" button, not the input. + const $picker = $(this); + $picker.wpColorPicker({ + change: function (event: JQueryEventObject, ui: any) { + $picker.trigger('adminMenuEditor:colorPickerChange', [ui.color.toString()]); + $picker.trigger('adminMenuEditor:controlValueChanged', [ui.color.toString()]); + }, + clear: function () { + $picker.trigger('adminMenuEditor:colorPickerChange', ['']); + $picker.trigger('adminMenuEditor:controlValueChanged', ['']); + } + }); + + //Update the color picker when the observable changes. + $picker.on('adminMenuEditor:observableValueChanged', function (event, newValue) { + if (typeof newValue !== 'string') { + newValue = ''; + } + if (newValue === '') { + //Programmatically click the "Clear" button. + $picker.closest('.wp-picker-input-wrap').find('.wp-picker-clear').trigger('click'); + } else { + $picker.iris('color', newValue); + } + }); + }); + }); + } + //endregion + + //region Content toggle + { + function findAssociatedItems($toggle: JQuery) { + const itemSelector = $toggle.data('item-selector'); + const commonParentSelector = $toggle.data('parent-selector'); + + return ( + commonParentSelector + ? $toggle.closest(commonParentSelector).find(itemSelector) + : $(itemSelector) + ); + } + + //Toggle the visibility of other content when the user clicks a link or button. + $('.ame-content-toggle-control').on('click', function (this: HTMLElement, event) { + event.preventDefault(); + + const $link = $(this); + const $items = findAssociatedItems($link); + + const newStateIsVisible = !$items.first().is(':visible'); + $items.toggle(newStateIsVisible); + + const visibleText = $link.data('visible-state-text') || 'Hide details'; + const hiddenText = $link.data('hidden-state-text') || 'Show details'; + $link.text(newStateIsVisible ? visibleText : hiddenText); + }).filter('[data-default-state="hidden"]').each(function (this: HTMLElement) { + //Hide content where it's set to be hidden by default. + const $link = $(this); + const $items = findAssociatedItems($link); + $items.hide(); + }); + } + //endregion +}); \ No newline at end of file diff --git a/customizables/assets/controls.css b/customizables/assets/controls.css new file mode 100644 index 0000000..86539c4 --- /dev/null +++ b/customizables/assets/controls.css @@ -0,0 +1,253 @@ +.ame-image-selector-v2 .ame-image-preview { + border: 1px dashed #b4b9be; + text-align: center; + background: #f1f1f1; + line-height: 0; + display: inline-block; + box-sizing: border-box; +} +.ame-image-selector-v2 .ame-image-preview img { + max-width: 100%; +} +.ame-image-selector-v2 .ame-image-preview-placeholder { + display: inline-block; + padding: 10px; + line-height: normal; +} +.ame-image-selector-v2 .ame-external-image-url { + margin-top: 0.4em; +} +.ame-image-selector-v2 .ame-image-selector-actions { + display: block; + margin-top: 0.8em; +} +.ame-image-selector-v2 .ame-remove-image-link { + line-height: 30px; + margin-left: 1em; +} + +.ame-ac-control .ame-image-selector-v2 { + box-sizing: border-box; + width: 100%; +} +.ame-ac-control .ame-image-selector-v2 .ame-image-selector-actions { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: baseline; +} +.ame-ac-control .ame-image-selector-v2 .ame-set-external-image-url { + margin-left: 0.30769231em; +} +.ame-ac-control .ame-image-selector-v2 .ame-remove-image-link { + margin-left: auto; +} +.ame-ac-control .ame-image-selector-v2 .ame-image-preview { + box-sizing: border-box; +} +.ame-ac-control .ame-image-selector-v2 .ame-image-preview-empty { + width: 100%; +} + +.ame-code-editor-control-wrap .CodeMirror { + height: auto; +} +.ame-code-editor-control-wrap .CodeMirror-scroll { + overflow-y: hidden; + overflow-x: auto; + min-height: 100px; +} + +.ame-number-input-control select:invalid, .ame-number-input-control select.ame-has-validation-errors, .ame-number-input-control input:invalid, .ame-number-input-control input.ame-has-validation-errors { + border-color: #d63638; +} +.ame-number-input-control select:invalid:focus, .ame-number-input-control select.ame-has-validation-errors:focus, .ame-number-input-control input:invalid:focus, .ame-number-input-control input.ame-has-validation-errors:focus { + box-shadow: 0 0 0 1px #d63638; +} + +.ame-small-number-input { + width: 5.1em; +} + +.ame-font-size-input { + width: 5.1em; +} + +.ame-container-with-popup-slider { + overflow: visible; +} + +.ame-popup-slider { + position: absolute; + border: 1px solid #ccd0d4; + border-radius: 4px; + background: white; + padding: 8px 16px; + box-shadow: 0 3px 10px 0 rgba(0, 0, 0, 0.2); +} +.ame-popup-slider, .ame-popup-slider * { + box-sizing: border-box; +} +.ame-popup-slider .ame-popup-slider-bar { + display: block; + height: 16px; + position: relative; + overflow: visible; + cursor: pointer; +} +.ame-popup-slider .ame-popup-slider-groove { + display: block; + height: 100%; + min-width: 16px; + padding: 6px 0; +} +.ame-popup-slider .ame-popup-slider-groove:before { + display: block; + content: " "; + width: 100%; + height: 100%; + background-color: #ebebeb; + border-radius: 3px; +} +.ame-popup-slider .ame-popup-slider-handle { + cursor: pointer; + width: 16px; + height: 16px; + position: absolute; + top: 0px; + margin-left: -8px; + border-radius: 50%; + background-color: #fff; + border: 2px solid #3582c4; +} +.ame-popup-slider .ame-popup-slider-tip { + display: block; + width: 24px; + height: 12px; + padding-right: 12px; + padding-left: 1px; + padding-bottom: 0; + position: absolute; + top: -12px; + overflow: hidden; + pointer-events: none; +} +.ame-popup-slider .ame-popup-slider-tip:after { + display: block; + content: " "; + width: 12px; + height: 12px; + background-color: white; + box-shadow: 0 0 0 0.9px #ccd0d4; + transform-origin: left bottom; + transform: rotate(45deg); +} +.ame-popup-slider .ame-popup-slider-top-tip { + top: -12px; +} +.ame-popup-slider .ame-popup-slider-bottom-tip { + top: 100%; + transform: scaleY(-1); +} + +.ame-radio-button-bar-control { + display: flex; + flex-direction: row; +} +.ame-radio-button-bar-control input[type=radio], .ame-radio-button-bar-control input[type=checkbox] { + position: absolute; + left: -9999em; + overflow: hidden; + clip: rect(0, 0, 0, 0); + margin: -1px; +} +.ame-radio-button-bar-control > label { + display: inline-block; +} +.ame-radio-button-bar-control input[type=radio]:checked ~ .button { + background-color: #dcdcde; + color: #135e96; + border-color: #0a4b78; + box-shadow: inset 0 2px 5px -3px #0a4b78; + z-index: 1; +} +.ame-radio-button-bar-control > .ame-radio-bar-item:not(:first-child) > .button { + margin-left: -1px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.ame-radio-button-bar-control > .ame-radio-bar-item:not(:last-child) > .button { + margin-right: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.ame-radio-button-bar-control input[type=radio]:disabled ~ .button { + color: #a7aaad; + border-color: #dcdcde; + background: #f6f7f7; + box-shadow: none; + cursor: default; +} +.ame-radio-button-bar-control .ame-radio-bar-button { + display: flex; + align-items: center; +} +.ame-radio-button-bar-control .ame-radio-bar-button.ame-rb-has-label .dashicons { + margin-right: 0.2em; +} +.ame-radio-button-bar-control .ame-radio-bar-button .dashicons-image-rotate { + font-size: 17px; + line-height: 17px; + height: 16px; +} +.ame-radio-button-bar-control .ame-radio-bar-button .dashicons-no, .ame-radio-button-bar-control .ame-radio-bar-button .dashicons-no-alt { + font-size: 22px; + line-height: 22px; + height: 22px; +} + +.ame-rg-has-nested-controls { + display: grid; + grid-template-columns: repeat(2, minmax(auto, max-content)); + column-gap: 1.2307692308em; + row-gap: 0.6153846154em; + align-items: center; +} +.ame-rg-has-nested-controls .ame-rg-option-label { + grid-column: 1; +} +.ame-rg-has-nested-controls .ame-rg-nested-control { + grid-column: 2; +} +.ame-rg-has-nested-controls.ame-rg-no-center-items { + align-items: normal; +} + +.ame-rg-with-color-pickers { + align-items: normal; +} +.ame-rg-with-color-pickers .ame-rg-has-choice-child { + margin-top: 0.3846153846em; +} +.ame-rg-with-color-pickers .ame-rg-nested-control .wp-picker-container { + min-width: 257px; +} + +.ame-ac-control .ame-radio-group-component > p { + margin-top: 8px; + margin-bottom: 8px; +} +.ame-ac-control .ame-radio-group-component > p:first-of-type { + margin-top: 0; +} +.ame-ac-control .ame-radio-group-component > p:last-of-type { + margin-bottom: 0; +} +.ame-ac-control .ame-rg-with-color-pickers .ame-rg-nested-control { + grid-column: 1; +} +.ame-ac-control .ame-rg-with-color-pickers .ame-rg-nested-control .wp-picker-container { + min-width: unset; +} + +/*# sourceMappingURL=controls.css.map */ diff --git a/customizables/assets/controls.css.map b/customizables/assets/controls.css.map new file mode 100644 index 0000000..d19cbd4 --- /dev/null +++ b/customizables/assets/controls.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["_image-selector.scss","_code-editor.scss","../../css/_forms.scss","_number-input.scss","_popup-slider.scss","_radio-button-bar.scss","_radio-group.scss"],"names":[],"mappings":"AACC;EACC;EACA;EACA;EACA;EAEA;EACA;;AAEA;EACC;;AAIF;EACC;EACA;EACA;;AAGD;EACC;;AAGD;EACC;EACA;;AAGD;EACC;EACA;;;AAKF;EACC;EACA;;AAGA;EACC;EACA;EACA;EACA;;AAKD;EACC;;AAID;EACC;;AAGD;EACC;;AAKD;EACC;;;ACjED;EACC;;AAED;EACC;EACA;EACA;;;ACOA;EACC,cALY;;AAQZ;EACC;;;ACZJ;EACC,OAFiB;;;AAOlB;EACC,OARiB;;;ACLlB;EACC;;;AAGD;EAoBC;EAEA;EACA;EACA;EACA;EACA;;AAzBA;EACC;;AA2BD;EACC;EACA,QA1BY;EA2BZ;EACA;EAEA;;AAID;EACC;EACA;EACA,WArCY;EAyCZ;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA,eAvCgB;;AA2ClB;EACC;EACA,OAvDY;EAwDZ,QAxDY;EA0DZ;EACA;EACA;EAEA;EAEA;EACA;;AAOD;EACC;EAEA;EACA,QALS;EAOT,eAPS;EAQT;EACA;EAEA;EACA;EACA;EAEA;;AAEA;EACC;EACA;EACA,OApBQ;EAqBR,QArBQ;EAsBR;EAEA;EAEA;EACA;;AAIF;EACC;;AAGD;EACC;EAEA;;;ACtHF;EACC;EACA;;AAEA;EHFA;EACA;EACA;EACA;EACA;;AGEA;EACC;;AAID;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;AAID;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EACA;;AAGC;EACC;;AAMF;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;;ACjEH;EAKC;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;;AAQF;EACC;;AAGA;EACC;;AAOD;EACC;;;AAMD;EACC;EACA;;AAEA;EACC;;AAGD;EACC;;AAOD;EACC;;AACA;EACC","file":"controls.css"} \ No newline at end of file diff --git a/customizables/assets/controls.scss b/customizables/assets/controls.scss new file mode 100644 index 0000000..064b441 --- /dev/null +++ b/customizables/assets/controls.scss @@ -0,0 +1 @@ +@import "combined-base-controls"; \ No newline at end of file diff --git a/customizables/assets/form-box.css b/customizables/assets/form-box.css new file mode 100644 index 0000000..6ea6cf5 --- /dev/null +++ b/customizables/assets/form-box.css @@ -0,0 +1,59 @@ +.ame-form-box { + box-sizing: border-box; + background: white; + border: 1px solid #ccd0d4; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); + margin-bottom: 20px; + margin-top: 0.5em; + padding: 0; +} + +.ame-form-box-header { + padding: 8px 12px; + border-bottom: 1px solid #eee; +} +.ame-form-box-header h2, .ame-form-box-header h3 { + margin: 0; + font-size: 14px; +} + +.ame-form-box-content { + margin: 12px; +} + +.ame-form-box-group { + margin: 15px 0; +} + +.ame-form-box-group-title { + display: inline-block; + vertical-align: top; + width: 200px; + padding-right: 10px; + font-weight: 600; + font-size: 14px; +} + +.ame-form-box-group-content { + display: inline-block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.ame-form-box-full-width-content { + width: 100%; +} + +.ame-form-box-main-column { + width: 540px; + float: left; + margin-right: 15px; +} + +.ame-form-box-sidebar-column { + width: 300px; + float: left; +} + +/*# sourceMappingURL=form-box.css.map */ diff --git a/customizables/assets/form-box.css.map b/customizables/assets/form-box.css.map new file mode 100644 index 0000000..4d3a08a --- /dev/null +++ b/customizables/assets/form-box.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["form-box.scss"],"names":[],"mappings":"AAGA;EACC;EACA;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;;AAEA;EACC;EACA;;;AAIF;EACC,QAzBmB;;;AA4BpB;EACC;;;AAGD;EACC;EACA;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC,OA3Dc;EA4Dd","file":"form-box.css"} \ No newline at end of file diff --git a/customizables/assets/form-box.scss b/customizables/assets/form-box.scss new file mode 100644 index 0000000..293e77b --- /dev/null +++ b/customizables/assets/form-box.scss @@ -0,0 +1,63 @@ +$internalBoxMargin: 12px; +$sidebarWidth: 300px; + +.ame-form-box { + box-sizing: border-box; + background: white; + border: 1px solid #ccd0d4; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); + + margin-bottom: 20px; + margin-top: 0.5em; + padding: 0; +} + +.ame-form-box-header { + padding: 8px $internalBoxMargin; + border-bottom: 1px solid #eee; + + h2, h3 { + margin: 0; + font-size: 14px; + } +} + +.ame-form-box-content { + margin: $internalBoxMargin; +} + +.ame-form-box-group { + margin: 15px 0; +} + +.ame-form-box-group-title { + display: inline-block; + vertical-align: top; + width: 200px; + padding-right: 10px; + + font-weight: 600; + font-size: 14px; +} + +.ame-form-box-group-content { + display: inline-block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.ame-form-box-full-width-content { + width: 100%; +} + +.ame-form-box-main-column { + width: 540px; + float: left; + margin-right: 15px; +} + +.ame-form-box-sidebar-column { + width: $sidebarWidth; + float: left; +} \ No newline at end of file diff --git a/customizables/assets/form-table.css b/customizables/assets/form-table.css new file mode 100644 index 0000000..7012e8e --- /dev/null +++ b/customizables/assets/form-table.css @@ -0,0 +1,25 @@ +.ame-rendered-form-table .ws_tooltip_trigger .dashicons { + font-size: 18px; + height: 14px; +} +.ame-rendered-form-table td .ws_tooltip_trigger .dashicons { + vertical-align: text-top; +} +.ame-rendered-form-table .ame-understated-tooltip .dashicons { + color: #777; +} +.ame-rendered-form-table .ame-understated-tooltip:hover .dashicons { + color: unset; +} +.ame-rendered-form-table th.ame-customizable-cg-has-tooltip label { + vertical-align: baseline; +} +.ame-rendered-form-table .ame-nested-section-header > td { + padding: 0; +} +.ame-rendered-form-table .ame-nested-section-header h3, .ame-rendered-form-table .ame-nested-section-header h4 { + font-size: 1.142857em; + color: #333A3F; +} + +/*# sourceMappingURL=form-table.css.map */ diff --git a/customizables/assets/form-table.css.map b/customizables/assets/form-table.css.map new file mode 100644 index 0000000..c719cc1 --- /dev/null +++ b/customizables/assets/form-table.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["form-table.scss"],"names":[],"mappings":"AACC;EAGC;EACA,QAHgB;;AAMjB;EACC;;AAGD;EACC;;AAGD;EACC;;AAMD;EACC;;AAIA;EACC;;AAGD;EACC;EACA","file":"form-table.css"} \ No newline at end of file diff --git a/customizables/assets/form-table.scss b/customizables/assets/form-table.scss new file mode 100644 index 0000000..6acf433 --- /dev/null +++ b/customizables/assets/form-table.scss @@ -0,0 +1,38 @@ +.ame-rendered-form-table { + .ws_tooltip_trigger .dashicons { + $reducedHeight: 14px; + + font-size: 18px; + height: $reducedHeight; + } + + td .ws_tooltip_trigger .dashicons { + vertical-align: text-top; + } + + .ame-understated-tooltip .dashicons { + color: #777; + } + + .ame-understated-tooltip:hover .dashicons { + color: unset; + } + + //WordPress sets the vertical alignment of labels to "middle" by default. + //For labels in group titles, we need to change that back to "baseline" + //to align the title with the tooltip trigger. + th.ame-customizable-cg-has-tooltip label { + vertical-align: baseline; + } + + .ame-nested-section-header { + > td { + padding: 0; + } + + h3, h4 { + font-size: 1.142857em; + color: #333A3F; + } + } +} \ No newline at end of file diff --git a/customizables/assets/image-selector.js b/customizables/assets/image-selector.js new file mode 100644 index 0000000..9de5e3d --- /dev/null +++ b/customizables/assets/image-selector.js @@ -0,0 +1,359 @@ +'use strict'; +var AmeImageSelectorApi; +(function (AmeImageSelectorApi) { + const $ = jQuery; + const _ = wsAmeLodash; + let mediaFrame = null, currentImageSelector = null; + const imageChangeEvents = [ + 'admin-menu-editor:media-image-removed', + 'admin-menu-editor:media-image-selected', + 'admin-menu-editor:external-image-selected' + ]; + AmeImageSelectorApi.imageChangeEventString = imageChangeEvents.join(' '); + class ImageSelector { + constructor($container, inputOptions = {}, initialImage = null) { + this.lastTriggeredImage = null; + this.$container = $container; + const usingExistingDom = ($container.find('input').length > 0); + if (!usingExistingDom) { + this.generateSelectorDom($container); + } + this.$attachmentId = $container.find('input.ame-image-attachment-id'); + this.$attachmentSiteId = $container.find('input.ame-image-attachment-site-id'); + this.$attachmentUrl = $container.find('input.ame-image-attachment-url'); + this.$externalUrl = $container.find('.ame-external-image-url'); + this.$externalUrlPreview = $container.find('.ame-external-image-url-preview'); + this.$width = $container.find('input.ame-detected-image-width'); + this.$height = $container.find('input.ame-detected-image-height'); + this.$selectImageButton = $container.find('.ame-select-image'); + this.$removeImageButton = $container.find('.ame-remove-image-link'); + this.$externalUrlButton = $container.find('.ame-set-external-image-url'); + const $previewPlaceholder = $container.find('.ame-image-preview .ame-image-preview-placeholder'); + const defaultOptions = { + canSelectMedia: usingExistingDom ? (!this.$selectImageButton.prop('disabled')) : true, + externalUrlsAllowed: usingExistingDom ? (this.$externalUrlPreview.length > 0) : true, + noImageText: usingExistingDom ? $previewPlaceholder.data('noImageText') : 'No image selected', + loadingText: usingExistingDom ? $previewPlaceholder.data('loadingText') : 'Loading...', + }; + this.options = $.extend({}, defaultOptions, inputOptions); + if (!this.options.canSelectMedia) { + this.$selectImageButton.prop('disabled', true); + } + if (!this.options.externalUrlsAllowed) { + this.$externalUrlPreview.hide(); + this.$externalUrlButton.hide(); + } + if (initialImage) { + this.lastTriggeredImage = this.withDefaults(initialImage); + this.updateDom(initialImage, null); + } + //The "Select Image" button. + this.$selectImageButton.on('click', (event) => { + event.preventDefault(); + currentImageSelector = this; + //If the media frame already exists, reopen it. + if (mediaFrame) { + mediaFrame.open(); + return; + } + //Initialize the media frame. + mediaFrame = wp.media({ + title: 'Select Image', + button: { + text: 'Select Image' + }, + library: { + type: 'image' + }, + multiple: false //Only select one image. + }); + //Save the choice when the user clicks the select button. + mediaFrame.on('select', function () { + //Get media attachment details from the frame. + const attachment = mediaFrame.state().get('selection').first().toJSON(); + let image = { + //Store the attachment ID. + attachmentId: attachment.id, + //Store the site ID. I assume that the Media Library only shows images + //from the current site, and the site ID doesn't change after page load. + attachmentSiteId: AmeIscBlogId || 0, + //In most cases, we don't need to store the attachment URL because + //the server-side code will get from the ID and overwrite the value. + //However, let's do it for screens that don't have full server-side processing. + attachmentUrl: attachment.url + }; + if (currentImageSelector !== null) { + currentImageSelector.setImage(image, attachment.url, 'admin-menu-editor:media-image-selected'); + } + }); + //Open the modal. + mediaFrame.open(); + }); + //The "Remove Image" link. + this.$removeImageButton.on('click', (event) => { + event.preventDefault(); + this.setImage({}, null, 'admin-menu-editor:media-image-removed'); + }); + //The "Set External URL" button. + this.$externalUrlButton.on('click', (event) => { + event.preventDefault(); + const oldUrl = this.$externalUrl.val(); + const newUrl = window.prompt('Please enter the image URL:', oldUrl); + if ((newUrl === null) || (newUrl === '')) { + //The user cancelled the prompt or left it empty. Do nothing. + return; + } + else if (!isPlausibleImageUrl(newUrl)) { + alert('Sorry, that doesn\'t look like a fully qualified image URL.'); + return; + } + this.setImage({ externalUrl: newUrl }, newUrl, 'admin-menu-editor:external-image-selected'); + }); + this.$container.on('adminMenuEditor:observableValueChanged', (event, data) => { + this.updateDom(data, null); + }); + //If the control is used in the Admin Customizer, use shorter + //labels for its buttons because the space is limited. + if (this.$container.closest('#ame-ac-admin-customizer').length > 0) { + this.shortenLabels(); + } + } + setImage(image, previewUrl, eventType = '') { + if ((image === null) || (typeof image === 'undefined')) { + image = {}; + } + this.updateDom(image, previewUrl); + this.triggerChangeEvents(image, eventType); + } + updateDom(image, previewUrl) { + this.$attachmentId.val(image.attachmentId || 0); + this.$attachmentSiteId.val(image.attachmentSiteId || 0); + this.$attachmentUrl.val(image.attachmentUrl || ''); + this.$externalUrl.val(image.externalUrl || ''); + this.$width.val(image.width || ''); + this.$height.val(image.height || ''); + const hasAttachment = !!image.attachmentId; + const hasExternalUrl = !!image.externalUrl; + const hasImage = hasAttachment || hasExternalUrl; + this.$externalUrlPreview.toggle(hasExternalUrl); + this.$removeImageButton.toggle(hasImage); + //Handle some cases where the image exists but the preview URL is not specified. + if (hasImage && !previewUrl) { + if (hasExternalUrl && (typeof image.externalUrl !== 'undefined')) { + previewUrl = image.externalUrl; + } + else if (hasAttachment && (typeof image.attachmentId !== 'undefined')) { + previewUrl = wp.media.attachment(image.attachmentId).get('url'); + //This may return undefined if the attachment hasn't been loaded yet. + //setPreviewImage() should handle that situation. + } + } + this.setPreviewImage(previewUrl, image.attachmentId); + } + setPreviewImage(imageUrl, attachmentId = null) { + const $preview = this.$container.find('.ame-image-preview'), $placeholder = $preview.find('.ame-image-preview-placeholder'); + //Remove the old image. + $preview.find('img').remove(); + if (!imageUrl && !attachmentId) { + //No image? Just show the placeholder. + $placeholder.text(this.options.noImageText).show(); + $preview.addClass('ame-image-preview-empty'); + return; + } + const addImage = (url) => { + $placeholder.hide(); + $preview.removeClass('ame-image-preview-empty'); + //Add a new image element. + const $img = $('Image preview'); + //Some modules need to know the dimensions of the image, e.g. to properly + //size a container element. This is easy for attachments, but in case the user + //chooses an external URL, we'll also store the width & height in hidden fields + //once the image loads. + this.$width.val(''); + this.$height.val(''); + //To ensure we don't miss the event, let's add the listener before setting + //the "src" attribute. + $img.on('load', () => { + const image = $img.get(0); + if (image && image.naturalWidth && image.naturalHeight) { + this.$width.val(image.naturalWidth); + this.$height.val(image.naturalHeight); + //Some fields have changed, so let's notify Knockout bindings. + this.triggerChangeEvents(this.readImageFromDom()); + } + }); + //Load the new image. + $img.attr('src', url); + $preview.append($img); + }; + if ((typeof imageUrl === 'string') && (imageUrl !== '')) { + addImage(imageUrl); + return; + } + //Try to load the attachment. + if (attachmentId) { + $placeholder.text(this.options.loadingText).show(); + /** + * Is the same attachment still selected? Note the intentional loose comparison. + */ + const isStillSameAttachment = () => { + return (this.$attachmentId.val() == attachmentId); + }; + const onLoadingDone = (url) => { + if (isStillSameAttachment()) { + if (url) { + addImage(url); + } + else { + //Failed to load the attachment. Show an error message. + $placeholder.text('Failed to load the image.').show(); + } + } + }; + wp.media.attachment(attachmentId).fetch().then( + //Success. + (attachment) => onLoadingDone(attachment && attachment.url), + //Error. + () => onLoadingDone(null)); + } + else { + this.setPreviewImage(null); + } + } + withDefaults(image) { + const defaults = { + attachmentId: 0, + attachmentSiteId: 0, + attachmentUrl: null, + externalUrl: null, + width: null, + height: null, + }; + let result = $.extend({}, defaults, image); + //Normalize empty URLs to null. + if (result.attachmentUrl === '') { + result.attachmentUrl = null; + } + if (result.externalUrl === '') { + result.externalUrl = null; + } + return result; + } + triggerChangeEvents(image, eventType = '') { + const fullImageObject = this.withDefaults(image); + //Avoid potential infinite loops: don't trigger events if the image hasn't changed. + if (this.lastTriggeredImage && _.isEqual(this.lastTriggeredImage, fullImageObject)) { + return; + } + this.lastTriggeredImage = fullImageObject; + //General image selector events. + if (eventType) { + this.$container.trigger(eventType, [fullImageObject]); + } + //Knockout integration event. + if ((typeof ko !== 'undefined') && this.$container.attr('data-bind')) { + this.$container.trigger('adminMenuEditor:controlValueChanged', [fullImageObject]); + } + } + readImageFromDom() { + return { + attachmentId: parseInt(this.$attachmentId.val(), 10) || 0, + attachmentSiteId: parseInt(this.$attachmentSiteId.val(), 10) || 0, + attachmentUrl: this.$attachmentUrl.val() || null, + externalUrl: this.$externalUrl.val() || null, + width: parseInt(this.$width.val(), 10) || null, + height: parseInt(this.$height.val(), 10) || null, + }; + } + shortenLabels() { + this.$container.find('.ame-image-selector-actions [data-ac-label]') + .each(function () { + const $action = $(this); + const label = $action.data('ac-label'); + if (label) { + if ($action.is('input')) { + $action.attr('value', label); + } + else { + $action.text(label); + } + } + }); + } + generateSelectorDom($container) { + $container.html(` + + + + + +
    + No image selected +
    +
    + +
    +
    + + + Remove Image +
    + `); + } + } + AmeImageSelectorApi.ImageSelector = ImageSelector; + function isPlausibleImageUrl(input) { + if (typeof URL !== 'undefined') { + try { + const url = new URL(input); + return ( + //Accept only HTTP(S). + ((url.protocol === "http:") || (url.protocol === "https:")) + //An image URL will usually have a path that's not just "/". + && (url.pathname.length > 1)); + } + catch (e) { + return false; + } + } + else { + const basicUrlValidator = /^https?:\/\/[-\w]+(?:\.[-\w]+)*(:\d{1,6})?\/./; + return !basicUrlValidator.test(input); + } + } +})(AmeImageSelectorApi || (AmeImageSelectorApi = {})); +jQuery(function ($) { + //Initialize image selectors. + $('.ame-image-selector-v2').each(function () { + const $this = $(this); + if ($this.data('ameIsComponent')) { + return; //Let components handle their own initialization. + } + new AmeImageSelectorApi.ImageSelector($(this)); + }); + //If an image selector is used in the Admin Customizer module, + //use the shorter labels because the sidebar is too narrow. + $('#ame-ac-admin-customizer') + .find('.ame-image-selector-v2 .ame-image-selector-actions [data-ac-label]') + .each(function () { + const $action = $(this); + const label = $action.data('ac-label'); + if (label) { + if ($action.is('input')) { + $action.attr('value', label); + } + else { + $action.text(label); + } + } + }); +}); +//# sourceMappingURL=image-selector.js.map \ No newline at end of file diff --git a/customizables/assets/image-selector.js.map b/customizables/assets/image-selector.js.map new file mode 100644 index 0000000..84ed48a --- /dev/null +++ b/customizables/assets/image-selector.js.map @@ -0,0 +1 @@ +{"version":3,"file":"image-selector.js","sourceRoot":"","sources":["image-selector.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/customizables/assets/image-selector.ts b/customizables/assets/image-selector.ts new file mode 100644 index 0000000..18b75f1 --- /dev/null +++ b/customizables/assets/image-selector.ts @@ -0,0 +1,455 @@ +'use strict'; + +declare const AmeIscBlogId: number; + +namespace AmeImageSelectorApi { + const $ = jQuery; + const _ = wsAmeLodash; + + let mediaFrame: null|ReturnType = null, + currentImageSelector: ImageSelector | null = null; + + const imageChangeEvents = [ + 'admin-menu-editor:media-image-removed', + 'admin-menu-editor:media-image-selected', + 'admin-menu-editor:external-image-selected' + ]; + export const imageChangeEventString = imageChangeEvents.join(' '); + + export interface ImageSettings { + attachmentId: number; + attachmentSiteId: number; + attachmentUrl?: string; + externalUrl: string | null; + width: number | null; + height: number | null; + } + + interface ImageSelectorOptions { + canSelectMedia: boolean; + externalUrlsAllowed: boolean; + noImageText: string; + loadingText: string; + } + + export class ImageSelector { + private readonly $container: JQuery; + private $attachmentId: JQuery; + private $attachmentSiteId: JQuery; + private $attachmentUrl: JQuery; + private $externalUrl: JQuery; + private $externalUrlPreview: JQuery; + private $width: JQuery; + private $height: JQuery; + + private $selectImageButton: JQuery; + private $removeImageButton: JQuery; + private $externalUrlButton: JQuery; + + private readonly options: ImageSelectorOptions; + private lastTriggeredImage: ImageSettings | null = null; + + constructor( + $container: JQuery, + inputOptions: Partial = {}, + initialImage: Partial | null = null + ) { + this.$container = $container; + + const usingExistingDom = ($container.find('input').length > 0); + if (!usingExistingDom) { + this.generateSelectorDom($container); + } + + this.$attachmentId = $container.find('input.ame-image-attachment-id'); + this.$attachmentSiteId = $container.find('input.ame-image-attachment-site-id'); + this.$attachmentUrl = $container.find('input.ame-image-attachment-url'); + this.$externalUrl = $container.find('.ame-external-image-url'); + this.$externalUrlPreview = $container.find('.ame-external-image-url-preview'); + this.$width = $container.find('input.ame-detected-image-width'); + this.$height = $container.find('input.ame-detected-image-height'); + + this.$selectImageButton = $container.find('.ame-select-image'); + this.$removeImageButton = $container.find('.ame-remove-image-link'); + this.$externalUrlButton = $container.find('.ame-set-external-image-url'); + + const $previewPlaceholder = $container.find('.ame-image-preview .ame-image-preview-placeholder'); + + const defaultOptions: ImageSelectorOptions = { + canSelectMedia: usingExistingDom ? (!this.$selectImageButton.prop('disabled')) : true, + externalUrlsAllowed: usingExistingDom ? (this.$externalUrlPreview.length > 0) : true, + noImageText: usingExistingDom ? $previewPlaceholder.data('noImageText') : 'No image selected', + loadingText: usingExistingDom ? $previewPlaceholder.data('loadingText') : 'Loading...', + }; + this.options = $.extend({}, defaultOptions, inputOptions); + + if (!this.options.canSelectMedia) { + this.$selectImageButton.prop('disabled', true); + } + if (!this.options.externalUrlsAllowed) { + this.$externalUrlPreview.hide(); + this.$externalUrlButton.hide(); + } + + if (initialImage) { + this.lastTriggeredImage = this.withDefaults(initialImage); + this.updateDom(initialImage, null); + } + + //The "Select Image" button. + this.$selectImageButton.on('click', (event) => { + event.preventDefault(); + currentImageSelector = this; + + //If the media frame already exists, reopen it. + if (mediaFrame) { + mediaFrame.open(); + return; + } + + //Initialize the media frame. + mediaFrame = wp.media({ + title: 'Select Image', + button: { + text: 'Select Image' + }, + library: { + type: 'image' + }, + multiple: false //Only select one image. + }); + + //Save the choice when the user clicks the select button. + mediaFrame.on('select', function () { + //Get media attachment details from the frame. + const attachment = mediaFrame.state().get('selection').first().toJSON(); + + let image: Partial = { + //Store the attachment ID. + attachmentId: attachment.id, + //Store the site ID. I assume that the Media Library only shows images + //from the current site, and the site ID doesn't change after page load. + attachmentSiteId: AmeIscBlogId || 0, + //In most cases, we don't need to store the attachment URL because + //the server-side code will get from the ID and overwrite the value. + //However, let's do it for screens that don't have full server-side processing. + attachmentUrl: attachment.url + }; + + if (currentImageSelector !== null) { + currentImageSelector.setImage( + image, + attachment.url, + 'admin-menu-editor:media-image-selected' + ); + } + }); + + //Open the modal. + mediaFrame.open(); + }); + + //The "Remove Image" link. + this.$removeImageButton.on('click', (event) => { + event.preventDefault(); + this.setImage({}, null, 'admin-menu-editor:media-image-removed'); + }); + + //The "Set External URL" button. + this.$externalUrlButton.on('click', (event) => { + event.preventDefault(); + + const oldUrl = this.$externalUrl.val(); + const newUrl = window.prompt('Please enter the image URL:', oldUrl); + if ((newUrl === null) || (newUrl === '')) { + //The user cancelled the prompt or left it empty. Do nothing. + return; + } else if (!isPlausibleImageUrl(newUrl)) { + alert('Sorry, that doesn\'t look like a fully qualified image URL.'); + return; + } + + this.setImage( + {externalUrl: newUrl}, + newUrl, + 'admin-menu-editor:external-image-selected' + ); + }); + + this.$container.on( + 'adminMenuEditor:observableValueChanged', + (event, data: Partial) => { + this.updateDom(data, null); + } + ); + + //If the control is used in the Admin Customizer, use shorter + //labels for its buttons because the space is limited. + if (this.$container.closest('#ame-ac-admin-customizer').length > 0) { + this.shortenLabels(); + } + } + + public setImage(image: Partial, previewUrl: string | null, eventType: string = '') { + if ((image === null) || (typeof image === 'undefined')) { + image = {}; + } + + this.updateDom(image, previewUrl); + this.triggerChangeEvents(image, eventType); + } + + protected updateDom(image: Partial, previewUrl: string | null) { + this.$attachmentId.val(image.attachmentId || 0); + this.$attachmentSiteId.val(image.attachmentSiteId || 0); + this.$attachmentUrl.val(image.attachmentUrl || ''); + this.$externalUrl.val(image.externalUrl || ''); + this.$width.val(image.width || ''); + this.$height.val(image.height || ''); + + const hasAttachment = !!image.attachmentId; + const hasExternalUrl = !!image.externalUrl; + const hasImage = hasAttachment || hasExternalUrl; + + this.$externalUrlPreview.toggle(hasExternalUrl); + this.$removeImageButton.toggle(hasImage); + + //Handle some cases where the image exists but the preview URL is not specified. + if (hasImage && !previewUrl) { + if (hasExternalUrl && (typeof image.externalUrl !== 'undefined')) { + previewUrl = image.externalUrl; + } else if (hasAttachment && (typeof image.attachmentId !== 'undefined')) { + previewUrl = wp.media.attachment(image.attachmentId).get('url'); + //This may return undefined if the attachment hasn't been loaded yet. + //setPreviewImage() should handle that situation. + } + } + + this.setPreviewImage(previewUrl, image.attachmentId); + } + + protected setPreviewImage(imageUrl: string | null | undefined, attachmentId: number | null = null) { + const $preview = this.$container.find('.ame-image-preview'), + $placeholder = $preview.find('.ame-image-preview-placeholder'); + + //Remove the old image. + $preview.find('img').remove(); + + if (!imageUrl && !attachmentId) { + //No image? Just show the placeholder. + $placeholder.text(this.options.noImageText).show(); + $preview.addClass('ame-image-preview-empty'); + return; + } + + const addImage = (url: string) => { + $placeholder.hide(); + $preview.removeClass('ame-image-preview-empty'); + + //Add a new image element. + const $img = $('Image preview'); + + //Some modules need to know the dimensions of the image, e.g. to properly + //size a container element. This is easy for attachments, but in case the user + //chooses an external URL, we'll also store the width & height in hidden fields + //once the image loads. + this.$width.val(''); + this.$height.val(''); + + //To ensure we don't miss the event, let's add the listener before setting + //the "src" attribute. + $img.on('load', () => { + const image = $img.get(0) as HTMLImageElement; + if (image && image.naturalWidth && image.naturalHeight) { + this.$width.val(image.naturalWidth); + this.$height.val(image.naturalHeight); + //Some fields have changed, so let's notify Knockout bindings. + this.triggerChangeEvents(this.readImageFromDom()); + } + }); + + //Load the new image. + $img.attr('src', url); + $preview.append($img); + } + + if ((typeof imageUrl === 'string') && (imageUrl !== '')) { + addImage(imageUrl); + return; + } + + //Try to load the attachment. + if (attachmentId) { + $placeholder.text(this.options.loadingText).show(); + + /** + * Is the same attachment still selected? Note the intentional loose comparison. + */ + const isStillSameAttachment = () => { + return (this.$attachmentId.val() == attachmentId); + } + + const onLoadingDone = (url: string | null) => { + if (isStillSameAttachment()) { + if (url) { + addImage(url); + } else { + //Failed to load the attachment. Show an error message. + $placeholder.text('Failed to load the image.').show(); + } + } + } + + wp.media.attachment(attachmentId).fetch().then( + //Success. + (attachment: any) => onLoadingDone(attachment && attachment.url), + //Error. + () => onLoadingDone(null) + ); + } else { + this.setPreviewImage(null); + } + } + + private withDefaults(image: Partial): ImageSettings { + const defaults = { + attachmentId: 0, + attachmentSiteId: 0, + attachmentUrl: null, + externalUrl: null, + width: null, + height: null, + } + let result = $.extend({}, defaults, image); + + //Normalize empty URLs to null. + if (result.attachmentUrl === '') { + result.attachmentUrl = null; + } + if (result.externalUrl === '') { + result.externalUrl = null; + } + return result; + } + + protected triggerChangeEvents(image: Partial, eventType: string = '') { + const fullImageObject: ImageSettings = this.withDefaults(image); + + //Avoid potential infinite loops: don't trigger events if the image hasn't changed. + if (this.lastTriggeredImage && _.isEqual(this.lastTriggeredImage, fullImageObject)) { + return; + } + this.lastTriggeredImage = fullImageObject; + + //General image selector events. + if (eventType) { + this.$container.trigger(eventType, [fullImageObject]); + } + //Knockout integration event. + if ((typeof ko !== 'undefined') && this.$container.attr('data-bind')) { + this.$container.trigger('adminMenuEditor:controlValueChanged', [fullImageObject]); + } + } + + private readImageFromDom(): Partial { + return { + attachmentId: parseInt(this.$attachmentId.val(), 10) || 0, + attachmentSiteId: parseInt(this.$attachmentSiteId.val(), 10) || 0, + attachmentUrl: this.$attachmentUrl.val() || null, + externalUrl: this.$externalUrl.val() || null, + width: parseInt(this.$width.val(), 10) || null, + height: parseInt(this.$height.val(), 10) || null, + }; + } + + private shortenLabels() { + this.$container.find('.ame-image-selector-actions [data-ac-label]') + .each(function (this: HTMLElement) { + const $action = $(this); + const label = $action.data('ac-label'); + if (label) { + if ($action.is('input')) { + $action.attr('value', label); + } else { + $action.text(label); + } + } + }); + } + + private generateSelectorDom($container: JQuery) { + $container.html(` + + + + + +
    + No image selected +
    +
    + +
    +
    + + + Remove Image +
    + `); + } + } + + function isPlausibleImageUrl(input: string) { + if (typeof URL !== 'undefined') { + try { + const url = new URL(input); + return ( + //Accept only HTTP(S). + ((url.protocol === "http:") || (url.protocol === "https:")) + //An image URL will usually have a path that's not just "/". + && (url.pathname.length > 1) + ); + } catch (e) { + return false; + } + } else { + const basicUrlValidator = /^https?:\/\/[-\w]+(?:\.[-\w]+)*(:\d{1,6})?\/./; + return !basicUrlValidator.test(input); + } + } +} + +jQuery(function ($: JQueryStatic) { + //Initialize image selectors. + $('.ame-image-selector-v2').each(function (this: HTMLElement) { + const $this = $(this); + if ($this.data('ameIsComponent')) { + return; //Let components handle their own initialization. + } + new AmeImageSelectorApi.ImageSelector($(this)); + }); + + //If an image selector is used in the Admin Customizer module, + //use the shorter labels because the sidebar is too narrow. + $('#ame-ac-admin-customizer') + .find('.ame-image-selector-v2 .ame-image-selector-actions [data-ac-label]') + .each(function (this: HTMLElement) { + const $action = $(this); + const label = $action.data('ac-label'); + if (label) { + if ($action.is('input')) { + $action.attr('value', label); + } else { + $action.text(label); + } + } + }); +}); diff --git a/customizables/assets/popup-slider.d.ts b/customizables/assets/popup-slider.d.ts new file mode 100644 index 0000000..1edd821 --- /dev/null +++ b/customizables/assets/popup-slider.d.ts @@ -0,0 +1,32 @@ +/* + * TypeScript definitions for the internal popup slider. It's exposed in the global + * scope as "AmePopupSlider". + */ + +declare class AmePopupSlider { + constructor($container: JQuery, options?: AmePopupSliderOptions); + + showForInput($input: JQuery): void; + + static createSlider($container: JQuery, options?: AmePopupSliderOptions): AmePopupSlider; +} + +declare type AmePopupSliderRanges = Record; + +declare interface AmePopupSliderOptions { + positionParentSelector?: string; + verticalOffset?: number; + /** + * A dictionary of slider ranges. Overrides the ranges specified in the HTML if present. + */ + ranges?: AmePopupSliderRanges|null; + /** + * A jQuery object that represents the slider element, if it has already been created. + */ + sliderElement?: JQuery|null; + positionWithinClosest?: string; +} \ No newline at end of file diff --git a/customizables/assets/popup-slider.js b/customizables/assets/popup-slider.js new file mode 100644 index 0000000..688811d --- /dev/null +++ b/customizables/assets/popup-slider.js @@ -0,0 +1,369 @@ +jQuery(function ($) { + const ownEventMarker = {'isAmeSliderEvent': true}; + + /** + * @param {JQuery} $container + * @param {AmePopupSliderOptions} [options] + * @constructor + */ + function PopupSlider($container, options) { + const self = this; + + this.minWidth = 240; + this.maxWidth = 600; + + this.$container = $container; + this.$popup = (options && options.sliderElement) ? options.sliderElement : $container.find('.ame-popup-slider'); + this.$bar = this.$popup.find('.ame-popup-slider-bar'); + this.$activeInput = null; + + this.options = jQuery.extend( + { + positionParentSelector: '', + verticalOffset: 0, + ranges: null, + }, + this.$popup.data('amePopupSliderOptions') || {}, + options || {} + ); + + //Ensure that the default range is always present, even if the user didn't specify it. + if (this.options.ranges && !this.options.ranges.hasOwnProperty('_default')) { + this.options.ranges['_default'] = {min: 0, max: 100, step: 1} + } + + // noinspection JSUnusedGlobalSymbols - IDE fail, "slide" is a valid option for a jQuery UI slider. + this.$bar.slider({ + slide: function (event, ui) { + if (!self.$activeInput) { + return; + } + + self.$activeInput.val(ui.value); + + /* + Trigger an event so that other scripts (e.g. Admin Customizer) + can react to the change. + + Important: Using a custom namespace would not work here because that + would only trigger handlers that explicitly specify that namespace. + + An event listener added using "$.on('input.custom')" will also + receive regular "input" events, but "$.trigger('input.custom')" + will NOT trigger handlers that listen for "input" events. It will + only trigger handlers attached using the "custom" namespace, such + as "input.whatever.custom". + */ + self.$activeInput.trigger('input', [ownEventMarker]); + + self.$activeInput.trigger('adminMenuEditor:controlValueChanged', ui.value); + } + }); + + //Don't let the slider handle steal focus from the input. + this.$bar.find('.ui-slider-handle').removeAttr('tabindex'); + + /* + Hide the slider when the user moves focus or interacts with something + that's not the input field or the slider. This event handler is only + added when the slider is shown. + + Note: This uses "mousedown" instead of "click" to keep the slider visible + in the situation where the user begins dragging the handle and then releases + the mouse button outside the slider box. If we used "click" instead, the click + event would trigger on the outside element and the slider would be hidden. + */ + this.hideOnOutsideInteraction = function (e) { + if ( + !self.$activeInput.is(e.target) + && !self.$popup.is(e.target) + && !jQuery.contains(self.$popup.get(0), e.target) + ) { + self.$popup.hide(); + + //Remove the event handler. + if (self.$activeInput) { + self.$activeInput.off('.ameSliderActiveInputChange'); + } + + self.$activeInput = null; + self.toggleHideEventListener(false); + } + }; + this.isHideListenerActive = false; + } + + PopupSlider.prototype.toggleHideEventListener = function (addListener) { + if (this.isHideListenerActive === addListener) { + return; //Nothing to do. + } + + const events = 'mousedown focusin'; + const $body = $('body'); + if (addListener) { + $body.on(events, this.hideOnOutsideInteraction); + this.isHideListenerActive = true; + } else { + $body.off(events, this.hideOnOutsideInteraction); + this.isHideListenerActive = false; + } + } + + /** + * @param {JQuery} $input + */ + PopupSlider.prototype.showForInput = function ($input) { + if ((this.$activeInput === $input) && this.$popup.is(':visible')) { + return; //Already visible and attached to the same input. + } + + //Remove event handlers associated with the previous input. + if (this.$activeInput) { + this.$activeInput.off('.ameSliderActiveInputChange'); + this.toggleHideEventListener(false); + } + + this.$activeInput = $input; + this.$popup.show(); + + //The slider should be as wide as the combination of the input and the unit + //field (if any), or at least this.minWidth pixels. + const unitElementId = $input.data('unit-element-id'); + const $unit = unitElementId ? this.$container.find('#' + unitElementId) : $(); + const unitWidth = $unit.length ? $unit.outerWidth() : 0; + const inputWidth = $input.outerWidth(); + + const popupWidth = Math.ceil( + Math.max( + this.minWidth, + Math.min(inputWidth + unitWidth, this.maxWidth) + ) + ); + this.$popup.outerWidth(popupWidth); + + //Position the dropdown below the input by default. Alternatively, you can + //position it below a parent element that matches a selector. + let $subject = $input; + if (this.options['positionParentSelector']) { + const $matchingParent = $input.closest(this.options['positionParentSelector']); + if ($matchingParent.length > 0) { + $subject = $matchingParent; + } + } + + const verticalOffset = this.options['verticalOffset'] || 0; + + //Don't forget about the tip/arrow. + const $tip = this.$popup.find('.ame-popup-slider-tip'); + const tipHeight = $tip.outerHeight(); + //The visual height of the tip is smaller because it's rotated 45 degrees + //and moved to cover the top border of the dropdown. + const tipVisualHeight = Math.ceil(tipHeight / Math.sqrt(2)) + 1; + + let positionOptions = { + my: 'left top+' + Math.max((tipVisualHeight + verticalOffset), 0), + at: 'left bottom', + of: $subject, + collision: 'fit flip' + }; + //Optionally, try to position the popup within the closest matching parent. + if (this.options.positionWithinClosest) { + const $within = $subject.closest(this.options.positionWithinClosest); + if ($within.length > 0) { + positionOptions.within = $within; + } + } + this.$popup.position(positionOptions); + + //Try to center the tip relative to the subject, on the X axis. + //The tip's width also looks different because it's rotated. + const tipVisualWidth = Math.ceil($tip.outerWidth() / Math.sqrt(2)) + 2; + const subjectWidth = $subject.outerWidth() || 0; + //The left edge of the popup might not be actually aligned with the left + //edge of the subject if that would cause it to overflow the window/container. + //We need to take that into account when calculating the tip's position. + const subjectLeft = $subject.offset().left || 0; + const popupLeft = this.$popup.offset().left || 0; + const desiredTipOffset = Math.round( + (subjectLeft - popupLeft) + + (subjectWidth - tipVisualWidth) / 2 + - 1.5 //Fudging a bit to account for unknown bugs. + ); + //The tip should stay within the popup's bounds. + const minTipOffset = 1; + const maxTipOffset = popupWidth - tipVisualWidth - 1; + + $tip.css('left', Math.max(Math.min(desiredTipOffset, maxTipOffset), minTipOffset)); + + //If the popup is below the subject, show only the top tip. + //Otherwise, show only the bottom tip. + const popupTop = this.$popup.offset().top || 0; + const subjectTop = $subject.offset().top || 0; + const $topTip = $tip.filter('.ame-popup-slider-top-tip'); + const $bottomTip = $tip.filter('.ame-popup-slider-bottom-tip'); + if (popupTop > subjectTop) { + $topTip.show(); + $bottomTip.hide(); + } else { + $topTip.hide(); + $bottomTip.show(); + } + + //Update slider range based on the selected unit. + const range = this.getCurrentRange($input, $unit); + this.$bar.slider('option', { + min: range.min, + max: range.max, + step: range.step + }); + + //Update the slider value. + this.updateValueFromInput($input); + + //Update the slider when the input value changes. + const self = this; + $input.on('input.ameSliderActiveInputChange', function (e, extraParam) { + //Ignore changes caused by the slider itself. + if (extraParam === ownEventMarker) { + return; + } + self.updateValueFromInput(self.$activeInput); + }); + + $input.on( + 'adminMenuEditor:observableValueChanged.ameSliderActiveInputChange', + function (e, newValue) { + self.updateValueDirectly(newValue); + } + ); + + this.toggleHideEventListener(true); + }; + + PopupSlider.prototype.getCurrentRange = function ($input, $unit) { + if (!$unit) { + const unitElementId = $input.data('unit-element-id'); + $unit = unitElementId ? this.$container.find('#' + unitElementId) : $(); + } + + //Use the custom ranges if specified, or extract them from the input/unit fields. + const ranges = this.options.ranges + ? this.options.ranges + : this.getInputRanges($input, $unit); + + const unit = $unit.is('span, div') ? $unit.data('number-unit') : $unit.val(); + return ranges[unit] || ranges['_default']; + } + + PopupSlider.prototype.getInputRanges = function ($input, $unit) { + let ranges = $input.data('ameParsedSliderRanges'); + if (ranges) { + return ranges; + } + + ranges = this.extractRanges($input); + if (!ranges) { + ranges = this.extractRanges($unit); + if (!ranges) { + ranges = {}; + } + } + + if (typeof ranges['_default'] === 'undefined') { + ranges['_default'] = { + min: parseFloat($input.attr('min')) || 0, + max: parseFloat($input.attr('max')) || 100, + step: parseFloat($input.attr('step')) || 1 + }; + } + + $input.data('ameParsedSliderRanges', ranges); + return ranges; + } + + PopupSlider.prototype.extractRanges = function ($element) { + if (!$element) { + return null; + } + + const config = $element.data('slider-ranges'); + if (!config || (typeof config !== 'object')) { + return null; + } + return config; + }; + + PopupSlider.prototype.updateValueFromInput = function ($input) { + this.updateValueDirectly($input.val()); + }; + + PopupSlider.prototype.updateValueDirectly = function (rawValue) { + const value = ((typeof rawValue === 'string') ? parseFloat(rawValue) : rawValue) || 0; + this.$bar.slider('value', value); + } + + /** + * Create a slider for the specified container. + * + * Automatically generates the slider's markup if it doesn't exist yet. + * + * @param {JQuery} $container + * @param {AmePopupSliderOptions} [options] + */ + PopupSlider.createSlider = function ($container, options) { + if ((typeof options === 'undefined') || (options === null)) { + options = {}; + } + + let $popup = options.sliderElement || $container.find('.ame-popup-slider'); + if (!$popup || ($popup.length === 0)) { + $popup = $(''); + $popup.append(` +
    +
    +
    +
    +
    +
    + `); + //Add the slider to the body instead of the container so that it can + //extend outside the container's bounds without being clipped. + jQuery('body').append($popup); + + //The slider should have a higher z-index than the container + //or any of the container's parents so that it's always visible. + let maxZIndex = 0; + $container.parentsUntil('body').addBack().each(function () { + const zIndex = parseInt($(this).css('z-index'), 10); + if ((zIndex > 0) && (zIndex > maxZIndex)) { + maxZIndex = zIndex; + } + }); + if (maxZIndex > 0) { + $popup.css('z-index', maxZIndex + 1); + } + + options.sliderElement = $popup; + } + + return new PopupSlider($container, options); + } + + //Expose the class in the global scope so that Knockout components can use it. + window.AmePopupSlider = PopupSlider; + + //Show the slider when the user clicks on an input. + $('.ame-container-with-popup-slider .ame-input-with-popup-slider').on('click', function () { + const $input = $(this); + /** @type {JQuery} */ + const $container = $input.closest('.ame-container-with-popup-slider'); + + let popupSlider = $container.data('amePopupSlider'); + if (!popupSlider) { + popupSlider = new PopupSlider($container); + $container.data('amePopupSlider', popupSlider); + } + + popupSlider.showForInput($input); + }); +}); \ No newline at end of file diff --git a/customizables/assets/tabbed-panels.css b/customizables/assets/tabbed-panels.css new file mode 100644 index 0000000..3627fde --- /dev/null +++ b/customizables/assets/tabbed-panels.css @@ -0,0 +1,120 @@ +.ame-tabbed-panel { + display: flex; + flex-direction: row; +} + +.ame-tp-tabs { + display: flex; + flex-direction: column; + width: 200px; + margin: 0; + border-right: 1px solid #dcdcde; + background-color: #f7f7f7; +} +.ame-tp-tabs li { + margin: 0; +} +.ame-tp-tabs li.ui-state-active { + background-color: #fff; +} +.ame-tp-tabs li.ui-state-active a { + color: #333; + border-right-color: #fff; + font-weight: 600; + box-shadow: none; +} +.ame-tp-tabs a { + display: block; + padding: 8px 10px; + text-decoration: none; + border-bottom: 1px solid #dcdcde; + border-right: 1px solid transparent; + margin-right: -1px; + font-size: 14px; + box-shadow: none; +} + +.ame-tp-content { + flex: 1; + padding: 0 12px 8px 12px; + background: white; +} + +.ame-tp-section { + font-size: 13px; +} +.ame-tp-section .ame-tp-control-group-title { + font-weight: 600; + font-size: 1.0769230769em; + margin: 0 0 0.2857142857em 0; + width: 12.8571428571em; + min-width: 3.5714285714em; + flex-grow: 0; +} +.ame-tp-section .ame-tp-control-group { + margin-bottom: 0.6153846154em; + display: flex; + flex-wrap: wrap; +} +.ame-tp-section .ame-tp-control-group-children .wp-color-result { + margin-bottom: 0; +} +.ame-tp-section .ame-tp-control-group-children fieldset > p { + margin: 0 0 4px 0; +} +.ame-tp-section .ame-tp-section { + margin-bottom: 1.1538461538em; +} +.ame-tp-section .ame-tp-section-children .ame-tp-section-title { + font-size: 1.1538461538em; + border-width: 1px 0; + border-style: solid; + border-color: #ccc; + background: #f9f9f9; + padding-top: 3px; + padding-bottom: 4px; + /*margin-left: -$contentHorizontalPadding; + margin-right: -$contentHorizontalPadding; + padding-left: $contentHorizontalPadding; + padding-right: $contentHorizontalPadding;*/ +} + +.ame-tp-section .ame-tp-control-group-title { + padding-top: 4px; +} +.ame-tp-section .ame-tp-control-group { + margin-bottom: 1.0769230769em; +} +.ame-tp-section .ame-tp-control-group-children > label { + padding-top: 0.3846153846em; +} + +.ame-tp-height-100.ame-tabbed-panel { + height: 100%; +} +.ame-tp-height-100 .ame-tp-content { + box-sizing: border-box; + height: 100%; + padding-right: 0; +} +.ame-tp-height-100 .ame-tp-content > .ui-tabs-panel { + box-sizing: border-box; + height: 100%; + overflow-y: auto; + padding-right: 12px; +} +.ame-tp-height-100 .ame-tp-tabs { + box-sizing: border-box; + height: 100%; + /* + Overflow must be visible or the "push the active tab over the border to hide the border" + trick won't work. Apparently, combining "overflow-x: visible" with "overflow-y: auto" + causes the browser to convert "visible" to "auto". + + @see https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue/6433475#6433475 + */ + overflow-y: visible; + overflow-x: visible; +} + +/*# sourceMappingURL=tabbed-panels.css.map */ diff --git a/customizables/assets/tabbed-panels.css.map b/customizables/assets/tabbed-panels.css.map new file mode 100644 index 0000000..30390e1 --- /dev/null +++ b/customizables/assets/tabbed-panels.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["tabbed-panels.scss"],"names":[],"mappings":"AAOA;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;EACA;;AAEA;EACC;;AAMA;EACC;;AAEA;EACC;EACA;EACA;EACA;;AAKH;EACC;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;;;AAIF;EACC;EACA;EACA;;;AAGD;EACC;;AAEA;EAGC;EACA;EACA;EAEA;EACA;EAEA;;AAGD;EACC;EAEA;EACA;;AAKA;EACC;;AAGD;EACC;;AAIF;EACC;;AAKA;EACC;EAEA;EACA;EACA;EACA;EAEA;EACA;AAGA;AAAA;AAAA;AAAA;;;AAUF;EACC;;AAGD;EACC;;AAMA;EACC;;;AAQF;EACC;;AAGD;EACC;EACA;EAKA;;AAGD;EACC;EACA;EACA;EAGA,eA7JyB;;AAgK1B;EACC;EACA;AAEA;AAAA;AAAA;AAAA;;AAAA;AAAA;EAOA;EACA","file":"tabbed-panels.css"} \ No newline at end of file diff --git a/customizables/assets/tabbed-panels.scss b/customizables/assets/tabbed-panels.scss new file mode 100644 index 0000000..08d7b56 --- /dev/null +++ b/customizables/assets/tabbed-panels.scss @@ -0,0 +1,180 @@ +$baseFontSize: 13; +@function pxToEm($px, $baseSize: $baseFontSize) { + @return ($px / $baseSize) * 1em; +} + +$contentHorizontalPadding: 12px; + +.ame-tabbed-panel { + display: flex; + flex-direction: row; +} + +.ame-tp-tabs { + display: flex; + flex-direction: column; + width: 200px; + margin: 0; + + border-right: 1px solid #dcdcde; + background-color: #f7f7f7; + + li { + margin: 0; + + &:hover { + + } + + &.ui-state-active { + background-color: #fff; + + a { + color: #333; + border-right-color: #fff; + font-weight: 600; + box-shadow: none; + } + } + } + + a { + display: block; + padding: 8px 10px; + text-decoration: none; + border-bottom: 1px solid #dcdcde; + + border-right: 1px solid transparent; + margin-right: -1px; + + font-size: 14px; + box-shadow: none; + } +} + +.ame-tp-content { + flex: 1; + padding: 0 $contentHorizontalPadding 8px $contentHorizontalPadding; + background: white; +} + +.ame-tp-section { + font-size: $baseFontSize * 1px; + + .ame-tp-control-group-title { + $titleFontSize: 14; + + font-weight: 600; + font-size: pxToEm($titleFontSize); + margin: 0 0 pxToEm(4, $titleFontSize) 0; + + width: pxToEm(180, $titleFontSize); + min-width: pxToEm(50, $titleFontSize); + + flex-grow: 0; + } + + .ame-tp-control-group { + margin-bottom: pxToEm(8); + + display: flex; + flex-wrap: wrap; + } + + .ame-tp-control-group-children { + //Adjust the layout of some child controls that look weird outside a form table. + .wp-color-result { + margin-bottom: 0; + } + + fieldset > p { + margin: 0 0 4px 0; + } + } + + .ame-tp-section { + margin-bottom: pxToEm(15); + } + + //Nested section titles. + .ame-tp-section-children { + .ame-tp-section-title { + font-size: pxToEm(15); + + border-width: 1px 0; + border-style: solid; + border-color: #ccc; + background: #f9f9f9; + + padding-top: 3px; + padding-bottom: 4px; + + //Expand the title background to the left and right edges of the content area. + /*margin-left: -$contentHorizontalPadding; + margin-right: -$contentHorizontalPadding; + padding-left: $contentHorizontalPadding; + padding-right: $contentHorizontalPadding;*/ + } + } +} + +//For layout with left-aligned labels. +.ame-tp-section { + .ame-tp-control-group-title { + padding-top: 4px; + } + + .ame-tp-control-group { + margin-bottom: pxToEm(14); + } + + .ame-tp-control-group-children { + //Move plain controls (like a checkbox in a label element) down + //to match the group title position. + > label { + padding-top: pxToEm(5); + } + } +} + +//Alternative layout that takes up 100% of the available height and scrolls +//the tab contents they're too tall. +.ame-tp-height-100 { + &.ame-tabbed-panel { + height: 100% + } + + .ame-tp-content { + box-sizing: border-box; + height: 100%; + + //Move the padding to the individual tabs instead of the content area. + //A tab's scroll bar should be flush to the right edge, without any extra + //padding after it. + padding-right: 0; + } + + .ame-tp-content > .ui-tabs-panel { + box-sizing: border-box; + height: 100%; + overflow-y: auto; + + //Content padding was moved here from the content area. + padding-right: $contentHorizontalPadding; + } + + .ame-tp-tabs { + box-sizing: border-box; + height: 100%; + + /* + Overflow must be visible or the "push the active tab over the border to hide the border" + trick won't work. Apparently, combining "overflow-x: visible" with "overflow-y: auto" + causes the browser to convert "visible" to "auto". + + @see https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue/6433475#6433475 + */ + overflow-y: visible; + overflow-x: visible; + } +} diff --git a/customizables/assets/tinymce-5.d.ts b/customizables/assets/tinymce-5.d.ts new file mode 100644 index 0000000..f69c840 --- /dev/null +++ b/customizables/assets/tinymce-5.d.ts @@ -0,0 +1,2820 @@ +interface StringPathBookmark { + start: string; + end?: string; +} +interface RangeBookmark { + rng: Range; +} +interface IdBookmark { + id: string; + keep?: boolean; +} +interface IndexBookmark { + name: string; + index: number; +} +interface PathBookmark { + start: number[]; + end?: number[]; +} +declare type Bookmark = StringPathBookmark | RangeBookmark | IdBookmark | IndexBookmark | PathBookmark; +declare type MappedEvent = K extends keyof T ? T[K] : any; +interface NativeEventMap { + 'beforepaste': Event; + 'blur': FocusEvent; + 'beforeinput': InputEvent; + 'click': MouseEvent; + 'compositionend': Event; + 'compositionstart': Event; + 'compositionupdate': Event; + 'contextmenu': PointerEvent; + 'copy': ClipboardEvent; + 'cut': ClipboardEvent; + 'dblclick': MouseEvent; + 'drag': DragEvent; + 'dragdrop': DragEvent; + 'dragend': DragEvent; + 'draggesture': DragEvent; + 'dragover': DragEvent; + 'dragstart': DragEvent; + 'drop': DragEvent; + 'focus': FocusEvent; + 'focusin': FocusEvent; + 'focusout': FocusEvent; + 'input': InputEvent; + 'keydown': KeyboardEvent; + 'keypress': KeyboardEvent; + 'keyup': KeyboardEvent; + 'mousedown': MouseEvent; + 'mouseenter': MouseEvent; + 'mouseleave': MouseEvent; + 'mousemove': MouseEvent; + 'mouseout': MouseEvent; + 'mouseover': MouseEvent; + 'mouseup': MouseEvent; + 'paste': ClipboardEvent; + 'selectionchange': Event; + 'submit': Event; + 'touchend': TouchEvent; + 'touchmove': TouchEvent; + 'touchstart': TouchEvent; + 'touchcancel': TouchEvent; + 'wheel': WheelEvent; +} +declare type EditorEvent = T & { + target: any; + type: string; + preventDefault(): void; + isDefaultPrevented(): boolean; + stopPropagation(): void; + isPropagationStopped(): boolean; + stopImmediatePropagation(): void; + isImmediatePropagationStopped(): boolean; +}; +interface EventDispatcherSettings { + scope?: any; + toggleEvent?: (name: string, state: boolean) => void | boolean; + beforeFire?: (args: EditorEvent) => void; +} +interface EventDispatcherConstructor { + readonly prototype: EventDispatcher; + new (settings?: EventDispatcherSettings): EventDispatcher; + isNative(name: string): boolean; +} +declare class EventDispatcher { + static isNative(name: string): boolean; + private readonly settings; + private readonly scope; + private readonly toggleEvent; + private bindings; + constructor(settings?: Record); + fire>(nameIn: K, argsIn?: U): EditorEvent; + on(name: K, callback: false | ((event: EditorEvent>) => void), prepend?: boolean, extra?: {}): this; + off(name?: K, callback?: (event: EditorEvent>) => void): this; + once(name: K, callback: (event: EditorEvent>) => void, prepend?: boolean): this; + has(name: string): boolean; +} +declare const enum UndoLevelType { + Fragmented = "fragmented", + Complete = "complete" +} +interface UndoLevel { + type: UndoLevelType; + fragments: string[]; + content: string; + bookmark: Bookmark; + beforeBookmark: Bookmark; +} +interface UndoManager { + data: UndoLevel[]; + typing: boolean; + add: (level?: UndoLevel, event?: EditorEvent) => UndoLevel; + beforeChange: () => void; + undo: () => UndoLevel; + redo: () => UndoLevel; + clear: () => void; + reset: () => void; + hasUndo: () => boolean; + hasRedo: () => boolean; + transact: (callback: () => void) => UndoLevel; + ignore: (callback: () => void) => void; + extra: (callback1: () => void, callback2: () => void) => void; +} +declare type ArrayCallback = (x: T, i: number, xs: ReadonlyArray) => R; +declare type ObjCallback = (value: T, key: string, obj: Record) => R; +declare type ArrayCallback$1 = ArrayCallback; +declare type ObjCallback$1 = ObjCallback; +interface Tools { + is(obj: any, type: string): boolean; + isArray(arr: any): arr is Array; + inArray(arr: ArrayLike, value: T): number; + grep(arr: ArrayLike | null | undefined, pred?: ArrayCallback$1): T[]; + grep(arr: Record | null | undefined, pred?: ObjCallback$1): T[]; + trim(str: string): string; + toArray(obj: ArrayLike): T[]; + hasOwn(obj: any, name: string): boolean; + makeMap(items: ArrayLike | string, delim?: string | RegExp, map?: Record): Record; + each(arr: ArrayLike | null | undefined, cb: ArrayCallback$1, scope?: any): boolean; + each(obj: Record | null | undefined, cb: ObjCallback$1, scope?: any): boolean; + map(arr: ArrayLike | null | undefined, cb: ArrayCallback$1): R[]; + map(obj: Record | null | undefined, cb: ObjCallback$1): R[]; + extend(obj: Object, ext: Object, ...objs: Object[]): any; + create(name: string, p: Object, root?: Object): any; + walk(obj: T, f: Function, n?: keyof T, scope?: any): void; + createNS(name: string, o?: Object): any; + resolve(path: string, o?: Object): any; + explode(s: string, d?: string | RegExp): string[]; + _addCacheSuffix(url: string): string; +} +declare type EventUtilsCallback = (event: EventUtilsEvent) => void; +declare type EventUtilsEvent = T & { + type: string; + target: any; + isDefaultPrevented: () => boolean; + preventDefault: () => void; + isPropagationStopped: () => boolean; + stopPropagation: () => void; + isImmediatePropagationStopped: () => boolean; + stopImmediatePropagation: () => void; +}; +interface EventUtilsConstructor { + readonly prototype: EventUtils; + new (): EventUtils; + Event: EventUtils; +} +declare class EventUtils { + static Event: EventUtils; + domLoaded: boolean; + events: Record; + private readonly expando; + private hasFocusIn; + private hasMouseEnterLeave; + private mouseEnterLeave; + private count; + constructor(); + bind(target: any, name: K, callback: EventUtilsCallback, scope?: any): EventUtilsCallback; + bind(target: any, names: string, callback: EventUtilsCallback, scope?: any): EventUtilsCallback; + unbind(target: any, name: K, callback?: EventUtilsCallback): this; + unbind(target: any, names: string, callback?: EventUtilsCallback): this; + unbind(target: any): this; + fire(target: any, name: string, args?: {}): this; + clean(target: any): this; + destroy(): void; + cancel(e: EventUtilsEvent): boolean; + private executeHandlers; +} +declare type DomQuerySelector = string | T | T[] | DomQuery; +declare type DomQueryInitSelector = DomQuerySelector | Window; +interface Hook { + get: (elm: T) => string; + set: ($elm: DomQuery, value: string | null) => void; +} +interface DomQueryConstructor { + readonly prototype: DomQuery; + attrHooks: Record; + cssHooks: Record; + fn: DomQuery; + find: any; + expr: { + cacheLength: number; + createPseudo: Function; + match: Record; + attrHandle: {}; + find: Record; + relative: Record; + preFilter: Record; + filter: Record; + pseudos: Record; + }; + extend: Tools['extend']; + isArray: Tools['isArray']; + new (selector?: DomQueryInitSelector, context?: Node): DomQuery; + (selector?: DomQueryInitSelector, context?: Node): DomQuery; + overrideDefaults(callback: () => { + context: Node; + element: Element; + }): DomQueryConstructor; + makeArray(object: T): T[]; + inArray(item: {}, array: T[]): number; + each(obj: T[], callback: (i: number, value: T) => void): void; + each(obj: T, callback: (key: string, obj: T[keyof T]) => void): void; + trim(str: string): string; + grep(array: T[], callback: (item: any, i: number) => boolean): T[]; + unique(results: T[]): T[]; + text(elem: Node): string; + contains(context: any, elem: Node): number; + filter(expr: string, elems: Node[], not?: boolean): any; +} +interface DomQuery extends ArrayLike { + init: (selector?: DomQueryInitSelector, context?: Node) => void; + context: T; + length: number; + selector: string; + add(items: Array | DomQuery, sort?: boolean): this; + addClass(className: string): this; + after(content: DomQuerySelector): this; + append(content: DomQuerySelector): this; + appendTo(val: DomQuerySelector): this; + attr(name: string, value: string | boolean | number | null): this; + attr(attrs: Record): this; + attr(name: string): string; + before(content: DomQuerySelector): this; + children(selector?: string): DomQuery; + clone(): this; + closest(selector: DomQuerySelector): this; + contents(selector?: string): DomQuery; + css(name: string, value: string | number | null): this; + css(styles: Record): this; + css(name: string): string; + each(callback: (i: number, value: T) => void): this; + empty(): this; + eq(index: number): this; + filter(selector: string | ((i: number, item: any) => boolean)): this; + find(selector: string): this; + first(): this; + hasClass(className: string): boolean; + hide(): this; + html(value: string): this; + html(): string; + is(selector: string | ((i: number, item: any) => boolean)): boolean; + last(): this; + next(selector?: string): DomQuery; + nextUntil(selector: DomQuerySelector, until?: string): DomQuery; + off(name: K, callback?: EventUtilsCallback): this; + off(name?: string, callback?: EventUtilsCallback): this; + offset(offset?: {}): {} | this; + on(name: K, callback: EventUtilsCallback): this; + on(name: string, callback: EventUtilsCallback): this; + parent(selector?: string): DomQuery; + parents(selector?: string): DomQuery; + parentsUntil(selector: DomQuerySelector, filter?: string): DomQuery; + prepend(content: DomQuerySelector): this; + prependTo(val: DomQuerySelector): this; + prev(selector?: string): DomQuery; + prevUntil(selector: DomQuerySelector, filter?: string): DomQuery; + prop(name: string, value: string): this; + prop(props: Record): this; + prop(name: string): string; + push(...items: T[]): number; + remove(): this; + removeAttr(name: string): this; + removeClass(className: string): this; + replaceWith(content: DomQuerySelector): this; + show(): this; + slice(start: number, end?: number): this; + splice(start: number, deleteCount?: number): T[]; + sort(compareFn?: (a: T, b: T) => number): T[]; + text(value: string): DomQuery; + text(): string; + toArray(): T[]; + toggleClass(className: string, state?: boolean): this; + trigger(name: string | { + type: string; + }): this; + unwrap(): this; + wrap(content: DomQuerySelector): this; + wrapAll(content: DomQuerySelector): this; + wrapInner(content: string): this; +} +declare type SchemaType = 'html4' | 'html5' | 'html5-strict'; +interface SchemaSettings { + block_elements?: string; + boolean_attributes?: string; + custom_elements?: string; + extended_valid_elements?: string; + invalid_elements?: string; + invalid_styles?: string | Record; + move_caret_before_on_enter_elements?: string; + non_empty_elements?: string; + schema?: SchemaType; + self_closing_elements?: string; + short_ended_elements?: string; + special?: string; + text_block_elements?: string; + text_inline_elements?: string; + valid_children?: string; + valid_classes?: string | Record; + valid_elements?: string; + valid_styles?: string | Record; + verify_html?: boolean; + whitespace_elements?: string; +} +declare type Attribute = { + required?: boolean; + defaultValue?: string; + forcedValue?: string; + validValues?: any; +}; +interface DefaultAttribute { + name: string; + value: string; +} +interface AttributePattern { + defaultValue?: string; + forcedValue?: string; + pattern: RegExp; + required?: boolean; + validValues?: Record; +} +declare type ElementRule = { + attributes: Record; + attributesDefault?: DefaultAttribute[]; + attributesForced?: DefaultAttribute[]; + attributesOrder: string[]; + attributePatterns?: AttributePattern[]; + attributesRequired?: string[]; + paddEmpty?: boolean; + removeEmpty?: boolean; + removeEmptyAttrs?: boolean; +}; +interface SchemaElement extends ElementRule { + outputName?: string; + parentsRequired?: string[]; + pattern?: RegExp; +} +declare type SchemaMap = { + [name: string]: {}; +}; +declare type SchemaRegExpMap = { + [name: string]: RegExp; +}; +interface Schema { + children: Record; + elements: Record; + getValidStyles(): SchemaMap; + getValidClasses(): SchemaMap; + getBlockElements(): SchemaMap; + getInvalidStyles(): SchemaMap; + getShortEndedElements(): SchemaMap; + getTextBlockElements(): SchemaMap; + getTextInlineElements(): SchemaMap; + getBoolAttrs(): SchemaMap; + getElementRule(name: string): SchemaElement; + getSelfClosingElements(): SchemaMap; + getNonEmptyElements(): SchemaMap; + getMoveCaretBeforeOnEnterElements(): SchemaMap; + getWhiteSpaceElements(): SchemaMap; + getSpecialElements(): SchemaRegExpMap; + isValidChild(name: string, child: string): boolean; + isValid(name: string, attr?: string): boolean; + getCustomElements(): SchemaMap; + addValidElements(validElements: string): void; + setValidElements(validElements: string): void; + addCustomElements(customElements: string): void; + addValidChildren(validChildren: any): void; +} +declare type Attributes = Array<{ + name: string; + value: string; +}> & { + map: Record; +}; +interface AstNodeConstructor { + readonly prototype: AstNode; + new (name: string, type: number): AstNode; + create(name: string, attrs?: Record): AstNode; +} +declare class AstNode { + static create(name: string, attrs?: Record): AstNode; + name: string; + type: number; + attributes?: Attributes; + value?: string; + shortEnded?: boolean; + parent?: AstNode; + firstChild?: AstNode; + lastChild?: AstNode; + next?: AstNode; + prev?: AstNode; + constructor(name: string, type: number); + replace(node: AstNode): AstNode; + attr(name: string, value: string): string | AstNode; + attr(name: Record): AstNode; + attr(name: string): string; + clone(): AstNode; + wrap(wrapper: AstNode): AstNode; + unwrap(): void; + remove(): AstNode; + append(node: AstNode): AstNode; + insert(node: AstNode, refNode: AstNode, before?: boolean): AstNode; + getAll(name: string): AstNode[]; + empty(): AstNode; + isEmpty(elements: SchemaMap, whitespace?: SchemaMap, predicate?: (node: AstNode) => boolean): boolean; + walk(prev?: boolean): AstNode; +} +declare type ContentFormat = 'raw' | 'text' | 'html' | 'tree'; +interface GetContentArgs { + format?: ContentFormat; + get?: boolean; + content?: string; + getInner?: boolean; + no_events?: boolean; + [key: string]: any; +} +interface SetContentArgs { + format?: string; + set?: boolean; + content?: string; + no_events?: boolean; +} +interface BlobCache { + create: (o: string | BlobInfoData, blob?: Blob, base64?: string, filename?: string) => BlobInfo; + add: (blobInfo: BlobInfo) => void; + get: (id: string) => BlobInfo | undefined; + getByUri: (blobUri: string) => BlobInfo | undefined; + getByData: (base64: string, type: string) => BlobInfo | undefined; + findFirst: (predicate: (blobInfo: BlobInfo) => boolean) => BlobInfo | undefined; + removeByUri: (blobUri: string) => void; + destroy: () => void; +} +interface BlobInfoData { + id?: string; + name?: string; + blob: Blob; + base64: string; + blobUri?: string; + uri?: string; +} +interface BlobInfo { + id: () => string; + name: () => string; + filename: () => string; + blob: () => Blob; + base64: () => string; + blobUri: () => string; + uri: () => string; +} +interface UploadFailureOptions { + remove?: boolean; +} +declare type UploadHandler = (blobInfo: BlobInfo, success: (url: string) => void, failure: (err: string, options?: UploadFailureOptions) => void, progress?: (percent: number) => void) => void; +interface RangeLikeObject { + startContainer: Node; + startOffset: number; + endContainer: Node; + endOffset: number; +} +declare type ApplyFormat = BlockFormat | InlineFormat | SelectorFormat; +declare type RemoveFormat = RemoveBlockFormat | RemoveInlineFormat | RemoveSelectorFormat; +declare type Format = ApplyFormat | RemoveFormat; +declare type Formats = Record; +declare type FormatAttrOrStyleValue = string | ((vars?: FormatVars) => string); +declare type FormatVars = Record; +interface CommonFormat { + ceFalseOverride?: boolean; + classes?: string | string[]; + collapsed?: boolean; + exact?: boolean; + expand?: boolean; + links?: boolean; + onmatch?: (node: Node, fmt: T, itemName: string) => boolean; + onformat?: (elm: Node, fmt: T, vars?: FormatVars, node?: Node | RangeLikeObject) => void; + remove_similar?: boolean; +} +interface CommonApplyFormat extends CommonFormat { + attributes?: Record; + preview?: string | boolean; + styles?: Record; + toggle?: boolean; + wrapper?: boolean; + merge_siblings?: boolean; + merge_with_parents?: boolean; +} +interface BlockFormat extends CommonApplyFormat { + block: string; + block_expand?: boolean; +} +interface InlineFormat extends CommonApplyFormat { + inline: string; + clear_child_styles?: boolean; +} +interface SelectorFormat extends CommonApplyFormat { + selector: string; + defaultBlock?: string; + inherit?: boolean; +} +interface CommonRemoveFormat extends CommonFormat { + remove?: 'none' | 'empty' | 'all'; + attributes?: string[] | Record; + styles?: string[] | Record; + split?: boolean; + deep?: boolean; + mixed?: boolean; +} +interface RemoveBlockFormat extends CommonRemoveFormat { + block: string; + list_block?: string; +} +interface RemoveInlineFormat extends CommonRemoveFormat { + inline: string; + preserve_attributes?: string[]; +} +interface RemoveSelectorFormat extends CommonRemoveFormat { + selector: string; +} +declare type StyleFormat = BlockStyleFormat | InlineStyleFormat | SelectorStyleFormat; +declare type AllowedFormat = Separator | FormatReference | StyleFormat | NestedFormatting; +interface Separator { + title: string; +} +interface FormatReference { + title: string; + format: string; + icon?: string; +} +interface NestedFormatting { + title: string; + items: Array; +} +interface CommonStyleFormat { + title: string; + icon?: string; +} +interface BlockStyleFormat extends BlockFormat, CommonStyleFormat { +} +interface InlineStyleFormat extends InlineFormat, CommonStyleFormat { +} +interface SelectorStyleFormat extends SelectorFormat, CommonStyleFormat { +} +interface AlertBannerSpec { + type: 'alertbanner'; + level: 'info' | 'warn' | 'error' | 'success'; + text: string; + icon: string; + url?: string; +} +interface ButtonSpec { + type: 'button'; + text: string; + disabled?: boolean; + primary?: boolean; + name?: string; + icon?: string; + borderless?: boolean; +} +interface CheckboxSpec { + name: string; + type: 'checkbox'; + label: string; + disabled?: boolean; +} +interface FormComponentSpec { + type: string; + name: string; +} +interface FormComponentWithLabelSpec extends FormComponentSpec { + label?: string; +} +interface CollectionSpec extends FormComponentWithLabelSpec { + type: 'collection'; +} +interface ColorInputSpec extends FormComponentWithLabelSpec { + type: 'colorinput'; +} +interface ColorPickerSpec extends FormComponentWithLabelSpec { + type: 'colorpicker'; +} +interface DropZoneSpec extends FormComponentWithLabelSpec { + type: 'dropzone'; +} +interface GridSpec { + type: 'grid'; + columns: number; + items: BodyComponentSpec[]; +} +interface IframeSpec extends FormComponentWithLabelSpec { + type: 'iframe'; + sandboxed?: boolean; +} +interface ImageToolsState { + blob: Blob; + url: string; +} +interface ImageToolsSpec extends FormComponentWithLabelSpec { + type: 'imagetools'; + currentState: ImageToolsState; +} +interface InputSpec extends FormComponentWithLabelSpec { + type: 'input'; + inputMode?: string; + placeholder?: string; + maximized?: boolean; + disabled?: boolean; +} +interface LabelSpec { + type: 'label'; + label: string; + items: BodyComponentSpec[]; +} +interface ListBoxSingleItemSpec { + text: string; + value: string; +} +interface ListBoxNestedItemSpec { + text: string; + items: ListBoxItemSpec[]; +} +declare type ListBoxItemSpec = ListBoxNestedItemSpec | ListBoxSingleItemSpec; +interface ListBoxSpec extends FormComponentWithLabelSpec { + type: 'listbox'; + items: ListBoxItemSpec[]; + disabled?: boolean; +} +interface SelectBoxItemSpec { + text: string; + value: string; +} +interface SelectBoxSpec extends FormComponentWithLabelSpec { + type: 'selectbox'; + items: SelectBoxItemSpec[]; + size?: number; + disabled?: boolean; +} +interface SizeInputSpec extends FormComponentWithLabelSpec { + type: 'sizeinput'; + constrain?: boolean; + disabled?: boolean; +} +interface TableSpec { + type: 'table'; + header: string[]; + cells: string[][]; +} +interface TextAreaSpec extends FormComponentWithLabelSpec { + type: 'textarea'; + placeholder?: string; + maximized?: boolean; + disabled?: boolean; +} +interface UrlInputSpec extends FormComponentWithLabelSpec { + type: 'urlinput'; + filetype?: 'image' | 'media' | 'file'; + disabled?: boolean; +} +interface HtmlPanelSpec { + type: 'htmlpanel'; + html: string; + presets?: 'presentation' | 'document'; +} +interface PanelSpec { + type: 'panel'; + classes?: string[]; + items: BodyComponentSpec[]; +} +declare type BodyComponentSpec = BarSpec | ButtonSpec | CheckboxSpec | TextAreaSpec | InputSpec | ListBoxSpec | SelectBoxSpec | SizeInputSpec | IframeSpec | HtmlPanelSpec | UrlInputSpec | DropZoneSpec | ColorInputSpec | GridSpec | ColorPickerSpec | ImageToolsSpec | AlertBannerSpec | CollectionSpec | LabelSpec | TableSpec | PanelSpec; +interface BarSpec { + type: 'bar'; + items: BodyComponentSpec[]; +} +interface CustomEditorInit { + setValue: (value: string) => void; + getValue: () => string; + destroy: () => void; +} +declare type CustomEditorInitFn = (elm: Element, settings: any) => Promise; +interface CustomEditorOldSpec extends FormComponentSpec { + type: 'customeditor'; + tag?: string; + init: (e: Element) => Promise; +} +interface CustomEditorNewSpec extends FormComponentSpec { + type: 'customeditor'; + tag?: string; + scriptId: string; + scriptUrl: string; + settings?: any; +} +declare type CustomEditorSpec = CustomEditorOldSpec | CustomEditorNewSpec; +interface CommonMenuItemSpec { + disabled?: boolean; + text?: string; + value?: string; + meta?: Record; + shortcut?: string; +} +interface CommonMenuItemInstanceApi { + isDisabled: () => boolean; + setDisabled: (state: boolean) => void; +} +interface DialogToggleMenuItemSpec extends CommonMenuItemSpec { + type?: 'togglemenuitem'; + name: string; +} +declare type DialogFooterMenuButtonItemSpec = DialogToggleMenuItemSpec; +interface BaseDialogFooterButtonSpec { + name?: string; + align?: 'start' | 'end'; + primary?: boolean; + disabled?: boolean; + icon?: string; +} +interface DialogFooterNormalButtonSpec extends BaseDialogFooterButtonSpec { + type: 'submit' | 'cancel' | 'custom'; + text: string; +} +interface DialogFooterMenuButtonSpec extends BaseDialogFooterButtonSpec { + type: 'menu'; + text?: string; + tooltip?: string; + icon?: string; + items: DialogFooterMenuButtonItemSpec[]; +} +declare type DialogFooterButtonSpec = DialogFooterNormalButtonSpec | DialogFooterMenuButtonSpec; +interface TabSpec { + name?: string; + title: string; + items: BodyComponentSpec[]; +} +interface TabPanelSpec { + type: 'tabpanel'; + tabs: TabSpec[]; +} +declare type DialogDataItem = any; +declare type DialogData = Record; +interface DialogInstanceApi { + getData: () => T; + setData: (data: Partial) => void; + disable: (name: string) => void; + focus: (name: string) => void; + showTab: (name: string) => void; + redial: (nu: DialogSpec) => void; + enable: (name: string) => void; + block: (msg: string) => void; + unblock: () => void; + close: () => void; +} +interface DialogActionDetails { + name: string; + value?: any; +} +interface DialogChangeDetails { + name: keyof T; +} +interface DialogTabChangeDetails { + newTabName: string; + oldTabName: string; +} +declare type DialogActionHandler = (api: DialogInstanceApi, details: DialogActionDetails) => void; +declare type DialogChangeHandler = (api: DialogInstanceApi, details: DialogChangeDetails) => void; +declare type DialogSubmitHandler = (api: DialogInstanceApi) => void; +declare type DialogCloseHandler = () => void; +declare type DialogCancelHandler = (api: DialogInstanceApi) => void; +declare type DialogTabChangeHandler = (api: DialogInstanceApi, details: DialogTabChangeDetails) => void; +declare type DialogSize = 'normal' | 'medium' | 'large'; +interface DialogSpec { + title: string; + size?: DialogSize; + body: TabPanelSpec | PanelSpec; + buttons: DialogFooterButtonSpec[]; + initialData?: T; + onAction?: DialogActionHandler; + onChange?: DialogChangeHandler; + onSubmit?: DialogSubmitHandler; + onClose?: DialogCloseHandler; + onCancel?: DialogCancelHandler; + onTabChange?: DialogTabChangeHandler; +} +interface UrlDialogInstanceApi { + block: (msg: string) => void; + unblock: () => void; + close: () => void; + sendMessage: (msg: any) => void; +} +interface UrlDialogActionDetails { + name: string; + value?: any; +} +interface UrlDialogMessage { + mceAction: string; + [key: string]: any; +} +declare type UrlDialogActionHandler = (api: UrlDialogInstanceApi, actions: UrlDialogActionDetails) => void; +declare type UrlDialogCloseHandler = () => void; +declare type UrlDialogCancelHandler = (api: UrlDialogInstanceApi) => void; +declare type UrlDialogMessageHandler = (api: UrlDialogInstanceApi, message: UrlDialogMessage) => void; +interface UrlDialogFooterButtonSpec extends DialogFooterNormalButtonSpec { + type: 'cancel' | 'custom'; +} +interface UrlDialogSpec { + title: string; + url: string; + height?: number; + width?: number; + buttons?: UrlDialogFooterButtonSpec[]; + onAction?: UrlDialogActionHandler; + onClose?: UrlDialogCloseHandler; + onCancel?: UrlDialogCancelHandler; + onMessage?: UrlDialogMessageHandler; +} +interface SeparatorMenuItemSpec { + type?: 'separator'; + text?: string; +} +declare type ColumnTypes = number | 'auto'; +declare type SeparatorItemSpec = SeparatorMenuItemSpec; +interface AutocompleterItemSpec { + type?: 'autocompleteitem'; + value: string; + text?: string; + icon?: string; + meta?: Record; +} +declare type AutocompleterContents = SeparatorItemSpec | AutocompleterItemSpec; +interface AutocompleterSpec { + type?: 'autocompleter'; + ch: string; + minChars?: number; + columns?: ColumnTypes; + matches?: (rng: Range, text: string, pattern: string) => boolean; + fetch: (pattern: string, maxResults: number, fetchOptions: Record) => Promise; + onAction: (autocompleterApi: AutocompleterInstanceApi, rng: any, value: string, meta: Record) => void; + maxResults?: number; +} +interface AutocompleterInstanceApi { + hide: () => void; + reload: (fetchOptions: Record) => void; +} +declare type ContextPosition = 'node' | 'selection' | 'line'; +declare type ContextScope = 'node' | 'editor'; +interface ContextBarSpec { + predicate?: (elem: Element) => boolean; + position?: ContextPosition; + scope?: ContextScope; +} +interface BaseToolbarButtonSpec { + disabled?: boolean; + tooltip?: string; + icon?: string; + text?: string; + onSetup?: (api: I) => (api: I) => void; +} +interface BaseToolbarButtonInstanceApi { + isDisabled: () => boolean; + setDisabled: (state: boolean) => void; +} +interface ToolbarButtonSpec extends BaseToolbarButtonSpec { + type?: 'button'; + onAction: (api: ToolbarButtonInstanceApi) => void; +} +interface ToolbarButtonInstanceApi extends BaseToolbarButtonInstanceApi { +} +interface BaseToolbarToggleButtonSpec extends BaseToolbarButtonSpec { + active?: boolean; +} +interface BaseToolbarToggleButtonInstanceApi extends BaseToolbarButtonInstanceApi { + isActive: () => boolean; + setActive: (state: boolean) => void; +} +interface ToolbarToggleButtonSpec extends BaseToolbarToggleButtonSpec { + type?: 'togglebutton'; + onAction: (api: ToolbarToggleButtonInstanceApi) => void; +} +interface ToolbarToggleButtonInstanceApi extends BaseToolbarToggleButtonInstanceApi { +} +interface ContextFormLaunchButtonApi extends BaseToolbarButtonSpec { + type: 'contextformbutton'; +} +interface ContextFormLaunchToggleButtonSpec extends BaseToolbarToggleButtonSpec { + type: 'contextformtogglebutton'; +} +interface ContextFormButtonInstanceApi extends BaseToolbarButtonInstanceApi { +} +interface ContextFormToggleButtonInstanceApi extends BaseToolbarToggleButtonInstanceApi { +} +interface ContextFormButtonSpec extends BaseToolbarButtonSpec { + type?: 'contextformbutton'; + primary?: boolean; + onAction: (formApi: ContextFormInstanceApi, api: ContextFormButtonInstanceApi) => void; +} +interface ContextFormToggleButtonSpec extends BaseToolbarToggleButtonSpec { + type?: 'contextformtogglebutton'; + onAction: (formApi: ContextFormInstanceApi, buttonApi: ContextFormToggleButtonInstanceApi) => void; + primary?: boolean; +} +interface ContextFormInstanceApi { + hide: () => void; + getValue: () => string; +} +interface ContextFormSpec extends ContextBarSpec { + type?: 'contextform'; + initValue?: () => string; + label?: string; + launch?: ContextFormLaunchButtonApi | ContextFormLaunchToggleButtonSpec; + commands: Array; +} +interface ContextToolbarSpec extends ContextBarSpec { + type?: 'contexttoolbar'; + items: string; +} +interface ChoiceMenuItemSpec extends CommonMenuItemSpec { + type?: 'choiceitem'; + icon?: string; +} +interface ChoiceMenuItemInstanceApi extends CommonMenuItemInstanceApi { + isActive: () => boolean; + setActive: (state: boolean) => void; +} +interface ContextMenuItem { + text: string; + icon?: string; + type?: 'item'; + onAction: () => void; +} +interface ContextSubMenu { + type: 'submenu'; + text: string; + icon?: string; + getSubmenuItems: () => string | Array; +} +declare type ContextMenuContents = string | ContextMenuItem | SeparatorMenuItemSpec | ContextSubMenu; +interface ContextMenuApi { + update: (element: Element) => string | Array; +} +interface FancyMenuItemSpec { + type: 'fancymenuitem'; + fancytype: string; + onAction: (data: any) => void; +} +interface MenuItemSpec extends CommonMenuItemSpec { + type?: 'menuitem'; + icon?: string; + onSetup?: (api: MenuItemInstanceApi) => (api: MenuItemInstanceApi) => void; + onAction?: (api: MenuItemInstanceApi) => void; +} +interface MenuItemInstanceApi extends CommonMenuItemInstanceApi { +} +declare type NestedMenuItemContents = string | MenuItemSpec | NestedMenuItemSpec | ToggleMenuItemSpec | SeparatorMenuItemSpec | FancyMenuItemSpec; +interface NestedMenuItemSpec extends CommonMenuItemSpec { + type?: 'nestedmenuitem'; + icon?: string; + getSubmenuItems: () => string | Array; + onSetup?: (api: NestedMenuItemInstanceApi) => (api: NestedMenuItemInstanceApi) => void; +} +interface NestedMenuItemInstanceApi extends CommonMenuItemInstanceApi { +} +interface ToggleMenuItemSpec extends CommonMenuItemSpec { + type?: 'togglemenuitem'; + icon?: string; + active?: boolean; + onSetup?: (api: ToggleMenuItemInstanceApi) => void; + onAction: (api: ToggleMenuItemInstanceApi) => void; +} +interface ToggleMenuItemInstanceApi extends CommonMenuItemInstanceApi { + isActive: () => boolean; + setActive: (state: boolean) => void; +} +type PublicDialog_d_AlertBannerSpec = AlertBannerSpec; +type PublicDialog_d_BarSpec = BarSpec; +type PublicDialog_d_BodyComponentSpec = BodyComponentSpec; +type PublicDialog_d_ButtonSpec = ButtonSpec; +type PublicDialog_d_CheckboxSpec = CheckboxSpec; +type PublicDialog_d_CollectionSpec = CollectionSpec; +type PublicDialog_d_ColorInputSpec = ColorInputSpec; +type PublicDialog_d_ColorPickerSpec = ColorPickerSpec; +type PublicDialog_d_CustomEditorSpec = CustomEditorSpec; +type PublicDialog_d_CustomEditorInit = CustomEditorInit; +type PublicDialog_d_CustomEditorInitFn = CustomEditorInitFn; +type PublicDialog_d_DialogData = DialogData; +type PublicDialog_d_DialogSize = DialogSize; +type PublicDialog_d_DialogSpec<_0> = DialogSpec<_0>; +type PublicDialog_d_DialogInstanceApi<_0> = DialogInstanceApi<_0>; +type PublicDialog_d_DialogFooterButtonSpec = DialogFooterButtonSpec; +type PublicDialog_d_DialogActionDetails = DialogActionDetails; +type PublicDialog_d_DialogChangeDetails<_0> = DialogChangeDetails<_0>; +type PublicDialog_d_DialogTabChangeDetails = DialogTabChangeDetails; +type PublicDialog_d_DropZoneSpec = DropZoneSpec; +type PublicDialog_d_GridSpec = GridSpec; +type PublicDialog_d_HtmlPanelSpec = HtmlPanelSpec; +type PublicDialog_d_IframeSpec = IframeSpec; +type PublicDialog_d_ImageToolsSpec = ImageToolsSpec; +type PublicDialog_d_InputSpec = InputSpec; +type PublicDialog_d_LabelSpec = LabelSpec; +type PublicDialog_d_ListBoxSpec = ListBoxSpec; +type PublicDialog_d_ListBoxItemSpec = ListBoxItemSpec; +type PublicDialog_d_ListBoxNestedItemSpec = ListBoxNestedItemSpec; +type PublicDialog_d_ListBoxSingleItemSpec = ListBoxSingleItemSpec; +type PublicDialog_d_PanelSpec = PanelSpec; +type PublicDialog_d_SelectBoxSpec = SelectBoxSpec; +type PublicDialog_d_SelectBoxItemSpec = SelectBoxItemSpec; +type PublicDialog_d_SizeInputSpec = SizeInputSpec; +type PublicDialog_d_TableSpec = TableSpec; +type PublicDialog_d_TabSpec = TabSpec; +type PublicDialog_d_TabPanelSpec = TabPanelSpec; +type PublicDialog_d_TextAreaSpec = TextAreaSpec; +type PublicDialog_d_UrlInputSpec = UrlInputSpec; +type PublicDialog_d_UrlDialogSpec = UrlDialogSpec; +type PublicDialog_d_UrlDialogFooterButtonSpec = UrlDialogFooterButtonSpec; +type PublicDialog_d_UrlDialogInstanceApi = UrlDialogInstanceApi; +type PublicDialog_d_UrlDialogActionDetails = UrlDialogActionDetails; +type PublicDialog_d_UrlDialogMessage = UrlDialogMessage; +declare namespace PublicDialog_d { + //export { PublicDialog_d_AlertBannerSpec as AlertBannerSpec, PublicDialog_d_BarSpec as BarSpec, PublicDialog_d_BodyComponentSpec as BodyComponentSpec, PublicDialog_d_ButtonSpec as ButtonSpec, PublicDialog_d_CheckboxSpec as CheckboxSpec, PublicDialog_d_CollectionSpec as CollectionSpec, PublicDialog_d_ColorInputSpec as ColorInputSpec, PublicDialog_d_ColorPickerSpec as ColorPickerSpec, PublicDialog_d_CustomEditorSpec as CustomEditorSpec, PublicDialog_d_CustomEditorInit as CustomEditorInit, PublicDialog_d_CustomEditorInitFn as CustomEditorInitFn, PublicDialog_d_DialogData as DialogData, PublicDialog_d_DialogSize as DialogSize, PublicDialog_d_DialogSpec as DialogSpec, PublicDialog_d_DialogInstanceApi as DialogInstanceApi, PublicDialog_d_DialogFooterButtonSpec as DialogFooterButtonSpec, PublicDialog_d_DialogActionDetails as DialogActionDetails, PublicDialog_d_DialogChangeDetails as DialogChangeDetails, PublicDialog_d_DialogTabChangeDetails as DialogTabChangeDetails, PublicDialog_d_DropZoneSpec as DropZoneSpec, PublicDialog_d_GridSpec as GridSpec, PublicDialog_d_HtmlPanelSpec as HtmlPanelSpec, PublicDialog_d_IframeSpec as IframeSpec, PublicDialog_d_ImageToolsSpec as ImageToolsSpec, PublicDialog_d_InputSpec as InputSpec, PublicDialog_d_LabelSpec as LabelSpec, PublicDialog_d_ListBoxSpec as ListBoxSpec, PublicDialog_d_ListBoxItemSpec as ListBoxItemSpec, PublicDialog_d_ListBoxNestedItemSpec as ListBoxNestedItemSpec, PublicDialog_d_ListBoxSingleItemSpec as ListBoxSingleItemSpec, PublicDialog_d_PanelSpec as PanelSpec, PublicDialog_d_SelectBoxSpec as SelectBoxSpec, PublicDialog_d_SelectBoxItemSpec as SelectBoxItemSpec, PublicDialog_d_SizeInputSpec as SizeInputSpec, PublicDialog_d_TableSpec as TableSpec, PublicDialog_d_TabSpec as TabSpec, PublicDialog_d_TabPanelSpec as TabPanelSpec, PublicDialog_d_TextAreaSpec as TextAreaSpec, PublicDialog_d_UrlInputSpec as UrlInputSpec, PublicDialog_d_UrlDialogSpec as UrlDialogSpec, PublicDialog_d_UrlDialogFooterButtonSpec as UrlDialogFooterButtonSpec, PublicDialog_d_UrlDialogInstanceApi as UrlDialogInstanceApi, PublicDialog_d_UrlDialogActionDetails as UrlDialogActionDetails, PublicDialog_d_UrlDialogMessage as UrlDialogMessage, }; +} +type PublicInlineContent_d_AutocompleterSpec = AutocompleterSpec; +type PublicInlineContent_d_AutocompleterItemSpec = AutocompleterItemSpec; +type PublicInlineContent_d_AutocompleterContents = AutocompleterContents; +type PublicInlineContent_d_AutocompleterInstanceApi = AutocompleterInstanceApi; +type PublicInlineContent_d_ContextPosition = ContextPosition; +type PublicInlineContent_d_ContextScope = ContextScope; +type PublicInlineContent_d_ContextFormSpec = ContextFormSpec; +type PublicInlineContent_d_ContextFormInstanceApi = ContextFormInstanceApi; +type PublicInlineContent_d_ContextFormButtonSpec = ContextFormButtonSpec; +type PublicInlineContent_d_ContextFormButtonInstanceApi = ContextFormButtonInstanceApi; +type PublicInlineContent_d_ContextFormToggleButtonSpec = ContextFormToggleButtonSpec; +type PublicInlineContent_d_ContextFormToggleButtonInstanceApi = ContextFormToggleButtonInstanceApi; +type PublicInlineContent_d_ContextToolbarSpec = ContextToolbarSpec; +type PublicInlineContent_d_SeparatorItemSpec = SeparatorItemSpec; +declare namespace PublicInlineContent_d { + //export { PublicInlineContent_d_AutocompleterSpec as AutocompleterSpec, PublicInlineContent_d_AutocompleterItemSpec as AutocompleterItemSpec, PublicInlineContent_d_AutocompleterContents as AutocompleterContents, PublicInlineContent_d_AutocompleterInstanceApi as AutocompleterInstanceApi, PublicInlineContent_d_ContextPosition as ContextPosition, PublicInlineContent_d_ContextScope as ContextScope, PublicInlineContent_d_ContextFormSpec as ContextFormSpec, PublicInlineContent_d_ContextFormInstanceApi as ContextFormInstanceApi, PublicInlineContent_d_ContextFormButtonSpec as ContextFormButtonSpec, PublicInlineContent_d_ContextFormButtonInstanceApi as ContextFormButtonInstanceApi, PublicInlineContent_d_ContextFormToggleButtonSpec as ContextFormToggleButtonSpec, PublicInlineContent_d_ContextFormToggleButtonInstanceApi as ContextFormToggleButtonInstanceApi, PublicInlineContent_d_ContextToolbarSpec as ContextToolbarSpec, PublicInlineContent_d_SeparatorItemSpec as SeparatorItemSpec, }; +} +type PublicMenu_d_MenuItemSpec = MenuItemSpec; +type PublicMenu_d_MenuItemInstanceApi = MenuItemInstanceApi; +type PublicMenu_d_NestedMenuItemContents = NestedMenuItemContents; +type PublicMenu_d_NestedMenuItemSpec = NestedMenuItemSpec; +type PublicMenu_d_NestedMenuItemInstanceApi = NestedMenuItemInstanceApi; +type PublicMenu_d_FancyMenuItemSpec = FancyMenuItemSpec; +type PublicMenu_d_ToggleMenuItemSpec = ToggleMenuItemSpec; +type PublicMenu_d_ToggleMenuItemInstanceApi = ToggleMenuItemInstanceApi; +type PublicMenu_d_ChoiceMenuItemSpec = ChoiceMenuItemSpec; +type PublicMenu_d_ChoiceMenuItemInstanceApi = ChoiceMenuItemInstanceApi; +type PublicMenu_d_SeparatorMenuItemSpec = SeparatorMenuItemSpec; +type PublicMenu_d_ContextMenuApi = ContextMenuApi; +type PublicMenu_d_ContextMenuContents = ContextMenuContents; +type PublicMenu_d_ContextMenuItem = ContextMenuItem; +type PublicMenu_d_ContextSubMenu = ContextSubMenu; +declare namespace PublicMenu_d { + //export { PublicMenu_d_MenuItemSpec as MenuItemSpec, PublicMenu_d_MenuItemInstanceApi as MenuItemInstanceApi, PublicMenu_d_NestedMenuItemContents as NestedMenuItemContents, PublicMenu_d_NestedMenuItemSpec as NestedMenuItemSpec, PublicMenu_d_NestedMenuItemInstanceApi as NestedMenuItemInstanceApi, PublicMenu_d_FancyMenuItemSpec as FancyMenuItemSpec, PublicMenu_d_ToggleMenuItemSpec as ToggleMenuItemSpec, PublicMenu_d_ToggleMenuItemInstanceApi as ToggleMenuItemInstanceApi, PublicMenu_d_ChoiceMenuItemSpec as ChoiceMenuItemSpec, PublicMenu_d_ChoiceMenuItemInstanceApi as ChoiceMenuItemInstanceApi, PublicMenu_d_SeparatorMenuItemSpec as SeparatorMenuItemSpec, PublicMenu_d_ContextMenuApi as ContextMenuApi, PublicMenu_d_ContextMenuContents as ContextMenuContents, PublicMenu_d_ContextMenuItem as ContextMenuItem, PublicMenu_d_ContextSubMenu as ContextSubMenu, }; +} +interface SidebarInstanceApi { + element: () => HTMLElement; +} +interface SidebarSpec { + icon?: string; + tooltip?: string; + onShow?: (api: SidebarInstanceApi) => void; + onSetup?: (api: SidebarInstanceApi) => (api: SidebarInstanceApi) => void; + onHide?: (api: SidebarInstanceApi) => void; +} +type PublicSidebar_d_SidebarSpec = SidebarSpec; +type PublicSidebar_d_SidebarInstanceApi = SidebarInstanceApi; +declare namespace PublicSidebar_d { + //export { PublicSidebar_d_SidebarSpec as SidebarSpec, PublicSidebar_d_SidebarInstanceApi as SidebarInstanceApi, }; +} +interface ToolbarGroupSetting { + name: string; + items: string[]; +} +declare type ToolbarConfig = string | ToolbarGroupSetting[]; +interface GroupToolbarButtonInstanceApi extends BaseToolbarButtonInstanceApi { +} +interface GroupToolbarButtonSpec extends BaseToolbarButtonSpec { + type?: 'grouptoolbarbutton'; + items?: ToolbarConfig; +} +declare type MenuButtonItemTypes = NestedMenuItemContents; +declare type SuccessCallback = (menu: string | MenuButtonItemTypes[]) => void; +interface BaseMenuButtonSpec { + text?: string; + tooltip?: string; + icon?: string; + fetch: (success: SuccessCallback) => void; + onSetup?: (api: BaseMenuButtonInstanceApi) => (api: BaseMenuButtonInstanceApi) => void; +} +interface BaseMenuButtonInstanceApi { + isDisabled: () => boolean; + setDisabled: (state: boolean) => void; + isActive: () => boolean; + setActive: (state: boolean) => void; +} +interface ToolbarMenuButtonSpec extends BaseMenuButtonSpec { + type?: 'menubutton'; + onSetup?: (api: ToolbarMenuButtonInstanceApi) => (api: ToolbarMenuButtonInstanceApi) => void; +} +interface ToolbarMenuButtonInstanceApi extends BaseMenuButtonInstanceApi { +} +declare type ToolbarSplitButtonItemTypes = ChoiceMenuItemSpec | SeparatorMenuItemSpec; +declare type SuccessCallback$1 = (menu: ToolbarSplitButtonItemTypes[]) => void; +declare type SelectPredicate = (value: string) => boolean; +declare type PresetTypes = 'color' | 'normal' | 'listpreview'; +declare type ColumnTypes$1 = number | 'auto'; +interface ToolbarSplitButtonSpec { + type?: 'splitbutton'; + tooltip?: string; + icon?: string; + text?: string; + select?: SelectPredicate; + presets?: PresetTypes; + columns?: ColumnTypes$1; + fetch: (success: SuccessCallback$1) => void; + onSetup?: (api: ToolbarSplitButtonInstanceApi) => (api: ToolbarSplitButtonInstanceApi) => void; + onAction: (api: ToolbarSplitButtonInstanceApi) => void; + onItemAction: (api: ToolbarSplitButtonInstanceApi, value: string) => void; +} +interface ToolbarSplitButtonInstanceApi { + isDisabled: () => boolean; + setDisabled: (state: boolean) => void; + setIconFill: (id: string, value: string) => void; + setIconStroke: (id: string, value: string) => void; + isActive: () => boolean; + setActive: (state: boolean) => void; +} +type PublicToolbar_d_ToolbarButtonSpec = ToolbarButtonSpec; +type PublicToolbar_d_ToolbarButtonInstanceApi = ToolbarButtonInstanceApi; +type PublicToolbar_d_ToolbarSplitButtonSpec = ToolbarSplitButtonSpec; +type PublicToolbar_d_ToolbarSplitButtonInstanceApi = ToolbarSplitButtonInstanceApi; +type PublicToolbar_d_ToolbarMenuButtonSpec = ToolbarMenuButtonSpec; +type PublicToolbar_d_ToolbarMenuButtonInstanceApi = ToolbarMenuButtonInstanceApi; +type PublicToolbar_d_ToolbarToggleButtonSpec = ToolbarToggleButtonSpec; +type PublicToolbar_d_ToolbarToggleButtonInstanceApi = ToolbarToggleButtonInstanceApi; +type PublicToolbar_d_GroupToolbarButtonSpec = GroupToolbarButtonSpec; +type PublicToolbar_d_GroupToolbarButtonInstanceApi = GroupToolbarButtonInstanceApi; +declare namespace PublicToolbar_d { + //export { PublicToolbar_d_ToolbarButtonSpec as ToolbarButtonSpec, PublicToolbar_d_ToolbarButtonInstanceApi as ToolbarButtonInstanceApi, PublicToolbar_d_ToolbarSplitButtonSpec as ToolbarSplitButtonSpec, PublicToolbar_d_ToolbarSplitButtonInstanceApi as ToolbarSplitButtonInstanceApi, PublicToolbar_d_ToolbarMenuButtonSpec as ToolbarMenuButtonSpec, PublicToolbar_d_ToolbarMenuButtonInstanceApi as ToolbarMenuButtonInstanceApi, PublicToolbar_d_ToolbarToggleButtonSpec as ToolbarToggleButtonSpec, PublicToolbar_d_ToolbarToggleButtonInstanceApi as ToolbarToggleButtonInstanceApi, PublicToolbar_d_GroupToolbarButtonSpec as GroupToolbarButtonSpec, PublicToolbar_d_GroupToolbarButtonInstanceApi as GroupToolbarButtonInstanceApi, }; +} +interface Registry { + addButton: (name: string, spec: ToolbarButtonSpec) => void; + addGroupToolbarButton: (name: string, spec: GroupToolbarButtonSpec) => void; + addToggleButton: (name: string, spec: ToolbarToggleButtonSpec) => void; + addMenuButton: (name: string, spec: ToolbarMenuButtonSpec) => void; + addSplitButton: (name: string, spec: ToolbarSplitButtonSpec) => void; + addMenuItem: (name: string, spec: MenuItemSpec) => void; + addNestedMenuItem: (name: string, spec: NestedMenuItemSpec) => void; + addToggleMenuItem: (name: string, spec: ToggleMenuItemSpec) => void; + addContextMenu: (name: string, spec: ContextMenuApi) => void; + addContextToolbar: (name: string, spec: ContextToolbarSpec) => void; + addContextForm: (name: string, spec: ContextFormSpec) => void; + addIcon: (name: string, svgData: string) => void; + addAutocompleter: (name: string, spec: AutocompleterSpec) => void; + addSidebar: (name: string, spec: SidebarSpec) => void; + getAll: () => { + buttons: Record; + menuItems: Record; + popups: Record; + contextMenus: Record; + contextToolbars: Record; + icons: Record; + sidebars: Record; + }; +} +interface StyleSheetLoader { + load: (url: string, success: () => void, failure?: () => void) => void; + loadAll: (urls: string[], success: (urls: string[]) => void, failure: (urls: string[]) => void) => void; + unload: (url: string) => void; + unloadAll: (urls: string[]) => void; + _setReferrerPolicy: (referrerPolicy: ReferrerPolicy) => void; +} +interface StyleSheetLoaderSettings { + maxLoadTime?: number; + contentCssCors?: boolean; + referrerPolicy?: ReferrerPolicy; +} +declare type Registry$1 = Registry; +interface EditorUiApi { + show: () => void; + hide: () => void; +} +interface EditorUi extends EditorUiApi { + registry: Registry$1; + styleSheetLoader: StyleSheetLoader; +} +type Ui_d_EditorUiApi = EditorUiApi; +type Ui_d_EditorUi = EditorUi; +declare namespace Ui_d { + //export { Ui_d_EditorUiApi as EditorUiApi, Ui_d_EditorUi as EditorUi, Registry$1 as Registry, PublicDialog_d as Dialog, PublicInlineContent_d as InlineContent, PublicMenu_d as Menu, PublicSidebar_d as Sidebar, PublicToolbar_d as Toolbar, }; +} +declare type EntityEncoding = 'named' | 'numeric' | 'raw'; +declare type ThemeInitFunc = (editor: Editor, elm: HTMLElement) => { + editorContainer: HTMLElement; + iframeContainer: HTMLElement; + height?: number; + iframeHeight?: number; + api?: EditorUiApi; +}; +declare type SetupCallback = (editor: Editor) => void; +declare type FilePickerCallback = (callback: Function, value: any, meta: Record) => void; +declare type FilePickerValidationStatus = 'valid' | 'unknown' | 'invalid' | 'none'; +declare type FilePickerValidationCallback = (info: { + type: string; + url: string; +}, callback: (validation: { + status: FilePickerValidationStatus; + message: string; +}) => void) => void; +declare type URLConverter = (url: string, name: string, elm?: HTMLElement) => string; +declare type URLConverterCallback = (url: string, node: Node, on_save: boolean, name: string) => void; +interface ToolbarGroup { + name?: string; + items: string[]; +} +declare type ToolbarMode = 'floating' | 'sliding' | 'scrolling' | 'wrap'; +interface BaseEditorSettings { + add_form_submit_trigger?: boolean; + add_unload_trigger?: boolean; + allow_conditional_comments?: boolean; + allow_html_data_urls?: boolean; + allow_html_in_named_anchor?: boolean; + allow_script_urls?: boolean; + allow_unsafe_link_target?: boolean; + anchor_bottom?: false | string; + anchor_top?: false | string; + auto_focus?: string | true; + automatic_uploads?: boolean; + base_url?: string; + block_formats?: string; + block_unsupported_drop?: boolean; + body_id?: string; + body_class?: string; + br_in_pre?: boolean; + br_newline_selector?: string; + browser_spellcheck?: boolean; + branding?: boolean; + cache_suffix?: string; + color_cols?: number; + color_map?: string[]; + content_css?: boolean | string | string[]; + content_css_cors?: boolean; + content_security_policy?: string; + content_style?: string; + contextmenu?: string | false; + contextmenu_never_use_native?: boolean; + convert_fonts_to_spans?: boolean; + convert_urls?: boolean; + custom_colors?: boolean; + custom_elements?: string; + custom_ui_selector?: string; + custom_undo_redo_levels?: number; + directionality?: 'ltr' | 'rtl'; + doctype?: string; + document_base_url?: string; + element_format?: 'xhtml' | 'html'; + elementpath?: boolean; + encoding?: string; + end_container_on_empty_block?: boolean; + entities?: string; + entity_encoding?: EntityEncoding; + extended_valid_elements?: string; + event_root?: string; + file_picker_callback?: FilePickerCallback; + file_picker_types?: string; + file_picker_validator_handler?: FilePickerValidationCallback; + fix_list_elements?: boolean; + fixed_toolbar_container?: string; + font_formats?: string; + font_size_classes?: string; + font_size_legacy_values?: string; + font_size_style_values?: string; + fontsize_formats?: string; + force_hex_style_colors?: boolean; + forced_root_block?: boolean | string; + forced_root_block_attrs?: Record; + formats?: Formats; + gecko_spellcheck?: boolean; + height?: number | string; + hidden_input?: boolean; + icons?: string; + icons_url?: string; + id?: string; + images_dataimg_filter?: (imgElm: HTMLImageElement) => boolean; + images_replace_blob_uris?: boolean; + images_reuse_filename?: boolean; + images_upload_base_path?: string; + images_upload_credentials?: boolean; + images_upload_handler?: UploadHandler; + images_upload_url?: string; + indent?: boolean; + indent_after?: string; + indent_before?: string; + indent_use_margin?: boolean; + indentation?: string; + init_instance_callback?: SetupCallback; + inline?: boolean; + inline_boundaries?: boolean; + inline_boundaries_selector?: string; + inline_styles?: boolean; + invalid_elements?: string; + invalid_styles?: string; + keep_styles?: boolean; + language?: string; + language_load?: boolean; + language_url?: string; + lineheight_formats?: string; + max_height?: number; + max_width?: number; + menu?: Record; + menubar?: boolean | string; + min_height?: number; + min_width?: number; + no_newline_selector?: string; + nowrap?: boolean; + object_resizing?: boolean | string; + placeholder?: string; + preserve_cdata?: boolean; + preview_styles?: boolean | string; + protect?: RegExp[]; + readonly?: boolean; + referrer_policy?: ReferrerPolicy; + relative_urls?: boolean; + remove_script_host?: boolean; + remove_trailing_brs?: boolean; + removed_menuitems?: string; + resize?: boolean | 'both'; + resize_img_proportional?: boolean; + root_name?: string; + schema?: SchemaType; + selector?: string; + setup?: SetupCallback; + skin?: boolean | string; + skin_url?: string; + statusbar?: boolean; + style_formats?: AllowedFormat[]; + style_formats_autohide?: boolean; + style_formats_merge?: boolean; + submit_patch?: boolean; + suffix?: string; + target?: HTMLElement; + theme?: string | ThemeInitFunc; + theme_url?: string; + toolbar?: boolean | string | string[] | Array; + toolbar1?: string; + toolbar2?: string; + toolbar3?: string; + toolbar4?: string; + toolbar5?: string; + toolbar6?: string; + toolbar7?: string; + toolbar8?: string; + toolbar9?: string; + toolbar_mode?: ToolbarMode; + typeahead_urls?: boolean; + url_converter?: URLConverter; + url_converter_scope?: any; + urlconverter_callback?: string | URLConverterCallback; + valid_children?: string; + valid_classes?: string | Record; + valid_elements?: string; + valid_styles?: string | Record; + visual?: boolean; + visual_anchor_class?: string; + visual_table_class?: string; + width?: number | string; + toolbar_drawer?: false | 'floating' | 'sliding' | 'scrolling'; + editor_deselector?: string; + editor_selector?: string; + elements?: string; + filepicker_validator_handler?: FilePickerValidationCallback; + mode?: 'exact' | 'textareas' | 'specific_textareas'; + types?: Record[]; + block_elements?: string; + boolean_attributes?: string; + move_caret_before_on_enter_elements?: string; + non_empty_elements?: string; + self_closing_elements?: string; + short_ended_elements?: string; + text_block_elements?: string; + text_inline_elements?: string; + whitespace_elements?: string; + disable_nodechange?: boolean; + forced_plugins?: string | string[]; + plugin_base_urls?: Record; + service_message?: string; + validate?: boolean; + [key: string]: any; +} +interface RawEditorSettings extends BaseEditorSettings { + external_plugins?: Record; + mobile?: RawEditorSettings; + plugins?: string | string[]; +} +interface EditorSettings extends BaseEditorSettings { + external_plugins: Record; + plugins: string; +} +interface ParamTypeMap { + 'hash': Record; + 'string': string; + 'number': number; + 'boolean': boolean; + 'string[]': string[]; + 'array': any[]; +} +interface BlobInfoImagePair { + image: HTMLImageElement; + blobInfo: BlobInfo; +} +declare class NodeChange { + private readonly editor; + private lastPath; + constructor(editor: Editor); + nodeChanged(args?: any): void; + private isSameElementPath; +} +interface SelectionOverrides { + showCaret: (direction: number, node: Element, before: boolean, scrollIntoView?: boolean) => Range | null; + showBlockCaretContainer: (blockCaretContainer: Element) => void; + hideFakeCaret: () => void; + destroy: () => void; +} +interface Quirks { + refreshContentEditable(): void; + isHidden(): boolean; +} +declare type DecoratorData = Record; +declare type Decorator = (uid: string, data: DecoratorData) => { + attributes?: {}; + classes?: string[]; +}; +declare type AnnotationListener = (state: boolean, name: string, data?: { + uid: string; + nodes: any[]; +}) => void; +declare type AnnotationListenerApi = AnnotationListener; +interface AnnotatorSettings { + decorate: Decorator; + persistent?: boolean; +} +interface Annotator { + register: (name: string, settings: AnnotatorSettings) => void; + annotate: (name: string, data: DecoratorData) => void; + annotationChanged: (name: string, f: AnnotationListenerApi) => void; + remove: (name: string) => void; + getAll: (name: string) => Record; +} +interface GeomRect { + readonly x: number; + readonly y: number; + readonly w: number; + readonly h: number; +} +interface Rect { + inflate(rect: GeomRect, w: number, h: number): GeomRect; + relativePosition(rect: GeomRect, targetRect: GeomRect, rel: string): GeomRect; + findBestRelativePosition(rect: GeomRect, targetRect: GeomRect, constrainRect: GeomRect, rels: string[]): string | null; + intersect(rect: GeomRect, cropRect: GeomRect): GeomRect | null; + clamp(rect: GeomRect, clampRect: GeomRect, fixedSize?: boolean): GeomRect; + create(x: number, y: number, w: number, h: number): GeomRect; + fromClientRect(clientRect: ClientRect): GeomRect; +} +interface StyleMap { + [s: string]: string | number; +} +interface StylesSettings { + allow_script_urls?: boolean; + allow_svg_data_urls?: boolean; + url_converter?: URLConverter; + url_converter_scope?: any; +} +interface Styles { + toHex(color: string): string; + parse(css: string): Record; + serialize(styles: StyleMap, elementName?: string): string; +} +interface DOMUtilsSettings { + schema: Schema; + url_converter: URLConverter; + url_converter_scope: {}; + ownEvents: boolean; + proxy: any; + keep_values: boolean; + hex_colors: boolean; + update_styles: boolean; + root_element: HTMLElement; + collect: Function; + onSetAttrib: Function; + contentCssCors: boolean; + referrerPolicy: ReferrerPolicy; +} +declare type Target = Node | Window; +declare type RunArguments = string | T | Array; +declare type BoundEvent = [Target, string, EventUtilsCallback, any]; +declare type Callback = EventUtilsCallback>; +interface DOMUtils { + doc: Document; + settings: Partial; + win: Window; + files: Record; + stdMode: boolean; + boxModel: boolean; + styleSheetLoader: StyleSheetLoader; + boundEvents: BoundEvent[]; + styles: Styles; + schema: Schema; + events: EventUtils; + root: Node; + $: DomQueryConstructor; + $$(elm: T | T[] | DomQuery): DomQuery; + $$(elm: string): DomQuery; + isBlock(node: string | Node): boolean; + clone(node: Node, deep: boolean): Node; + getRoot(): HTMLElement; + getViewPort(argWin?: Window): GeomRect; + getRect(elm: string | HTMLElement): GeomRect; + getSize(elm: string | HTMLElement): { + w: number; + h: number; + }; + getParent(node: string | Node, selector: K, root?: Node): HTMLElementTagNameMap[K] | null; + getParent(node: string | Node, selector: (node: HTMLElement) => node is T, root?: Node): T | null; + getParent(node: string | Node, selector?: string | ((node: HTMLElement) => boolean | void), root?: Node): T | null; + getParents(elm: string | Node, selector: K, root?: Node, collect?: boolean): Array; + getParents(node: string | Node, selector: (node: HTMLElement) => node is T, root?: Node): T[]; + getParents(elm: string | Node, selector?: string | ((node: HTMLElement) => boolean | void), root?: Node, collect?: boolean): T[]; + get(elm: string | Node): HTMLElement | null; + getNext(node: Node, selector: string | ((node: Node) => boolean)): Node | null; + getPrev(node: Node, selector: string | ((node: Node) => boolean)): Node | null; + select(selector: K, scope?: string | Node): Array; + select(selector: string, scope?: string | Node): T[]; + is(elm: Node | Node[], selector: string): boolean; + add(parentElm: RunArguments, name: string | Node, attrs?: Record, html?: string | Node, create?: boolean): HTMLElement; + create(name: string, attrs?: Record, html?: string | Node): HTMLElement; + createHTML(name: string, attrs?: Record, html?: string): string; + createFragment(html?: string): DocumentFragment; + remove(node: string | T | T[] | DomQuery, keepChildren?: boolean): T | T[]; + setStyle(elm: string | Node, name: string, value: string | number | null): void; + setStyle(elm: string | Node, styles: StyleMap): void; + getStyle(elm: string | Node, name: string, computed?: boolean): string; + setStyles(elm: string | Node, stylesArg: StyleMap): void; + removeAllAttribs(e: RunArguments): void; + setAttrib(elm: string | Node, name: string, value: string | boolean | number | null): void; + setAttribs(elm: string | Node, attrs: Record): void; + getAttrib(elm: string | Node, name: string, defaultVal?: string): string; + getPos(elm: string | Node, rootElm?: Node): { + x: number; + y: number; + }; + parseStyle(cssText: string): Record; + serializeStyle(stylesArg: StyleMap, name?: string): string; + addStyle(cssText: string): void; + loadCSS(url: string): void; + addClass(elm: string | Node | Node[], cls: string): void; + removeClass(elm: string | Node | Node[], cls: string): void; + hasClass(elm: string | Node, cls: string): boolean; + toggleClass(elm: string | Node | Node[], cls: string, state?: boolean): void; + show(elm: string | Node): void; + hide(elm: string | Node): void; + isHidden(elm: string | Node): boolean; + uniqueId(prefix?: string): string; + setHTML(elm: string | Node, html: string): void; + getOuterHTML(elm: string | Node): string; + setOuterHTML(elm: string | Node, html: string): void; + decode(text: string): string; + encode(text: string): string; + insertAfter(node: T | T[], reference: string | Node): T; + insertAfter(node: RunArguments, reference: string | Node): false | T; + replace(newElm: Node, oldElm: T | T[], keepChildren?: boolean): T; + replace(newElm: Node, oldElm: RunArguments, keepChildren?: boolean): false | T; + rename(elm: Element, name: K): HTMLElementTagNameMap[K]; + rename(elm: Element, name: string): Element; + findCommonAncestor(a: Node, b: Node): Node; + toHex(rgbVal: string): string; + run(elm: T | T[], func: (node: T) => R, scope?: any): R; + run(elm: RunArguments, func: (node: T) => R, scope?: any): false | R; + getAttribs(elm: string | Node): NamedNodeMap | Attr[]; + isEmpty(node: Node, elements?: Record): boolean; + createRng(): Range; + nodeIndex(node: Node, normalized?: boolean): number; + split(parentElm: Node, splitElm: Node, replacementElm: T): T; + split(parentElm: Node, splitElm: T): T; + bind(target: Target, name: K, func: Callback, scope?: any): Callback; + bind(target: Target[], name: K, func: Callback, scope?: any): Callback[]; + unbind(target: Target, name?: K, func?: EventUtilsCallback>): EventUtils; + unbind(target: Target[], name?: K, func?: EventUtilsCallback>): EventUtils[]; + fire(target: Node | Window, name: string, evt?: {}): EventUtils; + getContentEditable(node: Node): string | null; + getContentEditableParent(node: Node): string | null; + destroy(): void; + isChildOf(node: Node, parent: Node): boolean; + dumpRng(r: Range): string; +} +declare namespace DOMUtils { + const DOM: DOMUtils; + const nodeIndex: (node: Node, normalized?: boolean) => number; +} +interface GetSelectionContentArgs extends GetContentArgs { + selection?: boolean; + contextual?: boolean; +} +interface SelectionSetContentArgs extends SetContentArgs { + selection?: boolean; +} +interface BookmarkManager { + getBookmark(type: number, normalized?: boolean): Bookmark; + moveToBookmark(bookmark: Bookmark): boolean; +} +declare namespace BookmarkManager { + const isBookmarkNode: (node: Node) => boolean; +} +interface ControlSelection { + isResizable(elm: Element): boolean; + showResizeRect(elm: Element): void; + hideResizeRect(): void; + updateResizeRect(evt: EditorEvent): void; + destroy(): void; +} +interface ParserArgs { + getInner?: boolean | number; + forced_root_block?: boolean | string; + context?: string; + isRootContent?: boolean; + format?: string; + [key: string]: any; +} +declare type ParserFilterCallback = (nodes: AstNode[], name: string, args: ParserArgs) => void; +interface ParserFilter { + name: string; + callbacks: ParserFilterCallback[]; +} +interface DomParserSettings { + allow_html_data_urls?: boolean; + allow_conditional_comments?: boolean; + allow_html_in_named_anchor?: boolean; + allow_script_urls?: boolean; + allow_unsafe_link_target?: boolean; + convert_fonts_to_spans?: boolean; + fix_list_elements?: boolean; + font_size_legacy_values?: string; + forced_root_block?: boolean | string; + forced_root_block_attrs?: Record; + padd_empty_with_br?: boolean; + preserve_cdata?: boolean; + remove_trailing_brs?: boolean; + root_name?: string; + validate?: boolean; + inline_styles?: boolean; + blob_cache?: BlobCache; + images_dataimg_filter?: (img: HTMLImageElement) => boolean; +} +interface DomParser { + schema: Schema; + addAttributeFilter(name: string, callback: (nodes: AstNode[], name: string, args: ParserArgs) => void): void; + getAttributeFilters(): ParserFilter[]; + addNodeFilter(name: string, callback: (nodes: AstNode[], name: string, args: ParserArgs) => void): void; + getNodeFilters(): ParserFilter[]; + filterNode(node: AstNode): AstNode; + parse(html: string, args?: ParserArgs): AstNode; +} +interface WriterSettings { + element_format?: 'xhtml' | 'html'; + entities?: string; + entity_encoding?: string; + indent?: boolean; + indent_after?: string; + indent_before?: string; +} +interface Writer { + cdata(text: string): void; + comment(text: string): void; + doctype(text: string): void; + end(name: string): void; + getContent(): string; + pi(name: string, text: string): void; + reset(): void; + start(name: string, attrs?: Attributes, empty?: boolean): void; + text(text: string, raw?: boolean): void; +} +interface HtmlSerializerSettings extends WriterSettings { + inner?: boolean; + validate?: boolean; +} +interface HtmlSerializer { + serialize(node: AstNode): string; +} +interface DomSerializerArgs extends ParserArgs { + format?: string; +} +interface DomSerializerSettings extends DomParserSettings, WriterSettings, SchemaSettings, HtmlSerializerSettings { + url_converter?: URLConverter; + url_converter_scope?: {}; +} +interface DomSerializerImpl { + schema: Schema; + addNodeFilter(name: string, callback: (nodes: AstNode[], name: string, args: ParserArgs) => void): void; + addAttributeFilter(name: string, callback: (nodes: AstNode[], name: string, args: ParserArgs) => void): void; + getNodeFilters(): ParserFilter[]; + getAttributeFilters(): ParserFilter[]; + serialize(node: Element, parserArgs: { + format: 'tree'; + } & DomSerializerArgs): AstNode; + serialize(node: Element, parserArgs?: DomSerializerArgs): string; + addRules(rules: string): void; + setRules(rules: string): void; + addTempAttr(name: string): void; + getTempAttrs(): string[]; +} +interface DomSerializer extends DomSerializerImpl { +} +interface EditorSelection { + bookmarkManager: BookmarkManager; + controlSelection: ControlSelection; + dom: DOMUtils; + win: Window; + serializer: DomSerializer; + editor: Editor; + collapse: (toStart?: boolean) => void; + setCursorLocation: (node?: Node, offset?: number) => void; + getContent(args: { + format: 'tree'; + } & GetSelectionContentArgs): AstNode; + getContent(args?: GetSelectionContentArgs): string; + setContent: (content: string, args?: SelectionSetContentArgs) => void; + getBookmark: (type?: number, normalized?: boolean) => Bookmark; + moveToBookmark: (bookmark: Bookmark) => boolean; + select: (node: Node, content?: boolean) => Node; + isCollapsed: () => boolean; + isForward: () => boolean; + setNode: (elm: Element) => Element; + getNode: () => Element; + getSel: () => Selection | null; + setRng: (rng: Range, forward?: boolean) => void; + getRng: () => Range | null; + getStart: (real?: boolean) => Element; + getEnd: (real?: boolean) => Element; + getSelectedBlocks: (startElm?: Element, endElm?: Element) => Element[]; + normalize: () => Range; + selectorChanged: (selector: string, callback: (active: boolean, args: { + node: Node; + selector: String; + parents: Element[]; + }) => void) => EditorSelection; + selectorChangedWithUnbind: (selector: string, callback: (active: boolean, args: { + node: Node; + selector: String; + parents: Element[]; + }) => void) => { + unbind: () => void; + }; + getScrollContainer: () => HTMLElement; + scrollIntoView: (elm: Element, alignToTop?: boolean) => void; + placeCaretAt: (clientX: number, clientY: number) => void; + getBoundingClientRect: () => ClientRect; + destroy: () => void; +} +declare type EditorCommandCallback = (ui: boolean, value: any, args: any) => void; +declare type EditorCommandsCallback = (command: string, ui: boolean, value: any, args: any) => void; +interface Commands { + state: Record boolean>; + exec: Record; + value: Record string>; +} +interface EditorCommandsConstructor { + readonly prototype: EditorCommands; + new (editor: Editor): EditorCommands; +} +declare class EditorCommands { + private readonly editor; + private selectionBookmark; + private commands; + constructor(editor: Editor); + execCommand(command: string, ui?: boolean, value?: any, args?: any): boolean; + queryCommandState(command: string): boolean; + queryCommandValue(command: string): string; + addCommands(commandList: Commands[K], type: K): void; + addCommands(commandList: Record): void; + addCommand(command: string, callback: EditorCommandCallback, scope?: any): void; + queryCommandSupported(command: string): boolean; + addQueryStateHandler(command: string, callback: () => boolean, scope?: any): void; + addQueryValueHandler(command: string, callback: () => string, scope?: any): void; + hasCustomCommand(command: string): boolean; + private execNativeCommand; + private isFormatMatch; + private toggleFormat; + private storeSelection; + private restoreSelection; + private setupCommands; +} +interface WindowManager { + open: (config: DialogSpec, params?: any) => DialogInstanceApi; + openUrl: (config: UrlDialogSpec) => UrlDialogInstanceApi; + alert: (message: string, callback?: () => void, scope?: any) => void; + confirm: (message: string, callback?: (state: boolean) => void, scope?: any) => void; + close: () => void; +} +declare type InstanceApi = UrlDialogInstanceApi | DialogInstanceApi; +interface WindowManagerImpl { + open: (config: DialogSpec, params: any, closeWindow: (dialog: DialogInstanceApi) => void) => DialogInstanceApi; + openUrl: (config: UrlDialogSpec, closeWindow: (dialog: UrlDialogInstanceApi) => void) => UrlDialogInstanceApi; + alert: (message: string, callback: () => void) => void; + confirm: (message: string, callback: (state: boolean) => void) => void; + close: (dialog: InstanceApi) => void; +} +declare type ExecCommandEvent = { + command: string; + ui?: boolean; + value?: any; +}; +declare type GetContentEvent = GetContentArgs & { + source_view?: boolean; + selection?: boolean; + save?: boolean; +}; +declare type SetContentEvent = SetContentArgs & { + paste?: boolean; + selection?: boolean; +}; +declare type NewBlockEvent = { + newBlock: Element; +}; +declare type NodeChangeEvent = { + element: Element; + parents: Node[]; + selectionChange?: boolean; + initial?: boolean; +}; +declare type ObjectResizeEvent = { + target: HTMLElement; + width: number; + height: number; + origin: string; +}; +declare type ObjectSelectedEvent = { + target: Node; + targetClone?: Node; +}; +declare type ScrollIntoViewEvent = { + elm: HTMLElement; + alignToTop: boolean; +}; +declare type SetSelectionRangeEvent = { + range: Range; + forward: boolean; +}; +declare type ShowCaretEvent = { + target: Node; + direction: number; + before: boolean; +}; +declare type SwitchModeEvent = { + mode: string; +}; +declare type AddUndoEvent = { + level: UndoLevel; + lastLevel: UndoLevel; + originalEvent: Event; +}; +declare type UndoRedoEvent = { + level: UndoLevel; +}; +declare type WindowEvent = { + dialog: InstanceApi; +}; +declare type ProgressStateEvent = { + state: boolean; + time?: number; +}; +declare type PlaceholderToggleEvent = { + state: boolean; +}; +declare type LoadErrorEvent = { + message: string; +}; +interface EditorEventMap extends Omit { + 'activate': { + relatedTarget: Editor; + }; + 'deactivate': { + relatedTarget: Editor; + }; + 'focus': { + blurredEditor: Editor; + }; + 'blur': { + focusedEditor: Editor; + }; + 'resize': UIEvent; + 'scroll': UIEvent; + 'detach': {}; + 'remove': {}; + 'init': {}; + 'ScrollIntoView': ScrollIntoViewEvent; + 'AfterScrollIntoView': ScrollIntoViewEvent; + 'ObjectResized': ObjectResizeEvent; + 'ObjectResizeStart': ObjectResizeEvent; + 'SwitchMode': SwitchModeEvent; + 'ScrollWindow': UIEvent; + 'ResizeWindow': UIEvent; + 'SkinLoaded': {}; + 'SkinLoadError': LoadErrorEvent; + 'PluginLoadError': LoadErrorEvent; + 'IconsLoadError': LoadErrorEvent; + 'LanguageLoadError': LoadErrorEvent; + 'BeforeExecCommand': ExecCommandEvent; + 'ExecCommand': ExecCommandEvent; + 'NodeChange': NodeChangeEvent; + 'ShowCaret': ShowCaretEvent; + 'SelectionChange': {}; + 'ObjectSelected': ObjectSelectedEvent; + 'BeforeObjectSelected': ObjectSelectedEvent; + 'GetSelectionRange': { + range: Range; + }; + 'SetSelectionRange': SetSelectionRangeEvent; + 'AfterSetSelectionRange': SetSelectionRangeEvent; + 'BeforeGetContent': GetContentEvent; + 'GetContent': GetContentEvent; + 'BeforeSetContent': SetContentEvent; + 'SetContent': SetContentEvent; + 'LoadContent': {}; + 'PreviewFormats': {}; + 'AfterPreviewFormats': {}; + 'ScriptsLoaded': {}; + 'PreInit': {}; + 'PostRender': {}; + 'NewBlock': NewBlockEvent; + 'ClearUndos': {}; + 'TypingUndo': {}; + 'Redo': UndoRedoEvent; + 'Undo': UndoRedoEvent; + 'BeforeAddUndo': AddUndoEvent; + 'AddUndo': AddUndoEvent; + 'CloseWindow': WindowEvent; + 'OpenWindow': WindowEvent; + 'ProgressState': ProgressStateEvent; + 'PlaceholderToggle': PlaceholderToggleEvent; + 'tap': TouchEvent; + 'longpress': TouchEvent; + 'longpresscancel': {}; +} +interface EditorManagerEventMap { + 'AddEditor': { + editor: Editor; + }; + 'RemoveEditor': { + editor: Editor; + }; + 'BeforeUnload': { + returnValue: any; + }; +} +type EventTypes_d_ExecCommandEvent = ExecCommandEvent; +type EventTypes_d_GetContentEvent = GetContentEvent; +type EventTypes_d_SetContentEvent = SetContentEvent; +type EventTypes_d_NewBlockEvent = NewBlockEvent; +type EventTypes_d_NodeChangeEvent = NodeChangeEvent; +type EventTypes_d_ObjectResizeEvent = ObjectResizeEvent; +type EventTypes_d_ObjectSelectedEvent = ObjectSelectedEvent; +type EventTypes_d_ScrollIntoViewEvent = ScrollIntoViewEvent; +type EventTypes_d_SetSelectionRangeEvent = SetSelectionRangeEvent; +type EventTypes_d_ShowCaretEvent = ShowCaretEvent; +type EventTypes_d_SwitchModeEvent = SwitchModeEvent; +type EventTypes_d_AddUndoEvent = AddUndoEvent; +type EventTypes_d_UndoRedoEvent = UndoRedoEvent; +type EventTypes_d_WindowEvent<_0> = WindowEvent<_0>; +type EventTypes_d_ProgressStateEvent = ProgressStateEvent; +type EventTypes_d_PlaceholderToggleEvent = PlaceholderToggleEvent; +type EventTypes_d_LoadErrorEvent = LoadErrorEvent; +type EventTypes_d_EditorEventMap = EditorEventMap; +type EventTypes_d_EditorManagerEventMap = EditorManagerEventMap; +declare namespace EventTypes_d { + //export { EventTypes_d_ExecCommandEvent as ExecCommandEvent, EventTypes_d_GetContentEvent as GetContentEvent, EventTypes_d_SetContentEvent as SetContentEvent, EventTypes_d_NewBlockEvent as NewBlockEvent, EventTypes_d_NodeChangeEvent as NodeChangeEvent, EventTypes_d_ObjectResizeEvent as ObjectResizeEvent, EventTypes_d_ObjectSelectedEvent as ObjectSelectedEvent, EventTypes_d_ScrollIntoViewEvent as ScrollIntoViewEvent, EventTypes_d_SetSelectionRangeEvent as SetSelectionRangeEvent, EventTypes_d_ShowCaretEvent as ShowCaretEvent, EventTypes_d_SwitchModeEvent as SwitchModeEvent, EventTypes_d_AddUndoEvent as AddUndoEvent, EventTypes_d_UndoRedoEvent as UndoRedoEvent, EventTypes_d_WindowEvent as WindowEvent, EventTypes_d_ProgressStateEvent as ProgressStateEvent, EventTypes_d_PlaceholderToggleEvent as PlaceholderToggleEvent, EventTypes_d_LoadErrorEvent as LoadErrorEvent, EventTypes_d_EditorEventMap as EditorEventMap, EventTypes_d_EditorManagerEventMap as EditorManagerEventMap, }; +} +interface RawString { + raw: string; +} +declare type Primitive = string | number | boolean | Record | Function; +declare type TokenisedString = [string, ...Primitive[]]; +declare type Untranslated = Primitive | TokenisedString | RawString; +declare type TranslatedString = string; +interface I18n { + getData(): Record>; + setCode(newCode: string): void; + getCode(): string; + add(code: string, items: Record): void; + translate(text: Untranslated): TranslatedString; + isRtl(): boolean; + hasCode(code: string): boolean; +} +interface Observable { + fire>(name: K, args?: U, bubble?: boolean): EditorEvent; + on(name: K, callback: (event: EditorEvent>) => void, prepend?: boolean): EventDispatcher; + off(name?: K, callback?: (event: EditorEvent>) => void): EventDispatcher; + once(name: K, callback: (event: EditorEvent>) => void): EventDispatcher; + hasEventListeners(name: string): boolean; +} +interface URISettings { + base_uri?: URI; +} +interface URIConstructor { + readonly prototype: URI; + new (url: string, settings?: URISettings): URI; + getDocumentBaseUrl(loc: { + protocol: string; + host?: string; + href?: string; + pathname?: string; + }): string; + parseDataUri(uri: string): { + type: string; + data: string; + }; +} +declare class URI { + static parseDataUri(uri: string): { + type: string; + data: string; + }; + static getDocumentBaseUrl(loc: { + protocol: string; + host?: string; + href?: string; + pathname?: string; + }): string; + source: string; + protocol: string; + authority: string; + userInfo: string; + user: string; + password: string; + host: string; + port: string; + relative: string; + path: string; + directory: string; + file: string; + query: string; + anchor: string; + settings: URISettings; + constructor(url: string, settings?: URISettings); + setPath(path: string): void; + toRelative(uri: string): string; + toAbsolute(uri: string, noHost?: boolean): string; + isSameOrigin(uri: URI): boolean; + toRelPath(base: string, path: string): string; + toAbsPath(base: string, path: string): string; + getURI(noProtoHost?: boolean): string; +} +interface EditorManager extends Observable { + $: DomQueryConstructor; + defaultSettings: RawEditorSettings; + majorVersion: string; + minorVersion: string; + releaseDate: string; + editors: Editor[]; + activeEditor: Editor; + focusedEditor: Editor; + settings: RawEditorSettings; + baseURI: URI; + baseURL: string; + documentBaseURL: string; + i18n: I18n; + suffix: string; + add(editor: Editor): Editor; + addI18n(code: string, item: Record): void; + createEditor(id: string, settings: RawEditorSettings): Editor; + execCommand(cmd: string, ui: boolean, value: any): boolean; + get(): Editor[]; + get(id: number | string): Editor; + init(settings: RawEditorSettings): Promise; + overrideDefaults(defaultSettings: Partial): void; + remove(): void; + remove(selector: string | Editor): Editor | void; + setActive(editor: Editor): void; + setup(): void; + translate(text: string): string; + triggerSave(): void; + _setBaseUrl(baseUrl: string): void; +} +interface EditorObservable extends Observable { + bindPendingEventDelegates(): void; + toggleNativeEvent(name: string, state: boolean): any; + unbindAllNativeEvents(): void; +} +interface UploadResult { + element: HTMLImageElement; + status: boolean; + blobInfo: BlobInfo; + uploadUri: string; +} +declare type UploadCallback = (results: UploadResult[]) => void; +interface EditorUpload { + blobCache: BlobCache; + addFilter(filter: (img: HTMLImageElement) => boolean): void; + uploadImages(callback?: UploadCallback): Promise; + uploadImagesAuto(callback?: UploadCallback): void | Promise; + scanForImages(): Promise; + destroy(): void; +} +declare type FormatChangeCallback = (state: boolean, data: { + node: Node; + format: string; + parents: any; +}) => void; +interface FormatRegistry { + get(name?: string): Format[] | Record; + has(name: string): boolean; + register(name: string | Formats, format?: Format[] | Format): void; + unregister(name: string): Formats; +} +interface Formatter extends FormatRegistry { + apply(name: string, vars?: FormatVars, node?: Node | RangeLikeObject): void; + remove(name: string, vars?: FormatVars, node?: Node | Range, similar?: boolean): void; + toggle(name: string, vars?: FormatVars, node?: Node): void; + match(name: string, vars?: FormatVars, node?: Node): boolean; + matchAll(names: string[], vars?: FormatVars): string[]; + matchNode(node: Node, name: string, vars?: FormatVars, similar?: boolean): boolean; + canApply(name: string): boolean; + formatChanged(names: string, callback: FormatChangeCallback, similar?: boolean): { + unbind: () => void; + }; + getCssText(format: string | Format): string; +} +interface EditorMode { + isReadOnly: () => boolean; + set: (mode: string) => void; + get: () => string; + register: (mode: string, api: EditorModeApi) => void; +} +interface EditorModeApi { + activate: () => void; + deactivate: () => void; + editorReadOnly: boolean; +} +interface NotificationManagerImpl { + open(spec: NotificationSpec, closeCallback?: () => void): NotificationApi; + close(notification: T): void; + reposition(notifications: T[]): void; + getArgs(notification: T): NotificationSpec; +} +interface NotificationSpec { + type?: 'info' | 'warning' | 'error' | 'success'; + text: string; + icon?: string; + progressBar?: boolean; + timeout?: number; + closeButton?: boolean; +} +interface NotificationApi { + close: () => void; + progressBar: { + value: (percent: number) => void; + }; + text: (text: string) => void; + moveTo: (x: number, y: number) => void; + moveRel: (element: Element, rel: 'tc-tc' | 'bc-bc' | 'bc-tc' | 'tc-bc' | 'banner') => void; + getEl: () => HTMLElement; + settings: NotificationSpec; +} +interface NotificationManager { + open: (spec: NotificationSpec) => NotificationApi; + close: () => void; + getNotifications: () => NotificationApi[]; +} +interface Plugin { + getMetadata?(): { + name: string; + url: string; + }; + [key: string]: any; +} +declare type PluginManager = AddOnManager; +interface ShortcutsConstructor { + readonly prototype: Shortcuts; + new (editor: Editor): Shortcuts; +} +declare type CommandFunc = string | [string, boolean, any] | (() => void); +declare class Shortcuts { + private readonly editor; + private readonly shortcuts; + private pendingPatterns; + constructor(editor: Editor); + add(pattern: string, desc: string, cmdFunc: CommandFunc, scope?: any): boolean; + remove(pattern: string): boolean; + private normalizeCommandFunc; + private parseShortcut; + private createShortcut; + private hasModifier; + private isFunctionKey; + private matchShortcut; + private executeShortcutAction; +} +declare type Theme = { + ui?: any; + inline?: any; + execCommand?(command: string, ui?: boolean, value?: any): boolean; + destroy?(): void; + init?(editor: Editor, url: string, $: DomQueryConstructor): any; + renderUI?(): { + iframeContainer?: HTMLIFrameElement; + editorContainer: HTMLElement; + api?: EditorUiApi; + }; + getNotificationManagerImpl?(): NotificationManagerImpl; + getWindowManagerImpl?(): WindowManagerImpl; +}; +declare type ThemeManager = AddOnManager; +interface EditorConstructor { + readonly prototype: Editor; + new (id: string, settings: RawEditorSettings, editorManager: EditorManager): Editor; +} +declare class Editor implements EditorObservable { + documentBaseUrl: string; + baseUri: URI; + settings: EditorSettings; + id: string; + plugins: Record; + documentBaseURI: URI; + baseURI: URI; + contentCSS: string[]; + contentStyles: string[]; + ui: EditorUi; + mode: EditorMode; + setMode: (mode: string) => void; + $: DomQueryConstructor; + shortcuts: Shortcuts; + loadedCSS: Record; + editorCommands: EditorCommands; + suffix: string; + editorManager: EditorManager; + inline: boolean; + isNotDirty: boolean; + callbackLookup: any; + _nodeChangeDispatcher: NodeChange; + editorUpload: EditorUpload; + annotator: Annotator; + bodyElement: HTMLElement; + bookmark: any; + composing: boolean; + container: HTMLElement; + contentAreaContainer: HTMLElement; + contentDocument: Document; + contentWindow: Window; + delegates: Record void>; + destroyed: boolean; + dom: DOMUtils; + editorContainer: HTMLElement; + eventRoot?: Element; + formatter: Formatter; + formElement: HTMLElement; + formEventDelegate: (e: Event) => void; + hasHiddenInput: boolean; + hasVisual: boolean; + hidden: boolean; + iframeElement: HTMLIFrameElement | null; + iframeHTML: string; + initialized: boolean; + notificationManager: NotificationManager; + orgDisplay: string; + orgVisibility: string; + parser: DomParser; + quirks: Quirks; + readonly: boolean; + removed: boolean; + schema: Schema; + selection: EditorSelection; + serializer: DomSerializer; + startContent: string; + targetElm: HTMLElement; + theme: Theme; + undoManager: UndoManager; + validate: boolean; + windowManager: WindowManager; + _beforeUnload: () => void; + _eventDispatcher: EventDispatcher; + _mceOldSubmit: any; + _pendingNativeEvents: string[]; + _selectionOverrides: SelectionOverrides; + _skinLoaded: boolean; + bindPendingEventDelegates: EditorObservable['bindPendingEventDelegates']; + toggleNativeEvent: EditorObservable['toggleNativeEvent']; + unbindAllNativeEvents: EditorObservable['unbindAllNativeEvents']; + fire: EditorObservable['fire']; + on: EditorObservable['on']; + off: EditorObservable['off']; + once: EditorObservable['once']; + hasEventListeners: EditorObservable['hasEventListeners']; + constructor(id: string, settings: RawEditorSettings, editorManager: EditorManager); + render(): void; + focus(skipFocus?: boolean): void; + hasFocus(): boolean; + execCallback(name: string, ...x: any[]): any; + translate(text: Untranslated): TranslatedString; + getParam(name: string, defaultVal: ParamTypeMap[K], type: K): ParamTypeMap[K]; + getParam(name: K, defaultVal?: EditorSettings[K], type?: string): EditorSettings[K]; + getParam(name: string, defaultVal: T, type?: string): T; + hasPlugin(name: string, loaded?: boolean): boolean; + nodeChanged(args?: any): void; + addCommand(name: string, callback: EditorCommandCallback, scope?: object): void; + addQueryStateHandler(name: string, callback: () => boolean, scope?: any): void; + addQueryValueHandler(name: string, callback: () => string, scope?: any): void; + addShortcut(pattern: string, desc: string, cmdFunc: string | [string, boolean, any] | (() => void), scope?: any): void; + execCommand(cmd: string, ui?: boolean, value?: any, args?: any): boolean; + queryCommandState(cmd: string): boolean; + queryCommandValue(cmd: string): string; + queryCommandSupported(cmd: string): boolean; + show(): void; + hide(): void; + isHidden(): boolean; + setProgressState(state: boolean, time?: number): void; + load(args?: any): string; + save(args?: any): string; + setContent(content: string, args?: SetContentArgs): string; + setContent(content: AstNode, args?: SetContentArgs): AstNode; + getContent(args: { + format: 'tree'; + } & GetContentArgs): AstNode; + getContent(args?: GetContentArgs): string; + insertContent(content: string, args?: any): void; + resetContent(initialContent?: string): void; + isDirty(): boolean; + setDirty(state: boolean): void; + getContainer(): HTMLElement; + getContentAreaContainer(): HTMLElement; + getElement(): HTMLElement; + getWin(): Window; + getDoc(): Document; + getBody(): HTMLElement; + convertURL(url: string, name: string, elm?: any): string; + addVisual(elm?: HTMLElement): void; + remove(): void; + destroy(automatic?: boolean): void; + uploadImages(callback?: UploadCallback): Promise; + _scanForImages(): Promise; + addButton(): void; + addSidebar(): void; + addMenuItem(): void; + addContextToolbar(): void; +} +interface UrlObject { + prefix: string; + resource: string; + suffix: string; +} +declare type WaitState = 'added' | 'loaded'; +declare type AddOnCallback = (editor: Editor, url: string, $?: DomQueryConstructor) => void | T; +declare type AddOnConstructor = new (editor: Editor, url: string, $?: DomQueryConstructor) => T; +interface AddOnManager { + items: AddOnConstructor[]; + urls: Record; + lookup: Record; + dependencies?: string[]; + }>; + _listeners: { + name: string; + state: WaitState; + callback: () => void; + }[]; + get(name: string): AddOnConstructor; + dependencies(name: string): string[]; + requireLangPack(name: string, languages: string): void; + add(id: string, addOn: AddOnCallback, dependencies?: string[]): AddOnConstructor; + remove(name: string): void; + createUrl(baseUrl: UrlObject, dep: string | UrlObject): UrlObject; + addComponents(pluginName: string, scripts: string[]): void; + load(name: string, addOnUrl: string | UrlObject, success?: () => void, scope?: any, failure?: () => void): void; + waitFor(name: string, callback: () => void, state?: WaitState): void; +} +declare namespace AddOnManager { + let language: string; + let languageLoad: boolean; + let baseURL: string; + const PluginManager: AddOnManager; + const ThemeManager: AddOnManager; +} +interface RangeUtils { + walk(rng: Range, callback: (nodes: Node[]) => void): void; + split(rng: Range): RangeLikeObject; + normalize(rng: Range): boolean; +} +declare namespace RangeUtils { + const compareRanges: (rng1: RangeLikeObject, rng2: RangeLikeObject) => boolean; + const getCaretRangeFromPoint: (clientX: number, clientY: number, doc: Document) => Range; + const getSelectedNode: (range: Range) => Node; + const getNode: (container: Node, offset: number) => Node; +} +interface ScriptLoaderSettings { + referrerPolicy?: ReferrerPolicy; +} +interface ScriptLoaderConstructor { + readonly prototype: ScriptLoader; + new (): ScriptLoader; + ScriptLoader: ScriptLoader; +} +declare class ScriptLoader { + static ScriptLoader: ScriptLoader; + private settings; + private states; + private queue; + private scriptLoadedCallbacks; + private queueLoadedCallbacks; + private loading; + constructor(settings?: ScriptLoaderSettings); + _setReferrerPolicy(referrerPolicy: ReferrerPolicy): void; + loadScript(url: string, success?: () => void, failure?: () => void): void; + isDone(url: string): boolean; + markDone(url: string): void; + add(url: string, success?: () => void, scope?: any, failure?: () => void): void; + load(url: string, success?: () => void, scope?: any, failure?: () => void): void; + remove(url: string): void; + loadQueue(success?: () => void, scope?: any, failure?: (urls: string[]) => void): void; + loadScripts(scripts: string[], success?: () => void, scope?: any, failure?: (urls: string[]) => void): void; +} +declare type TextProcessCallback = (node: Text, offset: number, text: string) => number; +interface Spot { + container: Text; + offset: number; +} +interface TextSeeker { + backwards: (node: Node, offset: number, process: TextProcessCallback, root?: Node) => Spot | null; + forwards: (node: Node, offset: number, process: TextProcessCallback, root?: Node) => Spot | null; +} +interface DomTreeWalkerConstructor { + readonly prototype: DomTreeWalker; + new (startNode: Node, rootNode: Node): DomTreeWalker; +} +declare class DomTreeWalker { + private readonly rootNode; + private node; + constructor(startNode: Node, rootNode: Node); + current(): Node; + next(shallow?: boolean): Node; + prev(shallow?: boolean): Node; + prev2(shallow?: boolean): Node; + private findSibling; + private findPreviousNode; +} +interface Version { + major: number; + minor: number; +} +interface Env { + opera: boolean; + webkit: boolean; + ie: false | number; + gecko: boolean; + mac: boolean; + iOS: boolean; + android: boolean; + contentEditable: boolean; + transparentSrc: string; + caretAfter: boolean; + range: boolean; + documentMode: number; + fileApi: boolean; + ceFalse: boolean; + cacheSuffix: any; + container: any; + experimentalShadowDom: boolean; + canHaveCSP: boolean; + desktop: boolean; + windowsPhone: boolean; + browser: { + current: string | undefined; + version: Version; + isEdge: () => boolean; + isChrome: () => boolean; + isIE: () => boolean; + isOpera: () => boolean; + isFirefox: () => boolean; + isSafari: () => boolean; + }; + os: { + current: string | undefined; + version: Version; + isWindows: () => boolean; + isiOS: () => boolean; + isAndroid: () => boolean; + isOSX: () => boolean; + isLinux: () => boolean; + isSolaris: () => boolean; + isFreeBSD: () => boolean; + isChromeOS: () => boolean; + }; + deviceType: { + isiPad: () => boolean; + isiPhone: () => boolean; + isTablet: () => boolean; + isPhone: () => boolean; + isTouch: () => boolean; + isWebView: () => boolean; + isDesktop: () => boolean; + }; +} +interface FocusManager { + isEditorUIElement: (elm: Element) => boolean; +} +interface EntitiesMap { + [name: string]: string; +} +interface Entities { + encodeRaw(text: string, attr?: boolean): string; + encodeAllRaw(text: string): string; + encodeNumeric(text: string, attr?: boolean): string; + encodeNamed(text: string, attr?: boolean, entities?: EntitiesMap): string; + getEncodeFunc(name: string, entities?: EntitiesMap | string): (text: string, attr?: boolean) => string; + decode(text: string): string; +} +declare type AttrList = Array<{ + name: string; + value: string; +}> & { + map: Record; +}; +interface SaxParserSettings { + allow_conditional_comments?: boolean; + allow_html_data_urls?: boolean; + allow_script_urls?: boolean; + allow_svg_data_urls?: boolean; + fix_self_closing?: boolean; + preserve_cdata?: boolean; + remove_internals?: boolean; + self_closing_elements?: Record; + validate?: boolean; + cdata?(text: string): void; + comment?(text: string): void; + doctype?(text: string): void; + end?(name: string): void; + pi?(name: string, text: string): void; + start?(name: string, attrs: AttrList, empty: boolean): void; + text?(text: string, raw?: boolean): void; +} +declare type ParserFormat = 'html' | 'xhtml' | 'xml'; +interface SaxParser { + parse(html: string, format?: ParserFormat): void; +} +declare namespace SaxParser { + const findEndTag: (schema: Schema, html: string, startIndex: number) => number; +} +interface IconPack { + icons: Record; +} +interface IconManager { + add: (id: string, iconPack: IconPack) => void; + get: (id: string) => IconPack; + has: (id: string) => boolean; +} +interface Resource { + load(id: string, url: string): Promise; + add(id: string, data: any): void; +} +declare type WithSubItems = T[K] extends Array ? (T & T[K][number]) : T; +interface Props { + Mixins?: Array>; + Methods?: string; + Properties?: string; + Statics?: Record; + Defaults?: Record; + init?: (...args: A) => void; +} +declare type ExtendedClass, A extends any[]> = WithSubItems; +interface ExtendedClassConstructor, A extends any[] = any[]> extends Class { + readonly prototype: ExtendedClass; + new (...args: A): ExtendedClass; + [key: string]: T['Statics']; +} +interface Class { + extend, A extends any[] = any[]>(props: T): ExtendedClassConstructor; +} +interface RGB { + r: number; + g: number; + b: number; +} +interface HSV { + h: number; + s: number; + v: number; +} +interface Color { + toRgb(): RGB; + toHsv(): HSV; + toHex(): string; + parse(value: string | RGB | HSV): Color; +} +declare type ColorConstructor = new (value?: string | RGB | HSV) => Color; +declare type DebounceFunc void> = { + (...args: Parameters): void; + stop: () => void; +}; +interface Delay { + requestAnimationFrame(callback: () => void, element?: HTMLElement): void; + setEditorInterval(editor: Editor, callback: () => void, time?: number): number; + setEditorTimeout(editor: Editor, callback: () => void, time?: number): number; + setInterval(callback: () => void, time?: number): number; + setTimeout(callback: () => void, time?: number): number; + clearInterval(id: number): void; + clearTimeout(id: number): void; + debounce any>(callback: T, time?: number): DebounceFunc; + throttle any>(callback: T, time?: number): DebounceFunc; +} +interface JSONUtils { + serialize(obj: any): string; + parse(text: string): any; +} +interface JSONPSettings { + count?: number; + url: string; + callback(json: string): void; +} +interface JSONP { + callbacks: {}; + count: number; + send(settings: JSONPSettings): void; +} +interface JSONRequestSettings { + crossDomain?: boolean; + requestheaders?: Record; + type?: string; + url?: string; + error_scope?: any; + success_scope?: any; + success?(data: any): void; + error?(error: any, xhr: XMLHttpRequest): void; +} +interface JSONRequestArgs extends JSONRequestSettings { + id?: string; + method?: string; + params?: string; +} +interface JSONRequestConstructor { + readonly prototype: JSONRequest; + new (settings?: JSONRequestSettings): JSONRequest; + sendRPC(o: JSONRequestArgs): void; +} +declare class JSONRequest { + static sendRPC(o: JSONRequestArgs): void; + settings: JSONRequestSettings; + count: number; + constructor(settings?: JSONRequestSettings); + send(args: JSONRequestArgs): void; +} +interface KeyboardLikeEvent { + shiftKey: boolean; + ctrlKey: boolean; + altKey: boolean; + metaKey: boolean; +} +interface VK { + BACKSPACE: number; + DELETE: number; + DOWN: number; + ENTER: number; + LEFT: number; + RIGHT: number; + SPACEBAR: number; + TAB: number; + UP: number; + END: number; + HOME: number; + modifierPressed(e: KeyboardLikeEvent): boolean; + metaKeyPressed(e: KeyboardLikeEvent): boolean; +} +interface XHRSettings { + async?: boolean; + content_type?: string; + crossDomain?: boolean; + data?: Document | BodyInit; + requestheaders?: Record; + scope?: any; + type?: string; + url: string; + error_scope?: any; + success_scope?: any; + error?(message: 'TIMED_OUT' | 'GENERAL', xhr: XMLHttpRequest, settings: XHRSettings): void; + success?(text: string, xhr: XMLHttpRequest, settings: XHRSettings): void; +} +interface XHREventMap { + beforeInitialize: { + settings: XHRSettings; + }; + beforeSend: { + xhr: XMLHttpRequest; + settings: XHRSettings; + }; +} +interface XHR extends Observable { + send(settings: XHRSettings): void; +} +interface TinyMCE extends EditorManager { + geom: { + Rect: Rect; + }; + util: { + Promise: PromiseConstructor; + Delay: Delay; + Tools: Tools; + VK: VK; + URI: URIConstructor; + Class: Class; + EventDispatcher: EventDispatcherConstructor; + Observable: Observable; + I18n: I18n; + XHR: XHR; + JSON: JSONUtils; + JSONRequest: JSONRequestConstructor; + JSONP: JSONP; + LocalStorage: Storage; + Color: ColorConstructor; + }; + dom: { + EventUtils: EventUtilsConstructor; + Sizzle: any; + DomQuery: DomQueryConstructor; + TreeWalker: DomTreeWalkerConstructor; + TextSeeker: new (dom: DOMUtils, isBlockBoundary?: (node: Node) => boolean) => TextSeeker; + DOMUtils: new (doc: Document, settings: Partial) => DOMUtils; + ScriptLoader: ScriptLoaderConstructor; + RangeUtils: new (dom: DOMUtils) => RangeUtils; + Serializer: new (settings: DomSerializerSettings, editor?: Editor) => DomSerializer; + ControlSelection: (selection: EditorSelection, editor: Editor) => ControlSelection; + BookmarkManager: (selection: EditorSelection) => BookmarkManager; + Selection: new (dom: DOMUtils, win: Window, serializer: DomSerializer, editor: Editor) => EditorSelection; + StyleSheetLoader: new (documentOrShadowRoot: Document | ShadowRoot, settings: StyleSheetLoaderSettings) => StyleSheetLoader; + Event: EventUtils; + }; + html: { + Styles: new (settings?: StylesSettings, schema?: Schema) => Styles; + Entities: Entities; + Node: AstNodeConstructor; + Schema: new (settings?: SchemaSettings) => Schema; + SaxParser: new (settings?: SaxParserSettings, schema?: Schema) => SaxParser; + DomParser: new (settings?: DomParserSettings, schema?: Schema) => DomParser; + Writer: new (settings?: WriterSettings) => Writer; + Serializer: new (settings?: HtmlSerializerSettings, schema?: Schema) => HtmlSerializer; + }; + AddOnManager: new () => AddOnManager; + Annotator: new (editor: Editor) => Annotator; + Editor: EditorConstructor; + EditorCommands: EditorCommandsConstructor; + EditorManager: EditorManager; + EditorObservable: EditorObservable; + Env: Env; + FocusManager: FocusManager; + Formatter: new (editor: Editor) => Formatter; + NotificationManager: new (editor: Editor) => NotificationManager; + Shortcuts: ShortcutsConstructor; + UndoManager: new (editor: Editor) => UndoManager; + WindowManager: new (editor: Editor) => WindowManager; + DOM: DOMUtils; + ScriptLoader: ScriptLoader; + PluginManager: PluginManager; + ThemeManager: ThemeManager; + IconManager: IconManager; + Resource: Resource; + trim: Tools['trim']; + isArray: Tools['isArray']; + is: Tools['is']; + toArray: Tools['toArray']; + makeMap: Tools['makeMap']; + each: Tools['each']; + map: Tools['map']; + grep: Tools['grep']; + inArray: Tools['inArray']; + extend: Tools['extend']; + create: Tools['create']; + walk: Tools['walk']; + createNS: Tools['createNS']; + resolve: Tools['resolve']; + explode: Tools['explode']; + _addCacheSuffix: Tools['_addCacheSuffix']; + isOpera: boolean; + isWebKit: boolean; + isIE: false | number; + isGecko: boolean; + isMac: boolean; +} +declare const tinymce: TinyMCE; +declare type Formats$1 = { + Formats: Formats; + Format: Format; + ApplyFormat: ApplyFormat; + BlockFormat: BlockFormat; + InlineFormat: InlineFormat; + SelectorFormat: SelectorFormat; + RemoveFormat: RemoveFormat; + RemoveBlockFormat: RemoveBlockFormat; + RemoveInlineFormat: RemoveInlineFormat; + RemoveSelectorFormat: RemoveSelectorFormat; +}; +//export default tinymce; +//export { AddOnManager, Annotator, AstNode, Bookmark, BookmarkManager, Class, Color, ControlSelection, DOMUtils, Delay, DomParser, DomParserSettings, DomQuery, DomSerializer, DomSerializerSettings, DomTreeWalker, Editor, EditorCommands, EditorEvent, EditorManager, EditorModeApi, EditorObservable, EditorSelection, EditorSettings, Entities, Env, EventDispatcher, EventUtils, EventTypes_d as Events, FocusManager, Formats$1 as Formats, Formatter, GeomRect, HtmlSerializer, HtmlSerializerSettings, I18n, IconManager, JSONUtils as JSON, JSONP, JSONRequest, JSONRequestArgs, JSONRequestSettings, NotificationApi, NotificationManager, NotificationSpec, Observable, Plugin, PluginManager, RangeUtils, RawEditorSettings, Rect, Resource, SaxParser, SaxParserSettings, Schema, SchemaSettings, ScriptLoader, Shortcuts, StyleSheetLoader, Styles, TextSeeker, Theme, ThemeManager, TinyMCE, Tools, URI, Ui_d as Ui, UndoManager, VK, WindowManager, Writer, WriterSettings, XHR, XHRSettings }; diff --git a/customizables/constants.php b/customizables/constants.php new file mode 100644 index 0000000..9c55dc0 --- /dev/null +++ b/customizables/constants.php @@ -0,0 +1,3 @@ + { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "BRAND": () => (/* binding */ BRAND), +/* harmony export */ "DIRTY": () => (/* binding */ DIRTY), +/* harmony export */ "EMPTY_PATH": () => (/* binding */ EMPTY_PATH), +/* harmony export */ "INVALID": () => (/* binding */ INVALID), +/* harmony export */ "NEVER": () => (/* binding */ NEVER), +/* harmony export */ "OK": () => (/* binding */ OK), +/* harmony export */ "ParseStatus": () => (/* binding */ ParseStatus), +/* harmony export */ "Schema": () => (/* binding */ ZodType), +/* harmony export */ "ZodAny": () => (/* binding */ ZodAny), +/* harmony export */ "ZodArray": () => (/* binding */ ZodArray), +/* harmony export */ "ZodBigInt": () => (/* binding */ ZodBigInt), +/* harmony export */ "ZodBoolean": () => (/* binding */ ZodBoolean), +/* harmony export */ "ZodBranded": () => (/* binding */ ZodBranded), +/* harmony export */ "ZodCatch": () => (/* binding */ ZodCatch), +/* harmony export */ "ZodDate": () => (/* binding */ ZodDate), +/* harmony export */ "ZodDefault": () => (/* binding */ ZodDefault), +/* harmony export */ "ZodDiscriminatedUnion": () => (/* binding */ ZodDiscriminatedUnion), +/* harmony export */ "ZodEffects": () => (/* binding */ ZodEffects), +/* harmony export */ "ZodEnum": () => (/* binding */ ZodEnum), +/* harmony export */ "ZodError": () => (/* binding */ ZodError), +/* harmony export */ "ZodFirstPartyTypeKind": () => (/* binding */ ZodFirstPartyTypeKind), +/* harmony export */ "ZodFunction": () => (/* binding */ ZodFunction), +/* harmony export */ "ZodIntersection": () => (/* binding */ ZodIntersection), +/* harmony export */ "ZodIssueCode": () => (/* binding */ ZodIssueCode), +/* harmony export */ "ZodLazy": () => (/* binding */ ZodLazy), +/* harmony export */ "ZodLiteral": () => (/* binding */ ZodLiteral), +/* harmony export */ "ZodMap": () => (/* binding */ ZodMap), +/* harmony export */ "ZodNaN": () => (/* binding */ ZodNaN), +/* harmony export */ "ZodNativeEnum": () => (/* binding */ ZodNativeEnum), +/* harmony export */ "ZodNever": () => (/* binding */ ZodNever), +/* harmony export */ "ZodNull": () => (/* binding */ ZodNull), +/* harmony export */ "ZodNullable": () => (/* binding */ ZodNullable), +/* harmony export */ "ZodNumber": () => (/* binding */ ZodNumber), +/* harmony export */ "ZodObject": () => (/* binding */ ZodObject), +/* harmony export */ "ZodOptional": () => (/* binding */ ZodOptional), +/* harmony export */ "ZodParsedType": () => (/* binding */ ZodParsedType), +/* harmony export */ "ZodPipeline": () => (/* binding */ ZodPipeline), +/* harmony export */ "ZodPromise": () => (/* binding */ ZodPromise), +/* harmony export */ "ZodRecord": () => (/* binding */ ZodRecord), +/* harmony export */ "ZodSchema": () => (/* binding */ ZodType), +/* harmony export */ "ZodSet": () => (/* binding */ ZodSet), +/* harmony export */ "ZodString": () => (/* binding */ ZodString), +/* harmony export */ "ZodSymbol": () => (/* binding */ ZodSymbol), +/* harmony export */ "ZodTransformer": () => (/* binding */ ZodEffects), +/* harmony export */ "ZodTuple": () => (/* binding */ ZodTuple), +/* harmony export */ "ZodType": () => (/* binding */ ZodType), +/* harmony export */ "ZodUndefined": () => (/* binding */ ZodUndefined), +/* harmony export */ "ZodUnion": () => (/* binding */ ZodUnion), +/* harmony export */ "ZodUnknown": () => (/* binding */ ZodUnknown), +/* harmony export */ "ZodVoid": () => (/* binding */ ZodVoid), +/* harmony export */ "addIssueToContext": () => (/* binding */ addIssueToContext), +/* harmony export */ "any": () => (/* binding */ anyType), +/* harmony export */ "array": () => (/* binding */ arrayType), +/* harmony export */ "bigint": () => (/* binding */ bigIntType), +/* harmony export */ "boolean": () => (/* binding */ booleanType), +/* harmony export */ "coerce": () => (/* binding */ coerce), +/* harmony export */ "custom": () => (/* binding */ custom), +/* harmony export */ "date": () => (/* binding */ dateType), +/* harmony export */ "default": () => (/* binding */ z), +/* harmony export */ "defaultErrorMap": () => (/* binding */ errorMap), +/* harmony export */ "discriminatedUnion": () => (/* binding */ discriminatedUnionType), +/* harmony export */ "effect": () => (/* binding */ effectsType), +/* harmony export */ "enum": () => (/* binding */ enumType), +/* harmony export */ "function": () => (/* binding */ functionType), +/* harmony export */ "getErrorMap": () => (/* binding */ getErrorMap), +/* harmony export */ "getParsedType": () => (/* binding */ getParsedType), +/* harmony export */ "instanceof": () => (/* binding */ instanceOfType), +/* harmony export */ "intersection": () => (/* binding */ intersectionType), +/* harmony export */ "isAborted": () => (/* binding */ isAborted), +/* harmony export */ "isAsync": () => (/* binding */ isAsync), +/* harmony export */ "isDirty": () => (/* binding */ isDirty), +/* harmony export */ "isValid": () => (/* binding */ isValid), +/* harmony export */ "late": () => (/* binding */ late), +/* harmony export */ "lazy": () => (/* binding */ lazyType), +/* harmony export */ "literal": () => (/* binding */ literalType), +/* harmony export */ "makeIssue": () => (/* binding */ makeIssue), +/* harmony export */ "map": () => (/* binding */ mapType), +/* harmony export */ "nan": () => (/* binding */ nanType), +/* harmony export */ "nativeEnum": () => (/* binding */ nativeEnumType), +/* harmony export */ "never": () => (/* binding */ neverType), +/* harmony export */ "null": () => (/* binding */ nullType), +/* harmony export */ "nullable": () => (/* binding */ nullableType), +/* harmony export */ "number": () => (/* binding */ numberType), +/* harmony export */ "object": () => (/* binding */ objectType), +/* harmony export */ "objectUtil": () => (/* binding */ objectUtil), +/* harmony export */ "oboolean": () => (/* binding */ oboolean), +/* harmony export */ "onumber": () => (/* binding */ onumber), +/* harmony export */ "optional": () => (/* binding */ optionalType), +/* harmony export */ "ostring": () => (/* binding */ ostring), +/* harmony export */ "pipeline": () => (/* binding */ pipelineType), +/* harmony export */ "preprocess": () => (/* binding */ preprocessType), +/* harmony export */ "promise": () => (/* binding */ promiseType), +/* harmony export */ "quotelessJson": () => (/* binding */ quotelessJson), +/* harmony export */ "record": () => (/* binding */ recordType), +/* harmony export */ "set": () => (/* binding */ setType), +/* harmony export */ "setErrorMap": () => (/* binding */ setErrorMap), +/* harmony export */ "strictObject": () => (/* binding */ strictObjectType), +/* harmony export */ "string": () => (/* binding */ stringType), +/* harmony export */ "symbol": () => (/* binding */ symbolType), +/* harmony export */ "transformer": () => (/* binding */ effectsType), +/* harmony export */ "tuple": () => (/* binding */ tupleType), +/* harmony export */ "undefined": () => (/* binding */ undefinedType), +/* harmony export */ "union": () => (/* binding */ unionType), +/* harmony export */ "unknown": () => (/* binding */ unknownType), +/* harmony export */ "util": () => (/* binding */ util), +/* harmony export */ "void": () => (/* binding */ voidType), +/* harmony export */ "z": () => (/* binding */ z) +/* harmony export */ }); +var util; +(function (util) { + util.assertEqual = (val) => val; + function assertIs(_arg) { } + util.assertIs = assertIs; + function assertNever(_x) { + throw new Error(); + } + util.assertNever = assertNever; + util.arrayToEnum = (items) => { + const obj = {}; + for (const item of items) { + obj[item] = item; + } + return obj; + }; + util.getValidEnumValues = (obj) => { + const validKeys = util.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number"); + const filtered = {}; + for (const k of validKeys) { + filtered[k] = obj[k]; + } + return util.objectValues(filtered); + }; + util.objectValues = (obj) => { + return util.objectKeys(obj).map(function (e) { + return obj[e]; + }); + }; + util.objectKeys = typeof Object.keys === "function" // eslint-disable-line ban/ban + ? (obj) => Object.keys(obj) // eslint-disable-line ban/ban + : (object) => { + const keys = []; + for (const key in object) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + keys.push(key); + } + } + return keys; + }; + util.find = (arr, checker) => { + for (const item of arr) { + if (checker(item)) + return item; + } + return undefined; + }; + util.isInteger = typeof Number.isInteger === "function" + ? (val) => Number.isInteger(val) // eslint-disable-line ban/ban + : (val) => typeof val === "number" && isFinite(val) && Math.floor(val) === val; + function joinValues(array, separator = " | ") { + return array + .map((val) => (typeof val === "string" ? `'${val}'` : val)) + .join(separator); + } + util.joinValues = joinValues; + util.jsonStringifyReplacer = (_, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + return value; + }; +})(util || (util = {})); +var objectUtil; +(function (objectUtil) { + objectUtil.mergeShapes = (first, second) => { + return { + ...first, + ...second, // second overwrites first + }; + }; +})(objectUtil || (objectUtil = {})); +const ZodParsedType = util.arrayToEnum([ + "string", + "nan", + "number", + "integer", + "float", + "boolean", + "date", + "bigint", + "symbol", + "function", + "undefined", + "null", + "array", + "object", + "unknown", + "promise", + "void", + "never", + "map", + "set", +]); +const getParsedType = (data) => { + const t = typeof data; + switch (t) { + case "undefined": + return ZodParsedType.undefined; + case "string": + return ZodParsedType.string; + case "number": + return isNaN(data) ? ZodParsedType.nan : ZodParsedType.number; + case "boolean": + return ZodParsedType.boolean; + case "function": + return ZodParsedType.function; + case "bigint": + return ZodParsedType.bigint; + case "symbol": + return ZodParsedType.symbol; + case "object": + if (Array.isArray(data)) { + return ZodParsedType.array; + } + if (data === null) { + return ZodParsedType.null; + } + if (data.then && + typeof data.then === "function" && + data.catch && + typeof data.catch === "function") { + return ZodParsedType.promise; + } + if (typeof Map !== "undefined" && data instanceof Map) { + return ZodParsedType.map; + } + if (typeof Set !== "undefined" && data instanceof Set) { + return ZodParsedType.set; + } + if (typeof Date !== "undefined" && data instanceof Date) { + return ZodParsedType.date; + } + return ZodParsedType.object; + default: + return ZodParsedType.unknown; + } +}; + +const ZodIssueCode = util.arrayToEnum([ + "invalid_type", + "invalid_literal", + "custom", + "invalid_union", + "invalid_union_discriminator", + "invalid_enum_value", + "unrecognized_keys", + "invalid_arguments", + "invalid_return_type", + "invalid_date", + "invalid_string", + "too_small", + "too_big", + "invalid_intersection_types", + "not_multiple_of", + "not_finite", +]); +const quotelessJson = (obj) => { + const json = JSON.stringify(obj, null, 2); + return json.replace(/"([^"]+)":/g, "$1:"); +}; +class ZodError extends Error { + constructor(issues) { + super(); + this.issues = []; + this.addIssue = (sub) => { + this.issues = [...this.issues, sub]; + }; + this.addIssues = (subs = []) => { + this.issues = [...this.issues, ...subs]; + }; + const actualProto = new.target.prototype; + if (Object.setPrototypeOf) { + // eslint-disable-next-line ban/ban + Object.setPrototypeOf(this, actualProto); + } + else { + this.__proto__ = actualProto; + } + this.name = "ZodError"; + this.issues = issues; + } + get errors() { + return this.issues; + } + format(_mapper) { + const mapper = _mapper || + function (issue) { + return issue.message; + }; + const fieldErrors = { _errors: [] }; + const processError = (error) => { + for (const issue of error.issues) { + if (issue.code === "invalid_union") { + issue.unionErrors.map(processError); + } + else if (issue.code === "invalid_return_type") { + processError(issue.returnTypeError); + } + else if (issue.code === "invalid_arguments") { + processError(issue.argumentsError); + } + else if (issue.path.length === 0) { + fieldErrors._errors.push(mapper(issue)); + } + else { + let curr = fieldErrors; + let i = 0; + while (i < issue.path.length) { + const el = issue.path[i]; + const terminal = i === issue.path.length - 1; + if (!terminal) { + curr[el] = curr[el] || { _errors: [] }; + // if (typeof el === "string") { + // curr[el] = curr[el] || { _errors: [] }; + // } else if (typeof el === "number") { + // const errorArray: any = []; + // errorArray._errors = []; + // curr[el] = curr[el] || errorArray; + // } + } + else { + curr[el] = curr[el] || { _errors: [] }; + curr[el]._errors.push(mapper(issue)); + } + curr = curr[el]; + i++; + } + } + } + }; + processError(this); + return fieldErrors; + } + toString() { + return this.message; + } + get message() { + return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2); + } + get isEmpty() { + return this.issues.length === 0; + } + flatten(mapper = (issue) => issue.message) { + const fieldErrors = {}; + const formErrors = []; + for (const sub of this.issues) { + if (sub.path.length > 0) { + fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || []; + fieldErrors[sub.path[0]].push(mapper(sub)); + } + else { + formErrors.push(mapper(sub)); + } + } + return { formErrors, fieldErrors }; + } + get formErrors() { + return this.flatten(); + } +} +ZodError.create = (issues) => { + const error = new ZodError(issues); + return error; +}; + +const errorMap = (issue, _ctx) => { + let message; + switch (issue.code) { + case ZodIssueCode.invalid_type: + if (issue.received === ZodParsedType.undefined) { + message = "Required"; + } + else { + message = `Expected ${issue.expected}, received ${issue.received}`; + } + break; + case ZodIssueCode.invalid_literal: + message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util.jsonStringifyReplacer)}`; + break; + case ZodIssueCode.unrecognized_keys: + message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, ", ")}`; + break; + case ZodIssueCode.invalid_union: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_union_discriminator: + message = `Invalid discriminator value. Expected ${util.joinValues(issue.options)}`; + break; + case ZodIssueCode.invalid_enum_value: + message = `Invalid enum value. Expected ${util.joinValues(issue.options)}, received '${issue.received}'`; + break; + case ZodIssueCode.invalid_arguments: + message = `Invalid function arguments`; + break; + case ZodIssueCode.invalid_return_type: + message = `Invalid function return type`; + break; + case ZodIssueCode.invalid_date: + message = `Invalid date`; + break; + case ZodIssueCode.invalid_string: + if (typeof issue.validation === "object") { + if ("includes" in issue.validation) { + message = `Invalid input: must include "${issue.validation.includes}"`; + if (typeof issue.validation.position === "number") { + message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`; + } + } + else if ("startsWith" in issue.validation) { + message = `Invalid input: must start with "${issue.validation.startsWith}"`; + } + else if ("endsWith" in issue.validation) { + message = `Invalid input: must end with "${issue.validation.endsWith}"`; + } + else { + util.assertNever(issue.validation); + } + } + else if (issue.validation !== "regex") { + message = `Invalid ${issue.validation}`; + } + else { + message = "Invalid"; + } + break; + case ZodIssueCode.too_small: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact + ? `exactly equal to ` + : issue.inclusive + ? `greater than or equal to ` + : `greater than `}${issue.minimum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact + ? `exactly equal to ` + : issue.inclusive + ? `greater than or equal to ` + : `greater than `}${new Date(Number(issue.minimum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.too_big: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `less than or equal to` + : `less than`} ${issue.maximum}`; + else if (issue.type === "bigint") + message = `BigInt must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `less than or equal to` + : `less than`} ${issue.maximum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `smaller than or equal to` + : `smaller than`} ${new Date(Number(issue.maximum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.custom: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_intersection_types: + message = `Intersection results could not be merged`; + break; + case ZodIssueCode.not_multiple_of: + message = `Number must be a multiple of ${issue.multipleOf}`; + break; + case ZodIssueCode.not_finite: + message = "Number must be finite"; + break; + default: + message = _ctx.defaultError; + util.assertNever(issue); + } + return { message }; +}; + +let overrideErrorMap = errorMap; +function setErrorMap(map) { + overrideErrorMap = map; +} +function getErrorMap() { + return overrideErrorMap; +} + +const makeIssue = (params) => { + const { data, path, errorMaps, issueData } = params; + const fullPath = [...path, ...(issueData.path || [])]; + const fullIssue = { + ...issueData, + path: fullPath, + }; + let errorMessage = ""; + const maps = errorMaps + .filter((m) => !!m) + .slice() + .reverse(); + for (const map of maps) { + errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message; + } + return { + ...issueData, + path: fullPath, + message: issueData.message || errorMessage, + }; +}; +const EMPTY_PATH = []; +function addIssueToContext(ctx, issueData) { + const issue = makeIssue({ + issueData: issueData, + data: ctx.data, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + getErrorMap(), + errorMap, // then global default map + ].filter((x) => !!x), + }); + ctx.common.issues.push(issue); +} +class ParseStatus { + constructor() { + this.value = "valid"; + } + dirty() { + if (this.value === "valid") + this.value = "dirty"; + } + abort() { + if (this.value !== "aborted") + this.value = "aborted"; + } + static mergeArray(status, results) { + const arrayValue = []; + for (const s of results) { + if (s.status === "aborted") + return INVALID; + if (s.status === "dirty") + status.dirty(); + arrayValue.push(s.value); + } + return { status: status.value, value: arrayValue }; + } + static async mergeObjectAsync(status, pairs) { + const syncPairs = []; + for (const pair of pairs) { + syncPairs.push({ + key: await pair.key, + value: await pair.value, + }); + } + return ParseStatus.mergeObjectSync(status, syncPairs); + } + static mergeObjectSync(status, pairs) { + const finalObject = {}; + for (const pair of pairs) { + const { key, value } = pair; + if (key.status === "aborted") + return INVALID; + if (value.status === "aborted") + return INVALID; + if (key.status === "dirty") + status.dirty(); + if (value.status === "dirty") + status.dirty(); + if (typeof value.value !== "undefined" || pair.alwaysSet) { + finalObject[key.value] = value.value; + } + } + return { status: status.value, value: finalObject }; + } +} +const INVALID = Object.freeze({ + status: "aborted", +}); +const DIRTY = (value) => ({ status: "dirty", value }); +const OK = (value) => ({ status: "valid", value }); +const isAborted = (x) => x.status === "aborted"; +const isDirty = (x) => x.status === "dirty"; +const isValid = (x) => x.status === "valid"; +const isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise; + +var errorUtil; +(function (errorUtil) { + errorUtil.errToObj = (message) => typeof message === "string" ? { message } : message || {}; + errorUtil.toString = (message) => typeof message === "string" ? message : message === null || message === void 0 ? void 0 : message.message; +})(errorUtil || (errorUtil = {})); + +class ParseInputLazyPath { + constructor(parent, value, path, key) { + this._cachedPath = []; + this.parent = parent; + this.data = value; + this._path = path; + this._key = key; + } + get path() { + if (!this._cachedPath.length) { + if (this._key instanceof Array) { + this._cachedPath.push(...this._path, ...this._key); + } + else { + this._cachedPath.push(...this._path, this._key); + } + } + return this._cachedPath; + } +} +const handleResult = (ctx, result) => { + if (isValid(result)) { + return { success: true, data: result.value }; + } + else { + if (!ctx.common.issues.length) { + throw new Error("Validation failed but no issues detected."); + } + return { + success: false, + get error() { + if (this._error) + return this._error; + const error = new ZodError(ctx.common.issues); + this._error = error; + return this._error; + }, + }; + } +}; +function processCreateParams(params) { + if (!params) + return {}; + const { errorMap, invalid_type_error, required_error, description } = params; + if (errorMap && (invalid_type_error || required_error)) { + throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`); + } + if (errorMap) + return { errorMap: errorMap, description }; + const customMap = (iss, ctx) => { + if (iss.code !== "invalid_type") + return { message: ctx.defaultError }; + if (typeof ctx.data === "undefined") { + return { message: required_error !== null && required_error !== void 0 ? required_error : ctx.defaultError }; + } + return { message: invalid_type_error !== null && invalid_type_error !== void 0 ? invalid_type_error : ctx.defaultError }; + }; + return { errorMap: customMap, description }; +} +class ZodType { + constructor(def) { + /** Alias of safeParseAsync */ + this.spa = this.safeParseAsync; + this._def = def; + this.parse = this.parse.bind(this); + this.safeParse = this.safeParse.bind(this); + this.parseAsync = this.parseAsync.bind(this); + this.safeParseAsync = this.safeParseAsync.bind(this); + this.spa = this.spa.bind(this); + this.refine = this.refine.bind(this); + this.refinement = this.refinement.bind(this); + this.superRefine = this.superRefine.bind(this); + this.optional = this.optional.bind(this); + this.nullable = this.nullable.bind(this); + this.nullish = this.nullish.bind(this); + this.array = this.array.bind(this); + this.promise = this.promise.bind(this); + this.or = this.or.bind(this); + this.and = this.and.bind(this); + this.transform = this.transform.bind(this); + this.brand = this.brand.bind(this); + this.default = this.default.bind(this); + this.catch = this.catch.bind(this); + this.describe = this.describe.bind(this); + this.pipe = this.pipe.bind(this); + this.isNullable = this.isNullable.bind(this); + this.isOptional = this.isOptional.bind(this); + } + get description() { + return this._def.description; + } + _getType(input) { + return getParsedType(input.data); + } + _getOrReturnCtx(input, ctx) { + return (ctx || { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent, + }); + } + _processInputParams(input) { + return { + status: new ParseStatus(), + ctx: { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent, + }, + }; + } + _parseSync(input) { + const result = this._parse(input); + if (isAsync(result)) { + throw new Error("Synchronous parse encountered promise."); + } + return result; + } + _parseAsync(input) { + const result = this._parse(input); + return Promise.resolve(result); + } + parse(data, params) { + const result = this.safeParse(data, params); + if (result.success) + return result.data; + throw result.error; + } + safeParse(data, params) { + var _a; + const ctx = { + common: { + issues: [], + async: (_a = params === null || params === void 0 ? void 0 : params.async) !== null && _a !== void 0 ? _a : false, + contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap, + }, + path: (params === null || params === void 0 ? void 0 : params.path) || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data), + }; + const result = this._parseSync({ data, path: ctx.path, parent: ctx }); + return handleResult(ctx, result); + } + async parseAsync(data, params) { + const result = await this.safeParseAsync(data, params); + if (result.success) + return result.data; + throw result.error; + } + async safeParseAsync(data, params) { + const ctx = { + common: { + issues: [], + contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap, + async: true, + }, + path: (params === null || params === void 0 ? void 0 : params.path) || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data), + }; + const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx }); + const result = await (isAsync(maybeAsyncResult) + ? maybeAsyncResult + : Promise.resolve(maybeAsyncResult)); + return handleResult(ctx, result); + } + refine(check, message) { + const getIssueProperties = (val) => { + if (typeof message === "string" || typeof message === "undefined") { + return { message }; + } + else if (typeof message === "function") { + return message(val); + } + else { + return message; + } + }; + return this._refinement((val, ctx) => { + const result = check(val); + const setError = () => ctx.addIssue({ + code: ZodIssueCode.custom, + ...getIssueProperties(val), + }); + if (typeof Promise !== "undefined" && result instanceof Promise) { + return result.then((data) => { + if (!data) { + setError(); + return false; + } + else { + return true; + } + }); + } + if (!result) { + setError(); + return false; + } + else { + return true; + } + }); + } + refinement(check, refinementData) { + return this._refinement((val, ctx) => { + if (!check(val)) { + ctx.addIssue(typeof refinementData === "function" + ? refinementData(val, ctx) + : refinementData); + return false; + } + else { + return true; + } + }); + } + _refinement(refinement) { + return new ZodEffects({ + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "refinement", refinement }, + }); + } + superRefine(refinement) { + return this._refinement(refinement); + } + optional() { + return ZodOptional.create(this, this._def); + } + nullable() { + return ZodNullable.create(this, this._def); + } + nullish() { + return this.nullable().optional(); + } + array() { + return ZodArray.create(this, this._def); + } + promise() { + return ZodPromise.create(this, this._def); + } + or(option) { + return ZodUnion.create([this, option], this._def); + } + and(incoming) { + return ZodIntersection.create(this, incoming, this._def); + } + transform(transform) { + return new ZodEffects({ + ...processCreateParams(this._def), + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "transform", transform }, + }); + } + default(def) { + const defaultValueFunc = typeof def === "function" ? def : () => def; + return new ZodDefault({ + ...processCreateParams(this._def), + innerType: this, + defaultValue: defaultValueFunc, + typeName: ZodFirstPartyTypeKind.ZodDefault, + }); + } + brand() { + return new ZodBranded({ + typeName: ZodFirstPartyTypeKind.ZodBranded, + type: this, + ...processCreateParams(this._def), + }); + } + catch(def) { + const catchValueFunc = typeof def === "function" ? def : () => def; + return new ZodCatch({ + ...processCreateParams(this._def), + innerType: this, + catchValue: catchValueFunc, + typeName: ZodFirstPartyTypeKind.ZodCatch, + }); + } + describe(description) { + const This = this.constructor; + return new This({ + ...this._def, + description, + }); + } + pipe(target) { + return ZodPipeline.create(this, target); + } + isOptional() { + return this.safeParse(undefined).success; + } + isNullable() { + return this.safeParse(null).success; + } +} +const cuidRegex = /^c[^\s-]{8,}$/i; +const cuid2Regex = /^[a-z][a-z0-9]*$/; +const ulidRegex = /[0-9A-HJKMNP-TV-Z]{26}/; +const uuidRegex = /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i; +// from https://stackoverflow.com/a/46181/1550155 +// old version: too slow, didn't support unicode +// const emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; +//old email regex +// const emailRegex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@((?!-)([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{1,})[^-<>()[\].,;:\s@"]$/i; +// eslint-disable-next-line +const emailRegex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\])|(\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\.[A-Za-z]{2,})+))$/; +// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression +const emojiRegex = /^(\p{Extended_Pictographic}|\p{Emoji_Component})+$/u; +const ipv4Regex = /^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/; +const ipv6Regex = /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/; +// Adapted from https://stackoverflow.com/a/3143231 +const datetimeRegex = (args) => { + if (args.precision) { + if (args.offset) { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{${args.precision}}(([+-]\\d{2}(:?\\d{2})?)|Z)$`); + } + else { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{${args.precision}}Z$`); + } + } + else if (args.precision === 0) { + if (args.offset) { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(([+-]\\d{2}(:?\\d{2})?)|Z)$`); + } + else { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$`); + } + } + else { + if (args.offset) { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(([+-]\\d{2}(:?\\d{2})?)|Z)$`); + } + else { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$`); + } + } +}; +function isValidIP(ip, version) { + if ((version === "v4" || !version) && ipv4Regex.test(ip)) { + return true; + } + if ((version === "v6" || !version) && ipv6Regex.test(ip)) { + return true; + } + return false; +} +class ZodString extends ZodType { + constructor() { + super(...arguments); + this._regex = (regex, validation, message) => this.refinement((data) => regex.test(data), { + validation, + code: ZodIssueCode.invalid_string, + ...errorUtil.errToObj(message), + }); + /** + * @deprecated Use z.string().min(1) instead. + * @see {@link ZodString.min} + */ + this.nonempty = (message) => this.min(1, errorUtil.errToObj(message)); + this.trim = () => new ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "trim" }], + }); + this.toLowerCase = () => new ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toLowerCase" }], + }); + this.toUpperCase = () => new ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toUpperCase" }], + }); + } + _parse(input) { + if (this._def.coerce) { + input.data = String(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.string) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.string, + received: ctx.parsedType, + } + // + ); + return INVALID; + } + const status = new ParseStatus(); + let ctx = undefined; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.length < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "max") { + if (input.data.length > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "length") { + const tooBig = input.data.length > check.value; + const tooSmall = input.data.length < check.value; + if (tooBig || tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + if (tooBig) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message, + }); + } + else if (tooSmall) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message, + }); + } + status.dirty(); + } + } + else if (check.kind === "email") { + if (!emailRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "email", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "emoji") { + if (!emojiRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "emoji", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "uuid") { + if (!uuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "uuid", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "cuid") { + if (!cuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "cuid2") { + if (!cuid2Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid2", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "ulid") { + if (!ulidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ulid", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "url") { + try { + new URL(input.data); + } + catch (_a) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "url", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "regex") { + check.regex.lastIndex = 0; + const testResult = check.regex.test(input.data); + if (!testResult) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "regex", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "trim") { + input.data = input.data.trim(); + } + else if (check.kind === "includes") { + if (!input.data.includes(check.value, check.position)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { includes: check.value, position: check.position }, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "toLowerCase") { + input.data = input.data.toLowerCase(); + } + else if (check.kind === "toUpperCase") { + input.data = input.data.toUpperCase(); + } + else if (check.kind === "startsWith") { + if (!input.data.startsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { startsWith: check.value }, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "endsWith") { + if (!input.data.endsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { endsWith: check.value }, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "datetime") { + const regex = datetimeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "datetime", + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "ip") { + if (!isValidIP(input.data, check.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ip", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + _addCheck(check) { + return new ZodString({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + email(message) { + return this._addCheck({ kind: "email", ...errorUtil.errToObj(message) }); + } + url(message) { + return this._addCheck({ kind: "url", ...errorUtil.errToObj(message) }); + } + emoji(message) { + return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message) }); + } + uuid(message) { + return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message) }); + } + cuid(message) { + return this._addCheck({ kind: "cuid", ...errorUtil.errToObj(message) }); + } + cuid2(message) { + return this._addCheck({ kind: "cuid2", ...errorUtil.errToObj(message) }); + } + ulid(message) { + return this._addCheck({ kind: "ulid", ...errorUtil.errToObj(message) }); + } + ip(options) { + return this._addCheck({ kind: "ip", ...errorUtil.errToObj(options) }); + } + datetime(options) { + var _a; + if (typeof options === "string") { + return this._addCheck({ + kind: "datetime", + precision: null, + offset: false, + message: options, + }); + } + return this._addCheck({ + kind: "datetime", + precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === "undefined" ? null : options === null || options === void 0 ? void 0 : options.precision, + offset: (_a = options === null || options === void 0 ? void 0 : options.offset) !== null && _a !== void 0 ? _a : false, + ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message), + }); + } + regex(regex, message) { + return this._addCheck({ + kind: "regex", + regex: regex, + ...errorUtil.errToObj(message), + }); + } + includes(value, options) { + return this._addCheck({ + kind: "includes", + value: value, + position: options === null || options === void 0 ? void 0 : options.position, + ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message), + }); + } + startsWith(value, message) { + return this._addCheck({ + kind: "startsWith", + value: value, + ...errorUtil.errToObj(message), + }); + } + endsWith(value, message) { + return this._addCheck({ + kind: "endsWith", + value: value, + ...errorUtil.errToObj(message), + }); + } + min(minLength, message) { + return this._addCheck({ + kind: "min", + value: minLength, + ...errorUtil.errToObj(message), + }); + } + max(maxLength, message) { + return this._addCheck({ + kind: "max", + value: maxLength, + ...errorUtil.errToObj(message), + }); + } + length(len, message) { + return this._addCheck({ + kind: "length", + value: len, + ...errorUtil.errToObj(message), + }); + } + get isDatetime() { + return !!this._def.checks.find((ch) => ch.kind === "datetime"); + } + get isEmail() { + return !!this._def.checks.find((ch) => ch.kind === "email"); + } + get isURL() { + return !!this._def.checks.find((ch) => ch.kind === "url"); + } + get isEmoji() { + return !!this._def.checks.find((ch) => ch.kind === "emoji"); + } + get isUUID() { + return !!this._def.checks.find((ch) => ch.kind === "uuid"); + } + get isCUID() { + return !!this._def.checks.find((ch) => ch.kind === "cuid"); + } + get isCUID2() { + return !!this._def.checks.find((ch) => ch.kind === "cuid2"); + } + get isULID() { + return !!this._def.checks.find((ch) => ch.kind === "ulid"); + } + get isIP() { + return !!this._def.checks.find((ch) => ch.kind === "ip"); + } + get minLength() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxLength() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +} +ZodString.create = (params) => { + var _a; + return new ZodString({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodString, + coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false, + ...processCreateParams(params), + }); +}; +// https://stackoverflow.com/questions/3966484/why-does-modulus-operator-return-fractional-number-in-javascript/31711034#31711034 +function floatSafeRemainder(val, step) { + const valDecCount = (val.toString().split(".")[1] || "").length; + const stepDecCount = (step.toString().split(".")[1] || "").length; + const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount; + const valInt = parseInt(val.toFixed(decCount).replace(".", "")); + const stepInt = parseInt(step.toFixed(decCount).replace(".", "")); + return (valInt % stepInt) / Math.pow(10, decCount); +} +class ZodNumber extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + this.step = this.multipleOf; + } + _parse(input) { + if (this._def.coerce) { + input.data = Number(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.number) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.number, + received: ctx.parsedType, + }); + return INVALID; + } + let ctx = undefined; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "int") { + if (!util.isInteger(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: "integer", + received: "float", + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "min") { + const tooSmall = check.inclusive + ? input.data < check.value + : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "max") { + const tooBig = check.inclusive + ? input.data > check.value + : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "multipleOf") { + if (floatSafeRemainder(input.data, check.value) !== 0) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "finite") { + if (!Number.isFinite(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_finite, + message: check.message, + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new ZodNumber({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message), + }, + ], + }); + } + _addCheck(check) { + return new ZodNumber({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + int(message) { + return this._addCheck({ + kind: "int", + message: errorUtil.toString(message), + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: false, + message: errorUtil.toString(message), + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: false, + message: errorUtil.toString(message), + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: true, + message: errorUtil.toString(message), + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: true, + message: errorUtil.toString(message), + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value: value, + message: errorUtil.toString(message), + }); + } + finite(message) { + return this._addCheck({ + kind: "finite", + message: errorUtil.toString(message), + }); + } + safe(message) { + return this._addCheck({ + kind: "min", + inclusive: true, + value: Number.MIN_SAFE_INTEGER, + message: errorUtil.toString(message), + })._addCheck({ + kind: "max", + inclusive: true, + value: Number.MAX_SAFE_INTEGER, + message: errorUtil.toString(message), + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } + get isInt() { + return !!this._def.checks.find((ch) => ch.kind === "int" || + (ch.kind === "multipleOf" && util.isInteger(ch.value))); + } + get isFinite() { + let max = null, min = null; + for (const ch of this._def.checks) { + if (ch.kind === "finite" || + ch.kind === "int" || + ch.kind === "multipleOf") { + return true; + } + else if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + else if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return Number.isFinite(min) && Number.isFinite(max); + } +} +ZodNumber.create = (params) => { + return new ZodNumber({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodNumber, + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + ...processCreateParams(params), + }); +}; +class ZodBigInt extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + } + _parse(input) { + if (this._def.coerce) { + input.data = BigInt(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.bigint) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.bigint, + received: ctx.parsedType, + }); + return INVALID; + } + let ctx = undefined; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "min") { + const tooSmall = check.inclusive + ? input.data < check.value + : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + type: "bigint", + minimum: check.value, + inclusive: check.inclusive, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "max") { + const tooBig = check.inclusive + ? input.data > check.value + : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + type: "bigint", + maximum: check.value, + inclusive: check.inclusive, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "multipleOf") { + if (input.data % check.value !== BigInt(0)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message, + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new ZodBigInt({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message), + }, + ], + }); + } + _addCheck(check) { + return new ZodBigInt({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message), + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message), + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message), + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message), + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message), + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +} +ZodBigInt.create = (params) => { + var _a; + return new ZodBigInt({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodBigInt, + coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false, + ...processCreateParams(params), + }); +}; +class ZodBoolean extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = Boolean(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.boolean) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.boolean, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodBoolean.create = (params) => { + return new ZodBoolean({ + typeName: ZodFirstPartyTypeKind.ZodBoolean, + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + ...processCreateParams(params), + }); +}; +class ZodDate extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = new Date(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.date) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.date, + received: ctx.parsedType, + }); + return INVALID; + } + if (isNaN(input.data.getTime())) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_date, + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = undefined; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.getTime() < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + message: check.message, + inclusive: true, + exact: false, + minimum: check.value, + type: "date", + }); + status.dirty(); + } + } + else if (check.kind === "max") { + if (input.data.getTime() > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + message: check.message, + inclusive: true, + exact: false, + maximum: check.value, + type: "date", + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { + status: status.value, + value: new Date(input.data.getTime()), + }; + } + _addCheck(check) { + return new ZodDate({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + min(minDate, message) { + return this._addCheck({ + kind: "min", + value: minDate.getTime(), + message: errorUtil.toString(message), + }); + } + max(maxDate, message) { + return this._addCheck({ + kind: "max", + value: maxDate.getTime(), + message: errorUtil.toString(message), + }); + } + get minDate() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min != null ? new Date(min) : null; + } + get maxDate() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max != null ? new Date(max) : null; + } +} +ZodDate.create = (params) => { + return new ZodDate({ + checks: [], + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + typeName: ZodFirstPartyTypeKind.ZodDate, + ...processCreateParams(params), + }); +}; +class ZodSymbol extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.symbol) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.symbol, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodSymbol.create = (params) => { + return new ZodSymbol({ + typeName: ZodFirstPartyTypeKind.ZodSymbol, + ...processCreateParams(params), + }); +}; +class ZodUndefined extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.undefined, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodUndefined.create = (params) => { + return new ZodUndefined({ + typeName: ZodFirstPartyTypeKind.ZodUndefined, + ...processCreateParams(params), + }); +}; +class ZodNull extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.null) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.null, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodNull.create = (params) => { + return new ZodNull({ + typeName: ZodFirstPartyTypeKind.ZodNull, + ...processCreateParams(params), + }); +}; +class ZodAny extends ZodType { + constructor() { + super(...arguments); + // to prevent instances of other classes from extending ZodAny. this causes issues with catchall in ZodObject. + this._any = true; + } + _parse(input) { + return OK(input.data); + } +} +ZodAny.create = (params) => { + return new ZodAny({ + typeName: ZodFirstPartyTypeKind.ZodAny, + ...processCreateParams(params), + }); +}; +class ZodUnknown extends ZodType { + constructor() { + super(...arguments); + // required + this._unknown = true; + } + _parse(input) { + return OK(input.data); + } +} +ZodUnknown.create = (params) => { + return new ZodUnknown({ + typeName: ZodFirstPartyTypeKind.ZodUnknown, + ...processCreateParams(params), + }); +}; +class ZodNever extends ZodType { + _parse(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.never, + received: ctx.parsedType, + }); + return INVALID; + } +} +ZodNever.create = (params) => { + return new ZodNever({ + typeName: ZodFirstPartyTypeKind.ZodNever, + ...processCreateParams(params), + }); +}; +class ZodVoid extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.void, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodVoid.create = (params) => { + return new ZodVoid({ + typeName: ZodFirstPartyTypeKind.ZodVoid, + ...processCreateParams(params), + }); +}; +class ZodArray extends ZodType { + _parse(input) { + const { ctx, status } = this._processInputParams(input); + const def = this._def; + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType, + }); + return INVALID; + } + if (def.exactLength !== null) { + const tooBig = ctx.data.length > def.exactLength.value; + const tooSmall = ctx.data.length < def.exactLength.value; + if (tooBig || tooSmall) { + addIssueToContext(ctx, { + code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small, + minimum: (tooSmall ? def.exactLength.value : undefined), + maximum: (tooBig ? def.exactLength.value : undefined), + type: "array", + inclusive: true, + exact: true, + message: def.exactLength.message, + }); + status.dirty(); + } + } + if (def.minLength !== null) { + if (ctx.data.length < def.minLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.minLength.message, + }); + status.dirty(); + } + } + if (def.maxLength !== null) { + if (ctx.data.length > def.maxLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.maxLength.message, + }); + status.dirty(); + } + } + if (ctx.common.async) { + return Promise.all([...ctx.data].map((item, i) => { + return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + })).then((result) => { + return ParseStatus.mergeArray(status, result); + }); + } + const result = [...ctx.data].map((item, i) => { + return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + }); + return ParseStatus.mergeArray(status, result); + } + get element() { + return this._def.type; + } + min(minLength, message) { + return new ZodArray({ + ...this._def, + minLength: { value: minLength, message: errorUtil.toString(message) }, + }); + } + max(maxLength, message) { + return new ZodArray({ + ...this._def, + maxLength: { value: maxLength, message: errorUtil.toString(message) }, + }); + } + length(len, message) { + return new ZodArray({ + ...this._def, + exactLength: { value: len, message: errorUtil.toString(message) }, + }); + } + nonempty(message) { + return this.min(1, message); + } +} +ZodArray.create = (schema, params) => { + return new ZodArray({ + type: schema, + minLength: null, + maxLength: null, + exactLength: null, + typeName: ZodFirstPartyTypeKind.ZodArray, + ...processCreateParams(params), + }); +}; +function deepPartialify(schema) { + if (schema instanceof ZodObject) { + const newShape = {}; + for (const key in schema.shape) { + const fieldSchema = schema.shape[key]; + newShape[key] = ZodOptional.create(deepPartialify(fieldSchema)); + } + return new ZodObject({ + ...schema._def, + shape: () => newShape, + }); + } + else if (schema instanceof ZodArray) { + return new ZodArray({ + ...schema._def, + type: deepPartialify(schema.element), + }); + } + else if (schema instanceof ZodOptional) { + return ZodOptional.create(deepPartialify(schema.unwrap())); + } + else if (schema instanceof ZodNullable) { + return ZodNullable.create(deepPartialify(schema.unwrap())); + } + else if (schema instanceof ZodTuple) { + return ZodTuple.create(schema.items.map((item) => deepPartialify(item))); + } + else { + return schema; + } +} +class ZodObject extends ZodType { + constructor() { + super(...arguments); + this._cached = null; + /** + * @deprecated In most cases, this is no longer needed - unknown properties are now silently stripped. + * If you want to pass through unknown properties, use `.passthrough()` instead. + */ + this.nonstrict = this.passthrough; + // extend< + // Augmentation extends ZodRawShape, + // NewOutput extends util.flatten<{ + // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation + // ? Augmentation[k]["_output"] + // : k extends keyof Output + // ? Output[k] + // : never; + // }>, + // NewInput extends util.flatten<{ + // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation + // ? Augmentation[k]["_input"] + // : k extends keyof Input + // ? Input[k] + // : never; + // }> + // >( + // augmentation: Augmentation + // ): ZodObject< + // extendShape, + // UnknownKeys, + // Catchall, + // NewOutput, + // NewInput + // > { + // return new ZodObject({ + // ...this._def, + // shape: () => ({ + // ...this._def.shape(), + // ...augmentation, + // }), + // }) as any; + // } + /** + * @deprecated Use `.extend` instead + * */ + this.augment = this.extend; + } + _getCached() { + if (this._cached !== null) + return this._cached; + const shape = this._def.shape(); + const keys = util.objectKeys(shape); + return (this._cached = { shape, keys }); + } + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.object) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType, + }); + return INVALID; + } + const { status, ctx } = this._processInputParams(input); + const { shape, keys: shapeKeys } = this._getCached(); + const extraKeys = []; + if (!(this._def.catchall instanceof ZodNever && + this._def.unknownKeys === "strip")) { + for (const key in ctx.data) { + if (!shapeKeys.includes(key)) { + extraKeys.push(key); + } + } + } + const pairs = []; + for (const key of shapeKeys) { + const keyValidator = shape[key]; + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)), + alwaysSet: key in ctx.data, + }); + } + if (this._def.catchall instanceof ZodNever) { + const unknownKeys = this._def.unknownKeys; + if (unknownKeys === "passthrough") { + for (const key of extraKeys) { + pairs.push({ + key: { status: "valid", value: key }, + value: { status: "valid", value: ctx.data[key] }, + }); + } + } + else if (unknownKeys === "strict") { + if (extraKeys.length > 0) { + addIssueToContext(ctx, { + code: ZodIssueCode.unrecognized_keys, + keys: extraKeys, + }); + status.dirty(); + } + } + else if (unknownKeys === "strip") ; + else { + throw new Error(`Internal ZodObject error: invalid unknownKeys value.`); + } + } + else { + // run catchall validation + const catchall = this._def.catchall; + for (const key of extraKeys) { + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: catchall._parse(new ParseInputLazyPath(ctx, value, ctx.path, key) //, ctx.child(key), value, getParsedType(value) + ), + alwaysSet: key in ctx.data, + }); + } + } + if (ctx.common.async) { + return Promise.resolve() + .then(async () => { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + syncPairs.push({ + key, + value: await pair.value, + alwaysSet: pair.alwaysSet, + }); + } + return syncPairs; + }) + .then((syncPairs) => { + return ParseStatus.mergeObjectSync(status, syncPairs); + }); + } + else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get shape() { + return this._def.shape(); + } + strict(message) { + errorUtil.errToObj; + return new ZodObject({ + ...this._def, + unknownKeys: "strict", + ...(message !== undefined + ? { + errorMap: (issue, ctx) => { + var _a, _b, _c, _d; + const defaultError = (_c = (_b = (_a = this._def).errorMap) === null || _b === void 0 ? void 0 : _b.call(_a, issue, ctx).message) !== null && _c !== void 0 ? _c : ctx.defaultError; + if (issue.code === "unrecognized_keys") + return { + message: (_d = errorUtil.errToObj(message).message) !== null && _d !== void 0 ? _d : defaultError, + }; + return { + message: defaultError, + }; + }, + } + : {}), + }); + } + strip() { + return new ZodObject({ + ...this._def, + unknownKeys: "strip", + }); + } + passthrough() { + return new ZodObject({ + ...this._def, + unknownKeys: "passthrough", + }); + } + // const AugmentFactory = + // (def: Def) => + // ( + // augmentation: Augmentation + // ): ZodObject< + // extendShape, Augmentation>, + // Def["unknownKeys"], + // Def["catchall"] + // > => { + // return new ZodObject({ + // ...def, + // shape: () => ({ + // ...def.shape(), + // ...augmentation, + // }), + // }) as any; + // }; + extend(augmentation) { + return new ZodObject({ + ...this._def, + shape: () => ({ + ...this._def.shape(), + ...augmentation, + }), + }); + } + /** + * Prior to zod@1.0.12 there was a bug in the + * inferred type of merged objects. Please + * upgrade if you are experiencing issues. + */ + merge(merging) { + const merged = new ZodObject({ + unknownKeys: merging._def.unknownKeys, + catchall: merging._def.catchall, + shape: () => ({ + ...this._def.shape(), + ...merging._def.shape(), + }), + typeName: ZodFirstPartyTypeKind.ZodObject, + }); + return merged; + } + // merge< + // Incoming extends AnyZodObject, + // Augmentation extends Incoming["shape"], + // NewOutput extends { + // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation + // ? Augmentation[k]["_output"] + // : k extends keyof Output + // ? Output[k] + // : never; + // }, + // NewInput extends { + // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation + // ? Augmentation[k]["_input"] + // : k extends keyof Input + // ? Input[k] + // : never; + // } + // >( + // merging: Incoming + // ): ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"], + // NewOutput, + // NewInput + // > { + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + setKey(key, schema) { + return this.augment({ [key]: schema }); + } + // merge( + // merging: Incoming + // ): //ZodObject = (merging) => { + // ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"] + // > { + // // const mergedShape = objectUtil.mergeShapes( + // // this._def.shape(), + // // merging._def.shape() + // // ); + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + catchall(index) { + return new ZodObject({ + ...this._def, + catchall: index, + }); + } + pick(mask) { + const shape = {}; + util.objectKeys(mask).forEach((key) => { + if (mask[key] && this.shape[key]) { + shape[key] = this.shape[key]; + } + }); + return new ZodObject({ + ...this._def, + shape: () => shape, + }); + } + omit(mask) { + const shape = {}; + util.objectKeys(this.shape).forEach((key) => { + if (!mask[key]) { + shape[key] = this.shape[key]; + } + }); + return new ZodObject({ + ...this._def, + shape: () => shape, + }); + } + /** + * @deprecated + */ + deepPartial() { + return deepPartialify(this); + } + partial(mask) { + const newShape = {}; + util.objectKeys(this.shape).forEach((key) => { + const fieldSchema = this.shape[key]; + if (mask && !mask[key]) { + newShape[key] = fieldSchema; + } + else { + newShape[key] = fieldSchema.optional(); + } + }); + return new ZodObject({ + ...this._def, + shape: () => newShape, + }); + } + required(mask) { + const newShape = {}; + util.objectKeys(this.shape).forEach((key) => { + if (mask && !mask[key]) { + newShape[key] = this.shape[key]; + } + else { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional) { + newField = newField._def.innerType; + } + newShape[key] = newField; + } + }); + return new ZodObject({ + ...this._def, + shape: () => newShape, + }); + } + keyof() { + return createZodEnum(util.objectKeys(this.shape)); + } +} +ZodObject.create = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params), + }); +}; +ZodObject.strictCreate = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strict", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params), + }); +}; +ZodObject.lazycreate = (shape, params) => { + return new ZodObject({ + shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params), + }); +}; +class ZodUnion extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const options = this._def.options; + function handleResults(results) { + // return first issue-free validation if it exists + for (const result of results) { + if (result.result.status === "valid") { + return result.result; + } + } + for (const result of results) { + if (result.result.status === "dirty") { + // add issues from dirty option + ctx.common.issues.push(...result.ctx.common.issues); + return result.result; + } + } + // return invalid + const unionErrors = results.map((result) => new ZodError(result.ctx.common.issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors, + }); + return INVALID; + } + if (ctx.common.async) { + return Promise.all(options.map(async (option) => { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [], + }, + parent: null, + }; + return { + result: await option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: childCtx, + }), + ctx: childCtx, + }; + })).then(handleResults); + } + else { + let dirty = undefined; + const issues = []; + for (const option of options) { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [], + }, + parent: null, + }; + const result = option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: childCtx, + }); + if (result.status === "valid") { + return result; + } + else if (result.status === "dirty" && !dirty) { + dirty = { result, ctx: childCtx }; + } + if (childCtx.common.issues.length) { + issues.push(childCtx.common.issues); + } + } + if (dirty) { + ctx.common.issues.push(...dirty.ctx.common.issues); + return dirty.result; + } + const unionErrors = issues.map((issues) => new ZodError(issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors, + }); + return INVALID; + } + } + get options() { + return this._def.options; + } +} +ZodUnion.create = (types, params) => { + return new ZodUnion({ + options: types, + typeName: ZodFirstPartyTypeKind.ZodUnion, + ...processCreateParams(params), + }); +}; +///////////////////////////////////////////////////// +///////////////////////////////////////////////////// +////////// ////////// +////////// ZodDiscriminatedUnion ////////// +////////// ////////// +///////////////////////////////////////////////////// +///////////////////////////////////////////////////// +const getDiscriminator = (type) => { + if (type instanceof ZodLazy) { + return getDiscriminator(type.schema); + } + else if (type instanceof ZodEffects) { + return getDiscriminator(type.innerType()); + } + else if (type instanceof ZodLiteral) { + return [type.value]; + } + else if (type instanceof ZodEnum) { + return type.options; + } + else if (type instanceof ZodNativeEnum) { + // eslint-disable-next-line ban/ban + return Object.keys(type.enum); + } + else if (type instanceof ZodDefault) { + return getDiscriminator(type._def.innerType); + } + else if (type instanceof ZodUndefined) { + return [undefined]; + } + else if (type instanceof ZodNull) { + return [null]; + } + else { + return null; + } +}; +class ZodDiscriminatedUnion extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType, + }); + return INVALID; + } + const discriminator = this.discriminator; + const discriminatorValue = ctx.data[discriminator]; + const option = this.optionsMap.get(discriminatorValue); + if (!option) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union_discriminator, + options: Array.from(this.optionsMap.keys()), + path: [discriminator], + }); + return INVALID; + } + if (ctx.common.async) { + return option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + } + else { + return option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + } + } + get discriminator() { + return this._def.discriminator; + } + get options() { + return this._def.options; + } + get optionsMap() { + return this._def.optionsMap; + } + /** + * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor. + * However, it only allows a union of objects, all of which need to share a discriminator property. This property must + * have a different value for each object in the union. + * @param discriminator the name of the discriminator property + * @param types an array of object schemas + * @param params + */ + static create(discriminator, options, params) { + // Get all the valid discriminator values + const optionsMap = new Map(); + // try { + for (const type of options) { + const discriminatorValues = getDiscriminator(type.shape[discriminator]); + if (!discriminatorValues) { + throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`); + } + for (const value of discriminatorValues) { + if (optionsMap.has(value)) { + throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`); + } + optionsMap.set(value, type); + } + } + return new ZodDiscriminatedUnion({ + typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion, + discriminator, + options, + optionsMap, + ...processCreateParams(params), + }); + } +} +function mergeValues(a, b) { + const aType = getParsedType(a); + const bType = getParsedType(b); + if (a === b) { + return { valid: true, data: a }; + } + else if (aType === ZodParsedType.object && bType === ZodParsedType.object) { + const bKeys = util.objectKeys(b); + const sharedKeys = util + .objectKeys(a) + .filter((key) => bKeys.indexOf(key) !== -1); + const newObj = { ...a, ...b }; + for (const key of sharedKeys) { + const sharedValue = mergeValues(a[key], b[key]); + if (!sharedValue.valid) { + return { valid: false }; + } + newObj[key] = sharedValue.data; + } + return { valid: true, data: newObj }; + } + else if (aType === ZodParsedType.array && bType === ZodParsedType.array) { + if (a.length !== b.length) { + return { valid: false }; + } + const newArray = []; + for (let index = 0; index < a.length; index++) { + const itemA = a[index]; + const itemB = b[index]; + const sharedValue = mergeValues(itemA, itemB); + if (!sharedValue.valid) { + return { valid: false }; + } + newArray.push(sharedValue.data); + } + return { valid: true, data: newArray }; + } + else if (aType === ZodParsedType.date && + bType === ZodParsedType.date && + +a === +b) { + return { valid: true, data: a }; + } + else { + return { valid: false }; + } +} +class ZodIntersection extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const handleParsed = (parsedLeft, parsedRight) => { + if (isAborted(parsedLeft) || isAborted(parsedRight)) { + return INVALID; + } + const merged = mergeValues(parsedLeft.value, parsedRight.value); + if (!merged.valid) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_intersection_types, + }); + return INVALID; + } + if (isDirty(parsedLeft) || isDirty(parsedRight)) { + status.dirty(); + } + return { status: status.value, value: merged.data }; + }; + if (ctx.common.async) { + return Promise.all([ + this._def.left._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }), + this._def.right._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }), + ]).then(([left, right]) => handleParsed(left, right)); + } + else { + return handleParsed(this._def.left._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }), this._def.right._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + })); + } + } +} +ZodIntersection.create = (left, right, params) => { + return new ZodIntersection({ + left: left, + right: right, + typeName: ZodFirstPartyTypeKind.ZodIntersection, + ...processCreateParams(params), + }); +}; +class ZodTuple extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType, + }); + return INVALID; + } + if (ctx.data.length < this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: this._def.items.length, + inclusive: true, + exact: false, + type: "array", + }); + return INVALID; + } + const rest = this._def.rest; + if (!rest && ctx.data.length > this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: this._def.items.length, + inclusive: true, + exact: false, + type: "array", + }); + status.dirty(); + } + const items = [...ctx.data] + .map((item, itemIndex) => { + const schema = this._def.items[itemIndex] || this._def.rest; + if (!schema) + return null; + return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex)); + }) + .filter((x) => !!x); // filter nulls + if (ctx.common.async) { + return Promise.all(items).then((results) => { + return ParseStatus.mergeArray(status, results); + }); + } + else { + return ParseStatus.mergeArray(status, items); + } + } + get items() { + return this._def.items; + } + rest(rest) { + return new ZodTuple({ + ...this._def, + rest, + }); + } +} +ZodTuple.create = (schemas, params) => { + if (!Array.isArray(schemas)) { + throw new Error("You must pass an array of schemas to z.tuple([ ... ])"); + } + return new ZodTuple({ + items: schemas, + typeName: ZodFirstPartyTypeKind.ZodTuple, + rest: null, + ...processCreateParams(params), + }); +}; +class ZodRecord extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType, + }); + return INVALID; + } + const pairs = []; + const keyType = this._def.keyType; + const valueType = this._def.valueType; + for (const key in ctx.data) { + pairs.push({ + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)), + value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)), + }); + } + if (ctx.common.async) { + return ParseStatus.mergeObjectAsync(status, pairs); + } + else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get element() { + return this._def.valueType; + } + static create(first, second, third) { + if (second instanceof ZodType) { + return new ZodRecord({ + keyType: first, + valueType: second, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(third), + }); + } + return new ZodRecord({ + keyType: ZodString.create(), + valueType: first, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(second), + }); + } +} +class ZodMap extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.map) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.map, + received: ctx.parsedType, + }); + return INVALID; + } + const keyType = this._def.keyType; + const valueType = this._def.valueType; + const pairs = [...ctx.data.entries()].map(([key, value], index) => { + return { + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, "key"])), + value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"])), + }; + }); + if (ctx.common.async) { + const finalMap = new Map(); + return Promise.resolve().then(async () => { + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + }); + } + else { + const finalMap = new Map(); + for (const pair of pairs) { + const key = pair.key; + const value = pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + } + } +} +ZodMap.create = (keyType, valueType, params) => { + return new ZodMap({ + valueType, + keyType, + typeName: ZodFirstPartyTypeKind.ZodMap, + ...processCreateParams(params), + }); +}; +class ZodSet extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.set) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.set, + received: ctx.parsedType, + }); + return INVALID; + } + const def = this._def; + if (def.minSize !== null) { + if (ctx.data.size < def.minSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.minSize.message, + }); + status.dirty(); + } + } + if (def.maxSize !== null) { + if (ctx.data.size > def.maxSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.maxSize.message, + }); + status.dirty(); + } + } + const valueType = this._def.valueType; + function finalizeSet(elements) { + const parsedSet = new Set(); + for (const element of elements) { + if (element.status === "aborted") + return INVALID; + if (element.status === "dirty") + status.dirty(); + parsedSet.add(element.value); + } + return { status: status.value, value: parsedSet }; + } + const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i))); + if (ctx.common.async) { + return Promise.all(elements).then((elements) => finalizeSet(elements)); + } + else { + return finalizeSet(elements); + } + } + min(minSize, message) { + return new ZodSet({ + ...this._def, + minSize: { value: minSize, message: errorUtil.toString(message) }, + }); + } + max(maxSize, message) { + return new ZodSet({ + ...this._def, + maxSize: { value: maxSize, message: errorUtil.toString(message) }, + }); + } + size(size, message) { + return this.min(size, message).max(size, message); + } + nonempty(message) { + return this.min(1, message); + } +} +ZodSet.create = (valueType, params) => { + return new ZodSet({ + valueType, + minSize: null, + maxSize: null, + typeName: ZodFirstPartyTypeKind.ZodSet, + ...processCreateParams(params), + }); +}; +class ZodFunction extends ZodType { + constructor() { + super(...arguments); + this.validate = this.implement; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.function) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.function, + received: ctx.parsedType, + }); + return INVALID; + } + function makeArgsIssue(args, error) { + return makeIssue({ + data: args, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + getErrorMap(), + errorMap, + ].filter((x) => !!x), + issueData: { + code: ZodIssueCode.invalid_arguments, + argumentsError: error, + }, + }); + } + function makeReturnsIssue(returns, error) { + return makeIssue({ + data: returns, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + getErrorMap(), + errorMap, + ].filter((x) => !!x), + issueData: { + code: ZodIssueCode.invalid_return_type, + returnTypeError: error, + }, + }); + } + const params = { errorMap: ctx.common.contextualErrorMap }; + const fn = ctx.data; + if (this._def.returns instanceof ZodPromise) { + return OK(async (...args) => { + const error = new ZodError([]); + const parsedArgs = await this._def.args + .parseAsync(args, params) + .catch((e) => { + error.addIssue(makeArgsIssue(args, e)); + throw error; + }); + const result = await fn(...parsedArgs); + const parsedReturns = await this._def.returns._def.type + .parseAsync(result, params) + .catch((e) => { + error.addIssue(makeReturnsIssue(result, e)); + throw error; + }); + return parsedReturns; + }); + } + else { + return OK((...args) => { + const parsedArgs = this._def.args.safeParse(args, params); + if (!parsedArgs.success) { + throw new ZodError([makeArgsIssue(args, parsedArgs.error)]); + } + const result = fn(...parsedArgs.data); + const parsedReturns = this._def.returns.safeParse(result, params); + if (!parsedReturns.success) { + throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]); + } + return parsedReturns.data; + }); + } + } + parameters() { + return this._def.args; + } + returnType() { + return this._def.returns; + } + args(...items) { + return new ZodFunction({ + ...this._def, + args: ZodTuple.create(items).rest(ZodUnknown.create()), + }); + } + returns(returnType) { + return new ZodFunction({ + ...this._def, + returns: returnType, + }); + } + implement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + strictImplement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + static create(args, returns, params) { + return new ZodFunction({ + args: (args + ? args + : ZodTuple.create([]).rest(ZodUnknown.create())), + returns: returns || ZodUnknown.create(), + typeName: ZodFirstPartyTypeKind.ZodFunction, + ...processCreateParams(params), + }); + } +} +class ZodLazy extends ZodType { + get schema() { + return this._def.getter(); + } + _parse(input) { + const { ctx } = this._processInputParams(input); + const lazySchema = this._def.getter(); + return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx }); + } +} +ZodLazy.create = (getter, params) => { + return new ZodLazy({ + getter: getter, + typeName: ZodFirstPartyTypeKind.ZodLazy, + ...processCreateParams(params), + }); +}; +class ZodLiteral extends ZodType { + _parse(input) { + if (input.data !== this._def.value) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_literal, + expected: this._def.value, + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } + get value() { + return this._def.value; + } +} +ZodLiteral.create = (value, params) => { + return new ZodLiteral({ + value: value, + typeName: ZodFirstPartyTypeKind.ZodLiteral, + ...processCreateParams(params), + }); +}; +function createZodEnum(values, params) { + return new ZodEnum({ + values: values, + typeName: ZodFirstPartyTypeKind.ZodEnum, + ...processCreateParams(params), + }); +} +class ZodEnum extends ZodType { + _parse(input) { + if (typeof input.data !== "string") { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + expected: util.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type, + }); + return INVALID; + } + if (this._def.values.indexOf(input.data) === -1) { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues, + }); + return INVALID; + } + return OK(input.data); + } + get options() { + return this._def.values; + } + get enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Values() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + extract(values) { + return ZodEnum.create(values); + } + exclude(values) { + return ZodEnum.create(this.options.filter((opt) => !values.includes(opt))); + } +} +ZodEnum.create = createZodEnum; +class ZodNativeEnum extends ZodType { + _parse(input) { + const nativeEnumValues = util.getValidEnumValues(this._def.values); + const ctx = this._getOrReturnCtx(input); + if (ctx.parsedType !== ZodParsedType.string && + ctx.parsedType !== ZodParsedType.number) { + const expectedValues = util.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + expected: util.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type, + }); + return INVALID; + } + if (nativeEnumValues.indexOf(input.data) === -1) { + const expectedValues = util.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues, + }); + return INVALID; + } + return OK(input.data); + } + get enum() { + return this._def.values; + } +} +ZodNativeEnum.create = (values, params) => { + return new ZodNativeEnum({ + values: values, + typeName: ZodFirstPartyTypeKind.ZodNativeEnum, + ...processCreateParams(params), + }); +}; +class ZodPromise extends ZodType { + unwrap() { + return this._def.type; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.promise && + ctx.common.async === false) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.promise, + received: ctx.parsedType, + }); + return INVALID; + } + const promisified = ctx.parsedType === ZodParsedType.promise + ? ctx.data + : Promise.resolve(ctx.data); + return OK(promisified.then((data) => { + return this._def.type.parseAsync(data, { + path: ctx.path, + errorMap: ctx.common.contextualErrorMap, + }); + })); + } +} +ZodPromise.create = (schema, params) => { + return new ZodPromise({ + type: schema, + typeName: ZodFirstPartyTypeKind.ZodPromise, + ...processCreateParams(params), + }); +}; +class ZodEffects extends ZodType { + innerType() { + return this._def.schema; + } + sourceType() { + return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects + ? this._def.schema.sourceType() + : this._def.schema; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const effect = this._def.effect || null; + if (effect.type === "preprocess") { + const processed = effect.transform(ctx.data); + if (ctx.common.async) { + return Promise.resolve(processed).then((processed) => { + return this._def.schema._parseAsync({ + data: processed, + path: ctx.path, + parent: ctx, + }); + }); + } + else { + return this._def.schema._parseSync({ + data: processed, + path: ctx.path, + parent: ctx, + }); + } + } + const checkCtx = { + addIssue: (arg) => { + addIssueToContext(ctx, arg); + if (arg.fatal) { + status.abort(); + } + else { + status.dirty(); + } + }, + get path() { + return ctx.path; + }, + }; + checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx); + if (effect.type === "refinement") { + const executeRefinement = (acc + // effect: RefinementEffect + ) => { + const result = effect.refinement(acc, checkCtx); + if (ctx.common.async) { + return Promise.resolve(result); + } + if (result instanceof Promise) { + throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead."); + } + return acc; + }; + if (ctx.common.async === false) { + const inner = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + // return value is ignored + executeRefinement(inner.value); + return { status: status.value, value: inner.value }; + } + else { + return this._def.schema + ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }) + .then((inner) => { + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + return executeRefinement(inner.value).then(() => { + return { status: status.value, value: inner.value }; + }); + }); + } + } + if (effect.type === "transform") { + if (ctx.common.async === false) { + const base = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (!isValid(base)) + return base; + const result = effect.transform(base.value, checkCtx); + if (result instanceof Promise) { + throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`); + } + return { status: status.value, value: result }; + } + else { + return this._def.schema + ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }) + .then((base) => { + if (!isValid(base)) + return base; + return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ status: status.value, value: result })); + }); + } + } + util.assertNever(effect); + } +} +ZodEffects.create = (schema, effect, params) => { + return new ZodEffects({ + schema, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect, + ...processCreateParams(params), + }); +}; +ZodEffects.createWithPreprocess = (preprocess, schema, params) => { + return new ZodEffects({ + schema, + effect: { type: "preprocess", transform: preprocess }, + typeName: ZodFirstPartyTypeKind.ZodEffects, + ...processCreateParams(params), + }); +}; +class ZodOptional extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.undefined) { + return OK(undefined); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +} +ZodOptional.create = (type, params) => { + return new ZodOptional({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodOptional, + ...processCreateParams(params), + }); +}; +class ZodNullable extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.null) { + return OK(null); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +} +ZodNullable.create = (type, params) => { + return new ZodNullable({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodNullable, + ...processCreateParams(params), + }); +}; +class ZodDefault extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + let data = ctx.data; + if (ctx.parsedType === ZodParsedType.undefined) { + data = this._def.defaultValue(); + } + return this._def.innerType._parse({ + data, + path: ctx.path, + parent: ctx, + }); + } + removeDefault() { + return this._def.innerType; + } +} +ZodDefault.create = (type, params) => { + return new ZodDefault({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodDefault, + defaultValue: typeof params.default === "function" + ? params.default + : () => params.default, + ...processCreateParams(params), + }); +}; +class ZodCatch extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + // newCtx is used to not collect issues from inner types in ctx + const newCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [], + }, + }; + const result = this._def.innerType._parse({ + data: newCtx.data, + path: newCtx.path, + parent: { + ...newCtx, + }, + }); + if (isAsync(result)) { + return result.then((result) => { + return { + status: "valid", + value: result.status === "valid" + ? result.value + : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data, + }), + }; + }); + } + else { + return { + status: "valid", + value: result.status === "valid" + ? result.value + : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data, + }), + }; + } + } + removeCatch() { + return this._def.innerType; + } +} +ZodCatch.create = (type, params) => { + return new ZodCatch({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodCatch, + catchValue: typeof params.catch === "function" ? params.catch : () => params.catch, + ...processCreateParams(params), + }); +}; +class ZodNaN extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.nan) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.nan, + received: ctx.parsedType, + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } +} +ZodNaN.create = (params) => { + return new ZodNaN({ + typeName: ZodFirstPartyTypeKind.ZodNaN, + ...processCreateParams(params), + }); +}; +const BRAND = Symbol("zod_brand"); +class ZodBranded extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const data = ctx.data; + return this._def.type._parse({ + data, + path: ctx.path, + parent: ctx, + }); + } + unwrap() { + return this._def.type; + } +} +class ZodPipeline extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.common.async) { + const handleAsync = async () => { + const inResult = await this._def.in._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return DIRTY(inResult.value); + } + else { + return this._def.out._parseAsync({ + data: inResult.value, + path: ctx.path, + parent: ctx, + }); + } + }; + return handleAsync(); + } + else { + const inResult = this._def.in._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return { + status: "dirty", + value: inResult.value, + }; + } + else { + return this._def.out._parseSync({ + data: inResult.value, + path: ctx.path, + parent: ctx, + }); + } + } + } + static create(a, b) { + return new ZodPipeline({ + in: a, + out: b, + typeName: ZodFirstPartyTypeKind.ZodPipeline, + }); + } +} +const custom = (check, params = {}, +/* + * @deprecated + * + * Pass `fatal` into the params object instead: + * + * ```ts + * z.string().custom((val) => val.length > 5, { fatal: false }) + * ``` + * + */ +fatal) => { + if (check) + return ZodAny.create().superRefine((data, ctx) => { + var _a, _b; + if (!check(data)) { + const p = typeof params === "function" + ? params(data) + : typeof params === "string" + ? { message: params } + : params; + const _fatal = (_b = (_a = p.fatal) !== null && _a !== void 0 ? _a : fatal) !== null && _b !== void 0 ? _b : true; + const p2 = typeof p === "string" ? { message: p } : p; + ctx.addIssue({ code: "custom", ...p2, fatal: _fatal }); + } + }); + return ZodAny.create(); +}; +const late = { + object: ZodObject.lazycreate, +}; +var ZodFirstPartyTypeKind; +(function (ZodFirstPartyTypeKind) { + ZodFirstPartyTypeKind["ZodString"] = "ZodString"; + ZodFirstPartyTypeKind["ZodNumber"] = "ZodNumber"; + ZodFirstPartyTypeKind["ZodNaN"] = "ZodNaN"; + ZodFirstPartyTypeKind["ZodBigInt"] = "ZodBigInt"; + ZodFirstPartyTypeKind["ZodBoolean"] = "ZodBoolean"; + ZodFirstPartyTypeKind["ZodDate"] = "ZodDate"; + ZodFirstPartyTypeKind["ZodSymbol"] = "ZodSymbol"; + ZodFirstPartyTypeKind["ZodUndefined"] = "ZodUndefined"; + ZodFirstPartyTypeKind["ZodNull"] = "ZodNull"; + ZodFirstPartyTypeKind["ZodAny"] = "ZodAny"; + ZodFirstPartyTypeKind["ZodUnknown"] = "ZodUnknown"; + ZodFirstPartyTypeKind["ZodNever"] = "ZodNever"; + ZodFirstPartyTypeKind["ZodVoid"] = "ZodVoid"; + ZodFirstPartyTypeKind["ZodArray"] = "ZodArray"; + ZodFirstPartyTypeKind["ZodObject"] = "ZodObject"; + ZodFirstPartyTypeKind["ZodUnion"] = "ZodUnion"; + ZodFirstPartyTypeKind["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion"; + ZodFirstPartyTypeKind["ZodIntersection"] = "ZodIntersection"; + ZodFirstPartyTypeKind["ZodTuple"] = "ZodTuple"; + ZodFirstPartyTypeKind["ZodRecord"] = "ZodRecord"; + ZodFirstPartyTypeKind["ZodMap"] = "ZodMap"; + ZodFirstPartyTypeKind["ZodSet"] = "ZodSet"; + ZodFirstPartyTypeKind["ZodFunction"] = "ZodFunction"; + ZodFirstPartyTypeKind["ZodLazy"] = "ZodLazy"; + ZodFirstPartyTypeKind["ZodLiteral"] = "ZodLiteral"; + ZodFirstPartyTypeKind["ZodEnum"] = "ZodEnum"; + ZodFirstPartyTypeKind["ZodEffects"] = "ZodEffects"; + ZodFirstPartyTypeKind["ZodNativeEnum"] = "ZodNativeEnum"; + ZodFirstPartyTypeKind["ZodOptional"] = "ZodOptional"; + ZodFirstPartyTypeKind["ZodNullable"] = "ZodNullable"; + ZodFirstPartyTypeKind["ZodDefault"] = "ZodDefault"; + ZodFirstPartyTypeKind["ZodCatch"] = "ZodCatch"; + ZodFirstPartyTypeKind["ZodPromise"] = "ZodPromise"; + ZodFirstPartyTypeKind["ZodBranded"] = "ZodBranded"; + ZodFirstPartyTypeKind["ZodPipeline"] = "ZodPipeline"; +})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {})); +const instanceOfType = ( +// const instanceOfType = any>( +cls, params = { + message: `Input not instance of ${cls.name}`, +}) => custom((data) => data instanceof cls, params); +const stringType = ZodString.create; +const numberType = ZodNumber.create; +const nanType = ZodNaN.create; +const bigIntType = ZodBigInt.create; +const booleanType = ZodBoolean.create; +const dateType = ZodDate.create; +const symbolType = ZodSymbol.create; +const undefinedType = ZodUndefined.create; +const nullType = ZodNull.create; +const anyType = ZodAny.create; +const unknownType = ZodUnknown.create; +const neverType = ZodNever.create; +const voidType = ZodVoid.create; +const arrayType = ZodArray.create; +const objectType = ZodObject.create; +const strictObjectType = ZodObject.strictCreate; +const unionType = ZodUnion.create; +const discriminatedUnionType = ZodDiscriminatedUnion.create; +const intersectionType = ZodIntersection.create; +const tupleType = ZodTuple.create; +const recordType = ZodRecord.create; +const mapType = ZodMap.create; +const setType = ZodSet.create; +const functionType = ZodFunction.create; +const lazyType = ZodLazy.create; +const literalType = ZodLiteral.create; +const enumType = ZodEnum.create; +const nativeEnumType = ZodNativeEnum.create; +const promiseType = ZodPromise.create; +const effectsType = ZodEffects.create; +const optionalType = ZodOptional.create; +const nullableType = ZodNullable.create; +const preprocessType = ZodEffects.createWithPreprocess; +const pipelineType = ZodPipeline.create; +const ostring = () => stringType().optional(); +const onumber = () => numberType().optional(); +const oboolean = () => booleanType().optional(); +const coerce = { + string: ((arg) => ZodString.create({ ...arg, coerce: true })), + number: ((arg) => ZodNumber.create({ ...arg, coerce: true })), + boolean: ((arg) => ZodBoolean.create({ + ...arg, + coerce: true, + })), + bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })), + date: ((arg) => ZodDate.create({ ...arg, coerce: true })), +}; +const NEVER = INVALID; + +var z = /*#__PURE__*/Object.freeze({ + __proto__: null, + defaultErrorMap: errorMap, + setErrorMap: setErrorMap, + getErrorMap: getErrorMap, + makeIssue: makeIssue, + EMPTY_PATH: EMPTY_PATH, + addIssueToContext: addIssueToContext, + ParseStatus: ParseStatus, + INVALID: INVALID, + DIRTY: DIRTY, + OK: OK, + isAborted: isAborted, + isDirty: isDirty, + isValid: isValid, + isAsync: isAsync, + get util () { return util; }, + get objectUtil () { return objectUtil; }, + ZodParsedType: ZodParsedType, + getParsedType: getParsedType, + ZodType: ZodType, + ZodString: ZodString, + ZodNumber: ZodNumber, + ZodBigInt: ZodBigInt, + ZodBoolean: ZodBoolean, + ZodDate: ZodDate, + ZodSymbol: ZodSymbol, + ZodUndefined: ZodUndefined, + ZodNull: ZodNull, + ZodAny: ZodAny, + ZodUnknown: ZodUnknown, + ZodNever: ZodNever, + ZodVoid: ZodVoid, + ZodArray: ZodArray, + ZodObject: ZodObject, + ZodUnion: ZodUnion, + ZodDiscriminatedUnion: ZodDiscriminatedUnion, + ZodIntersection: ZodIntersection, + ZodTuple: ZodTuple, + ZodRecord: ZodRecord, + ZodMap: ZodMap, + ZodSet: ZodSet, + ZodFunction: ZodFunction, + ZodLazy: ZodLazy, + ZodLiteral: ZodLiteral, + ZodEnum: ZodEnum, + ZodNativeEnum: ZodNativeEnum, + ZodPromise: ZodPromise, + ZodEffects: ZodEffects, + ZodTransformer: ZodEffects, + ZodOptional: ZodOptional, + ZodNullable: ZodNullable, + ZodDefault: ZodDefault, + ZodCatch: ZodCatch, + ZodNaN: ZodNaN, + BRAND: BRAND, + ZodBranded: ZodBranded, + ZodPipeline: ZodPipeline, + custom: custom, + Schema: ZodType, + ZodSchema: ZodType, + late: late, + get ZodFirstPartyTypeKind () { return ZodFirstPartyTypeKind; }, + coerce: coerce, + any: anyType, + array: arrayType, + bigint: bigIntType, + boolean: booleanType, + date: dateType, + discriminatedUnion: discriminatedUnionType, + effect: effectsType, + 'enum': enumType, + 'function': functionType, + 'instanceof': instanceOfType, + intersection: intersectionType, + lazy: lazyType, + literal: literalType, + map: mapType, + nan: nanType, + nativeEnum: nativeEnumType, + never: neverType, + 'null': nullType, + nullable: nullableType, + number: numberType, + object: objectType, + oboolean: oboolean, + onumber: onumber, + optional: optionalType, + ostring: ostring, + pipeline: pipelineType, + preprocess: preprocessType, + promise: promiseType, + record: recordType, + set: setType, + strictObject: strictObjectType, + string: stringType, + symbol: symbolType, + transformer: effectsType, + tuple: tupleType, + 'undefined': undefinedType, + union: unionType, + unknown: unknownType, + 'void': voidType, + NEVER: NEVER, + ZodIssueCode: ZodIssueCode, + quotelessJson: quotelessJson, + ZodError: ZodError +}); + + + + +/***/ }), + +/***/ "./extras/modules/admin-customizer/admin-customizer.ts": +/*!*************************************************************!*\ + !*** ./extras/modules/admin-customizer/admin-customizer.ts ***! + \*************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AmeAdminCustomizer": () => (/* binding */ AmeAdminCustomizer) +/* harmony export */ }); +/* harmony import */ var _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../pro-customizables/assets/customizable.js */ "./extras/pro-customizables/assets/customizable.js"); +/* harmony import */ var _pro_customizables_ko_components_ame_components_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../pro-customizables/ko-components/ame-components.js */ "./extras/pro-customizables/ko-components/ame-components.js"); +/* harmony import */ var _ko_components_ame_ac_structure_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./ko-components/ame-ac-structure.js */ "./extras/modules/admin-customizer/ko-components/ame-ac-structure.js"); +/* harmony import */ var _ko_components_ame_ac_section_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./ko-components/ame-ac-section.js */ "./extras/modules/admin-customizer/ko-components/ame-ac-section.js"); +/* harmony import */ var _ko_components_ame_ac_section_link_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./ko-components/ame-ac-section-link.js */ "./extras/modules/admin-customizer/ko-components/ame-ac-section-link.js"); +/* harmony import */ var _ko_components_ame_ac_control_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./ko-components/ame-ac-control.js */ "./extras/modules/admin-customizer/ko-components/ame-ac-control.js"); +/* harmony import */ var _ko_components_ame_ac_control_group_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ko-components/ame-ac-control-group.js */ "./extras/modules/admin-customizer/ko-components/ame-ac-control-group.js"); +/* harmony import */ var _ko_components_ame_ac_content_section_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./ko-components/ame-ac-content-section.js */ "./extras/modules/admin-customizer/ko-components/ame-ac-content-section.js"); +/* harmony import */ var _admin_customizer_base_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./admin-customizer-base.js */ "./extras/modules/admin-customizer/admin-customizer-base.js"); +/* harmony import */ var _ko_components_ame_ac_separator_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./ko-components/ame-ac-separator.js */ "./extras/modules/admin-customizer/ko-components/ame-ac-separator.js"); +/* harmony import */ var _ko_components_ame_ac_validation_errors_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./ko-components/ame-ac-validation-errors.js */ "./extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js"); +/* harmony import */ var _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../../zod/lib/index.js */ "./extras/zod/lib/index.js"); + +/// +/// +/// + + + + + + + + + + + + +var AmeAdminCustomizer; +(function (AmeAdminCustomizer) { + var SettingCollection = _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_0__.AmeCustomizable.SettingCollection; + var unserializeUiElement = _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_0__.AmeCustomizable.unserializeUiElement; + var unserializeSetting = _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_0__.AmeCustomizable.unserializeSetting; + const $ = jQuery; + const _ = wsAmeLodash; + (0,_pro_customizables_ko_components_ame_components_js__WEBPACK_IMPORTED_MODULE_1__.registerBaseComponents)(); + ko.components.register('ame-ac-structure', _ko_components_ame_ac_structure_js__WEBPACK_IMPORTED_MODULE_2__["default"]); + ko.components.register('ame-ac-section', _ko_components_ame_ac_section_js__WEBPACK_IMPORTED_MODULE_3__["default"]); + ko.components.register('ame-ac-section-link', _ko_components_ame_ac_section_link_js__WEBPACK_IMPORTED_MODULE_4__["default"]); + ko.components.register('ame-ac-content-section', _ko_components_ame_ac_content_section_js__WEBPACK_IMPORTED_MODULE_7__["default"]); + ko.components.register('ame-ac-control-group', _ko_components_ame_ac_control_group_js__WEBPACK_IMPORTED_MODULE_6__["default"]); + ko.components.register('ame-ac-control', _ko_components_ame_ac_control_js__WEBPACK_IMPORTED_MODULE_5__["default"]); + ko.components.register('ame-ac-separator', _ko_components_ame_ac_separator_js__WEBPACK_IMPORTED_MODULE_9__["default"]); + ko.components.register('ame-ac-validation-errors', _ko_components_ame_ac_validation_errors_js__WEBPACK_IMPORTED_MODULE_10__["default"]); + const reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); + let prefersReducedMotion = reducedMotionQuery && reducedMotionQuery.matches; + reducedMotionQuery.addEventListener('change', () => { + prefersReducedMotion = reducedMotionQuery.matches; + }); + class CustomizerSettingsCollection extends SettingCollection { + constructor(ajaxUrl, saveChangesetNonce, trashChangesetNonce, changesetName, changesetItemCount = 0, changesetStatus = null) { + super(); + this.ajaxUrl = ajaxUrl; + this.saveChangesetNonce = saveChangesetNonce; + this.trashChangesetNonce = trashChangesetNonce; + /** + * Settings that have changed since the last save attempt. + */ + this.pendingSettings = {}; + /** + * Settings that in the process of being sent to the server to be saved. + * They might not be saved yet. + */ + this.sentSettings = {}; + this.currentChangesetRequest = null; + this.saveTriggerTimeoutId = null; + this.exclusiveOperation = ko.observable(false); + const self = this; + this.currentChangeset = ko.observable(new Changeset(changesetName, changesetItemCount, changesetStatus)); + this.changesetName = ko.pureComputed(() => { + var _a; + return ((_a = self.currentChangeset()) === null || _a === void 0 ? void 0 : _a.name()) || ''; + }); + //Automatically save the changeset when any settings change. + const totalChangeCount = ko.pureComputed(() => { + const changeset = self.currentChangeset(); + return (changeset ? changeset.currentSessionChanges() : 0); + }); + totalChangeCount.subscribe(_.debounce((counter) => { + if (counter > 0) { + self.queueChangesetUpdate(); + } + }, 3000, { leading: true, trailing: true })); + this.isExclusiveOperationInProgress = ko.pureComputed(() => { + return self.exclusiveOperation(); + }); + //Keep track of unsaved changes and changesets. + this.addChangeListener((setting) => { + this.pendingSettings[setting.id] = setting; + let changeset = this.currentChangeset(); + //If the current changeset cannot be modified, create a new one + //for the changed setting(s). + if (!(changeset === null || changeset === void 0 ? void 0 : changeset.canBeModified())) { + changeset = new Changeset(); + this.currentChangeset(changeset); + } + //Track the number of changes in the current session. + changeset.currentSessionChanges(changeset.currentSessionChanges() + 1); + }); + } + queueChangesetUpdate(delay = 0) { + if (delay > 0) { + if (this.saveTriggerTimeoutId !== null) { + //Replace the existing timeout with a new one. + clearTimeout(this.saveTriggerTimeoutId); + } + this.saveTriggerTimeoutId = setTimeout(() => { + this.saveTriggerTimeoutId = null; + this.queueChangesetUpdate(0); + }, delay); + return; + } + if (this.saveTriggerTimeoutId !== null) { + return; //Another timeout is already waiting. + } + if (this.currentChangesetRequest !== null) { + //There's an in-progress request, so wait until it's done. + this.currentChangesetRequest.always(() => { + //Wait a bit to avoid hammering the server. + this.queueChangesetUpdate(1000); + }); + return; + } + this.saveChangeset(); + } + saveChangeset(status = null) { + var _a; + //Do nothing if there are no changes. + if (_.isEmpty(this.pendingSettings) && (status === null)) { + return $.Deferred().reject(new Error('There are no changes to save.')).promise(); + } + if (this.isExclusiveOperationInProgress()) { + return $.Deferred().reject(new Error('Another exclusive changeset operation is in progress.')).promise(); + } + let isExclusiveRequest = (status === 'publish') || (status === 'trash'); + if (isExclusiveRequest) { + this.exclusiveOperation(true); + } + const savedChangeset = this.currentChangeset(); + //Keep a local copy of the settings in case something changes instance + //properties while the request is in progress (should never happen). + const settingsToSend = this.pendingSettings; + this.sentSettings = settingsToSend; + this.pendingSettings = {}; + const modifiedSettings = _.mapValues(settingsToSend, setting => setting.value()); + const requestData = { + action: 'ws_ame_ac_save_changeset', + _ajax_nonce: this.saveChangesetNonce, + changeset: (_a = savedChangeset === null || savedChangeset === void 0 ? void 0 : savedChangeset.name) !== null && _a !== void 0 ? _a : '', + modified: JSON.stringify(modifiedSettings), + }; + if (status !== null) { + requestData['status'] = status; + } + //If the changeset doesn't have a name, it is new. + if (!(savedChangeset === null || savedChangeset === void 0 ? void 0 : savedChangeset.hasName())) { + requestData['createNew'] = 1; + } + const request = $.ajax({ + url: this.ajaxUrl, + method: 'POST', + data: requestData, + dataType: 'json', + timeout: 20000, + }); + this.currentChangesetRequest = request; + const self = this; + function storeValidationResultsFrom(serverResponse) { + const results = _.get(serverResponse, ['data', 'validationResults']); + if (typeof results !== 'object') { + return; + } + for (const settingId in results) { + const setting = self.get(settingId); + if (!setting.isDefined()) { + continue; + } + if (!modifiedSettings.hasOwnProperty(settingId)) { + continue; + } + const sentValue = modifiedSettings[settingId]; + const state = results[settingId]; + if (state.isValid) { + setting.get().clearValidationErrorsForValue(sentValue); + } + else { + //Since the server response is not fully validated, some typeof checks + //are still useful. + // noinspection SuspiciousTypeOfGuard + setting.get().addValidationErrorsForValue(sentValue, _.filter(state.errors, error => (typeof error.message === 'string'))); + } + } + } + function storeChangesetDetailsFrom(serverResponse) { + if (!savedChangeset) { + return; + } + //Store the returned changeset name in case a new changeset was created. + if (!savedChangeset.hasName()) { + const newName = _.get(serverResponse, ['data', 'changeset']); + if (typeof newName === 'string') { + savedChangeset.name(newName); + } + } + //Store the changeset status. + const newStatus = _.get(serverResponse, ['data', 'changesetStatus']); + if (typeof newStatus === 'string') { + savedChangeset.status(newStatus); + } + //Store the number of changes in the changeset. + const newChangeCount = _.get(serverResponse, ['data', 'changesetItemCount']); + if (typeof newChangeCount === 'number') { + savedChangeset.knownItemCount(newChangeCount); + } + //Was the changeset published? Because changesets are typically moved + //to trash after publishing, "status" might be "trash" instead of "publish", + //but we still want to know if it was successfully published. + const wasPublished = _.get(serverResponse, ['data', 'changesetWasPublished'], null); + if (wasPublished) { + savedChangeset.wasPublished(wasPublished); + } + } + request.done(function (response) { + storeChangesetDetailsFrom(response); + storeValidationResultsFrom(response); + //After successfully publishing a changeset, it has no more + //unsaved changes. + const isPublished = (savedChangeset.status() === 'publish') + || (savedChangeset.status() === 'future') + || (savedChangeset.wasPublished()); + if (isPublished) { + savedChangeset.currentSessionChanges(0); + } + //After a changeset is published or trashed, it can no longer + //be edited. We may be able to replace it with a new changeset + //that was created on the server. + if (!self.currentChangeset().canBeModified()) { + const nextChangeset = _.get(response, ['data', 'nextChangeset']); + if ((typeof nextChangeset === 'string') && (nextChangeset !== '')) { + self.currentChangeset(new Changeset(nextChangeset)); + } + } + }); + request.fail((requestObject) => { + if (typeof requestObject.responseJSON === 'object') { + storeValidationResultsFrom(requestObject.responseJSON); + storeChangesetDetailsFrom(requestObject.responseJSON); + } + //Add the unsaved settings back to the pending list. + for (const id in settingsToSend) { + //Keep only settings that still exist. + if (this.get(id).isDefined()) { + this.pendingSettings[id] = settingsToSend[id]; + } + } + //We don't automatically retry because the problem might be something + //that doesn't get better on its own, like missing permissions. + }); + request.always(() => { + this.currentChangesetRequest = null; + this.sentSettings = {}; + if (isExclusiveRequest) { + this.exclusiveOperation(false); + } + }); + return request; + } + savePendingSettings(timeout = 20) { + if (this.isExclusiveOperationInProgress()) { + //Wait for the exclusive operation to finish. + const deferred = $.Deferred(); + const result = deferred.then(() => this.doSavePendingSettings()); + const startTime = Date.now(); + const timer = setInterval(() => { + if (!this.isExclusiveOperationInProgress()) { + clearInterval(timer); + deferred.resolve(); + } + else if ((Date.now() - startTime) > timeout) { + clearInterval(timer); + deferred.reject(new Error('Exclusive operation timed out.')); + } + }, 200); + return result; + } + return this.doSavePendingSettings(); + } + doSavePendingSettings() { + //If there are no changes, we don't need to do anything. + if (_.isEmpty(this.pendingSettings)) { + return $.Deferred().resolve().promise(); + } + return this.saveChangeset(); + } + getCurrentChangeset() { + return this.currentChangeset(); + } + /** + * Get any unsaved setting changes. + * + * @returns An object mapping setting IDs to their modified values. + */ + get unsavedChanges() { + //Include both pending settings and sent settings. Sent settings + //might not be saved yet. + let unsavedSettings = {}; + _.defaults(unsavedSettings, this.pendingSettings, this.sentSettings); + return _.mapValues(unsavedSettings, setting => setting.value()); + } + publishChangeset() { + if (this.isExclusiveOperationInProgress()) { + return $.Deferred() + .reject(new Error('Another exclusive changeset operation is already in progress.')) + .promise(); + } + return this.saveChangeset('publish'); + } + trashChangeset() { + if (this.isExclusiveOperationInProgress()) { + return $.Deferred() + .reject(new Error('Another exclusive changeset operation is already in progress.')) + .promise(); + } + const changeset = this.currentChangeset(); + if (!changeset.hasName()) { + //The changeset hasn't been saved yet, so we can just mark it as trashed. + changeset.status('trash'); + changeset.currentSessionChanges(0); + //It's a success of sorts. + return $.Deferred().resolve(true).promise(); + } + this.exclusiveOperation(true); + const requestData = { + action: 'ws_ame_ac_trash_changeset', + _ajax_nonce: this.trashChangesetNonce, + changeset: changeset.name + }; + const request = $.ajax({ + url: this.ajaxUrl, + method: 'POST', + data: requestData, + dataType: 'json', + timeout: 20000, + }); + this.currentChangesetRequest = request; + request.done(function () { + changeset.status('trash'); + changeset.currentSessionChanges(0); + }); + //Unfortunately, jQuery doesn't seem to allow us to create a custom + //error object and pass it to other handlers, so code that uses this + //method will have to parse the error response itself. + request.always(() => { + this.currentChangesetRequest = null; + this.exclusiveOperation(false); + }); + return request; + } + } + class Changeset { + constructor(name = '', knownItemCount = 0, initialStatus = '') { + /** + * The number of times settings have been changed in this changeset + * during the current customizer session. + * + * Note that this is not the same as the number settings in the changeset: + * if the same setting is changed X times, this counter will increase by X, + * but the changeset will still only have one entry for that setting. + */ + this.currentSessionChanges = ko.observable(0); + /** + * Once a changeset has been published or deleted, its contents can't be modified any more. + * @private + */ + this.fixedContentStatuses = { 'publish': true, 'trash': true, 'future': true }; + this.wasPublished = ko.observable(false); + this.name = ko.observable(name); + this.knownItemCount = ko.observable(knownItemCount); + this.status = ko.observable(initialStatus !== null && initialStatus !== void 0 ? initialStatus : ''); + } + hasName() { + const name = this.name(); + return (name !== ''); + } + canBeModified() { + return !this.fixedContentStatuses.hasOwnProperty(this.status()); + } + isNonEmpty() { + return (this.currentSessionChanges() > 0) || (this.knownItemCount() > 0); + } + } + //region Admin theme + const UrlOrEmpty = _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].union([ + _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].string().url().max(1000), + _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].literal('') + ]); + const AdminThemeMetadata = _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].object({ + pluginName: _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].string().max(100), + shortDescription: _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].string().max(500), + pluginSlug: _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].string().max(64).toLowerCase().default('') + .refine(function (input) { + //Only allow alphanumeric characters, underscores, and dashes. + //Empty string is allowed. + return /^[a-z0-9_-]*$/.test(input); + }, { message: 'The slug can only contain letters (a-z), numbers, underscores, and dashes.' }), + identifierPrefix: _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].string().max(20).optional(), + pluginVersion: _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].string().default('1.0').optional(), + pluginUrl: UrlOrEmpty.optional(), + authorName: _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].string().max(100).optional(), + authorUrl: UrlOrEmpty.optional(), + requiredWpVersion: _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].string().max(30).default('4.7').optional(), + testedWpVersion: _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].string().max(30).optional(), + }); + const AdminThemeSettings = _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].record( + //Key type + _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].string().min(1), + //Value type + _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__["default"].any()); + class AdminThemeImportReport { + constructor(fileName, metadata) { + this.fileName = fileName; + this.metadata = metadata; + this.totalSettings = 0; + this.importedSettings = 0; + this.invalidSettings = 0; + this.skippedSettings = 0; + this.differentImportedSettings = 0; + this.pluginName = metadata.pluginName || '(Unnamed)'; + } + } + function observableWithZodValidation(value, schema) { + const underlyingObservable = ko.observable(value); + const observable = ko.pureComputed({ + read: underlyingObservable, + write: (newValue) => { + const validationResult = schema.safeParse(newValue); + if (validationResult.success) { + underlyingObservable(validationResult.data); + observable.ameZodValidationError(null); + observable.ameValidationErrors([]); + } + else { + observable.ameZodValidationError(validationResult.error); + //Convert Zod issues to ObservableValidationErrors. + observable.ameValidationErrors(validationResult.error.issues.map(issue => { + return { + code: issue.code, + message: issue.message + }; + })); + } + } + }); + observable.ameZodValidationError = ko.observable(null); + observable.ameValidationErrors = ko.observable([]); + observable.ameIsValid = ko.pureComputed(() => { + const errors = observable.ameValidationErrors(); + return !errors || errors.length === 0; + }); + return observable; + } + class ObservableThemeMetadata { + constructor(metadata) { + var _a, _b, _c, _d, _e, _f, _g, _h; + this.pluginName = observableWithZodValidation(metadata.pluginName, AdminThemeMetadata.shape.pluginName); + this.shortDescription = observableWithZodValidation(metadata.shortDescription, AdminThemeMetadata.shape.shortDescription); + this.pluginSlug = observableWithZodValidation((_a = metadata.pluginSlug) !== null && _a !== void 0 ? _a : '', AdminThemeMetadata.shape.pluginSlug); + this.identifierPrefix = observableWithZodValidation((_b = metadata.identifierPrefix) !== null && _b !== void 0 ? _b : '', AdminThemeMetadata.shape.identifierPrefix); + this.pluginVersion = observableWithZodValidation((_c = metadata.pluginVersion) !== null && _c !== void 0 ? _c : '', AdminThemeMetadata.shape.pluginVersion); + this.pluginUrl = observableWithZodValidation((_d = metadata.pluginUrl) !== null && _d !== void 0 ? _d : '', AdminThemeMetadata.shape.pluginUrl); + this.authorName = observableWithZodValidation((_e = metadata.authorName) !== null && _e !== void 0 ? _e : '', AdminThemeMetadata.shape.authorName); + this.authorUrl = observableWithZodValidation((_f = metadata.authorUrl) !== null && _f !== void 0 ? _f : '', AdminThemeMetadata.shape.authorUrl); + this.requiredWpVersion = observableWithZodValidation((_g = metadata.requiredWpVersion) !== null && _g !== void 0 ? _g : '', AdminThemeMetadata.shape.requiredWpVersion); + this.testedWpVersion = observableWithZodValidation((_h = metadata.testedWpVersion) !== null && _h !== void 0 ? _h : '', AdminThemeMetadata.shape.testedWpVersion); + } + toObject() { + return { + pluginName: this.pluginName(), + shortDescription: this.shortDescription(), + pluginSlug: this.pluginSlug(), + identifierPrefix: this.identifierPrefix(), + pluginVersion: this.pluginVersion(), + pluginUrl: this.pluginUrl(), + authorName: this.authorName(), + authorUrl: this.authorUrl(), + requiredWpVersion: this.requiredWpVersion(), + testedWpVersion: this.testedWpVersion(), + }; + } + isValid() { + //This seems really inelegant, but I can't think of a better way to do it. + return this.pluginName.ameIsValid() + && this.shortDescription.ameIsValid() + && this.pluginSlug.ameIsValid() + && this.identifierPrefix.ameIsValid() + && this.pluginVersion.ameIsValid() + && this.pluginUrl.ameIsValid() + && this.authorName.ameIsValid() + && this.authorUrl.ameIsValid() + && this.requiredWpVersion.ameIsValid() + && this.testedWpVersion.ameIsValid(); + } + } + class DownloadThemeDialog extends AmeBaseKnockoutDialog { + constructor(getChangesetName, savePendingChangesetData) { + super(); + this.getChangesetName = getChangesetName; + this.savePendingChangesetData = savePendingChangesetData; + this.isOperationInProgress = ko.observable(false); + this.autoCancelButton = true; + this.advancedOptionsVisible = ko.observable(false); + this.helpVisible = ko.observable(false); + this.changesetName = ko.observable(''); + this.metadataJson = ko.observable(''); + this.downloadCookieName = ko.observable(''); + this.cleanupCurrentDownload = () => { + }; + this.options.minWidth = 400; + this.meta = ko.observable(new ObservableThemeMetadata(AdminThemeMetadata.parse({ + pluginName: 'Custom Admin Theme', + shortDescription: 'A custom admin theme generated using the Admin Menu Editor Pro plugin.', + pluginVersion: '1.0', + }))); + this.isConfirmButtonEnabled = ko.computed(() => { + if (this.isOperationInProgress()) { + return false; + } + if (getChangesetName() === '') { + //To generate an admin theme, the changeset must have already been saved. + return false; + } + return this.meta().isValid(); + }); + this.areFieldsEditable = ko.computed(() => { + return !this.isOperationInProgress(); + }); + this.advancedOptionsToggleLabel = ko.pureComputed(() => { + return this.advancedOptionsVisible() ? 'Fewer options' : 'More options'; + }); + this.helpToggleLabel = ko.pureComputed(() => { + return this.helpVisible() ? 'Hide info' : 'How it works'; + }); + } + getConfirmButtonLabel() { + return 'Download Admin Theme'; + } + toggleAdvancedOptions() { + this.advancedOptionsVisible(!this.advancedOptionsVisible()); + } + toggleHelp() { + this.helpVisible(!this.helpVisible()); + } + onConfirm(event) { + //Sanity checks. + const changesetName = this.getChangesetName(); + if (changesetName === '') { + alert('Error: The changeset has not been saved yet (name is empty).'); + return; + } + if (!this.meta().isValid()) { + //This should never happen because the confirm button is disabled + //when the metadata is invalid. + alert('Error: The admin theme details are not valid.'); + return; + } + const metadata = this.meta().toObject(); + this.isOperationInProgress(true); + const $form = $('#ame-ac-theme-download-request-form'); + const $frame = $('#ame-ac-theme-download-frame'); + //Cancel the operation and re-enable buttons if the request takes too long. + let isCancelledOrDone = false; + const requestTimeoutMs = 30000; + const requestStartTime = (new Date()).getTime(); + let statusCheckInterval = null; + const cleanup = this.cleanupCurrentDownload = () => { + isCancelledOrDone = true; + $frame.off('load.ameAcDownloadAdminTheme'); + if (timeoutTimer) { + clearTimeout(timeoutTimer); + } + if (statusCheckInterval) { + clearInterval(statusCheckInterval); + } + $frame.attr('src', 'about:blank'); + this.isOperationInProgress(false); + if (this.cleanupCurrentDownload === cleanup) { + this.cleanupCurrentDownload = () => { + }; + } + }; + const timeoutTimer = setTimeout(() => { + cleanup(); + alert('Error: The download operation timed out.'); + }, requestTimeoutMs); + this.savePendingChangesetData().then(() => { + if (isCancelledOrDone) { + return; + } + this.changesetName(changesetName); + this.metadataJson(JSON.stringify(metadata)); + //The server will set a cookie with a unique name that can be used + //to check if the download has been initiated. Note that the user + //can still cancel the download. + const cookieName = ('ameAcFileDownload_' + + new Date().getTime() + + '_' + + Math.round(Math.random() * 10000) //No dots allowed these cookie names. + ); + this.downloadCookieName(cookieName); + //Clear the frame to prevent the old response from being read. + $frame.attr('src', 'about:blank'); + try { + $frame.contents().find('body').html(''); + } + catch (e) { + //Ignore but log cross-origin errors. These should not happen in practice. + if (console && console.error) { + console.error(e); + } + } + statusCheckInterval = setInterval(() => { + const cookieValue = $.cookie(cookieName); + if (cookieValue) { + cleanup(); + $.removeCookie(cookieName); + //Close the dialog when the download starts. + this.isOpen(false); + return; + } + if ((new Date()).getTime() - requestStartTime > requestTimeoutMs) { + cleanup(); + } + }, 1000); + $frame.on('load.ameAcDownloadAdminTheme', () => { + //Get the response from the frame. It should be JSON displayed as text. + const responseText = String($frame.contents().text()).trim(); + const response = JSON.parse(responseText); + cleanup(); + if ((response === null) || (typeof response !== 'object')) { + alert('Error: Received an invalid response from the server.'); + } + else { + if (!response.success) { + let errorMessage; + if (response.data.message) { + errorMessage = response.data.message; + } + else { + errorMessage = 'An unknown error occurred on the server.'; + } + alert(errorMessage); + } + else { + //This should never happen in practice. + alert('Error: The server did not start the download correctly.'); + } + } + }); + $form.trigger('submit'); + }, () => { + if (isCancelledOrDone) { + return; + } + cleanup(); + alert('Error: Could not save the changeset data before generating an admin theme.'); + }); + } + onClose(event, ui) { + this.cleanupCurrentDownload(); + } + } + //endregion + class SectionNavigation { + constructor() { + this.sectionNavStack = ko.observableArray([]); + this.$sectionList = $('#ame-ac-container-collection'); + this.$sectionList.on('click', '.ame-ac-section-link', (event) => { + event.preventDefault(); + if (event.currentTarget === null) { + return; //Shouldn't happen in practice, but let's satisfy the type checker. + } + const targetId = $(event.currentTarget).data('target-id'); + if (targetId) { + this.navigateToSection(targetId); + } + }); + this.$sectionList.on('click', '.ame-ac-section-back-button', (event) => { + event.preventDefault(); + this.navigateBack(); + }); + this.breadcrumbs = ko.pureComputed(() => { + return this.sectionNavStack() + .map((sectionId) => $('#' + sectionId)) + .filter(($section) => $section.length > 0) + .map(($section) => { + return { + title: $section.find('.ame-ac-section-title .ame-ac-section-own-title') + .first().text() + }; + }); + }); + } + navigateToSection(sectionElementId) { + const $section = $('#' + sectionElementId); + if ($section.length === 0) { + return; + } + if ($section.hasClass('ame-ac-current-section')) { + return; //Already on this section. + } + //If the requested section is in the navigation stack, navigate back + //to it instead of putting more sections on the stack. + const stackIndex = this.sectionNavStack.indexOf(sectionElementId); + if (stackIndex !== -1) { + while (this.sectionNavStack().length > stackIndex) { + this.navigateBack(); + } + return; + } + const $previousSection = this.$sectionList.find('.ame-ac-current-section'); + if ($previousSection.length > 0) { + this.expectTransition($previousSection, '.ame-ac-section'); + $previousSection + .removeClass('ame-ac-current-section') + .addClass('ame-ac-previous-section'); + this.sectionNavStack.push($previousSection.attr('id')); + $previousSection.trigger('adminMenuEditor:leaveSection'); + } + this.expectTransition($section, '.ame-ac-section'); + $section.addClass('ame-ac-current-section'); + $section.trigger('adminMenuEditor:enterSection'); + } + navigateBack() { + if (this.sectionNavStack().length < 1) { + return; + } + const $newCurrentSection = $('#' + this.sectionNavStack.pop()); + if ($newCurrentSection.length === 0) { + return; + } + const $oldCurrentSection = this.$sectionList.find('.ame-ac-current-section'); + this.expectTransition($oldCurrentSection, '.ame-ac-section'); + $oldCurrentSection.removeClass('ame-ac-current-section ame-ac-previous-section'); + $oldCurrentSection.trigger('adminMenuEditor:leaveSection'); + const $oldPreviousSection = this.$sectionList.find('.ame-ac-previous-section'); + $oldPreviousSection.removeClass('ame-ac-previous-section'); + //Show the new current section. + this.expectTransition($newCurrentSection, '.ame-ac-section'); + $newCurrentSection.addClass('ame-ac-current-section'); + $newCurrentSection.trigger('adminMenuEditor:enterSection'); + //The next section in the stack becomes the previous section. + if (this.sectionNavStack().length > 0) { + this.$sectionList.find('#' + this.sectionNavStack()[this.sectionNavStack().length - 1]) + .addClass('ame-ac-previous-section'); + } + } + //Add a special class to sections when they have an active CSS transition. + //This is used to keep both sections visible while the previous section + //slides out and the next section slides in. + expectTransition($element, requiredSelector) { + if (prefersReducedMotion) { + return; + } + if ($element.data('ameHasTransitionEvents')) { + return; //Event handler(s) already added. + } + const transitionEvents = 'transitionend transitioncancel'; + $element.addClass('ame-ac-transitioning'); + function transitionEndCallback(event) { + //Ignore events that bubble from child elements. + if (!$(event.target).is(requiredSelector)) { + return; + } + $element + .off(transitionEvents, transitionEndCallback) + .data('ameHasTransitionEvents', null) + .removeClass('ame-ac-transitioning'); + } + $element.data('ameHasTransitionEvents', true); + $element.on(transitionEvents, transitionEndCallback); + } + } + class AdminCustomizer extends _admin_customizer_base_js__WEBPACK_IMPORTED_MODULE_8__.AmeAdminCustomizerBase.AdminCustomizerBase { + constructor(scriptData) { + super(scriptData); + this.exitPromptMessage = 'Unsaved changes will be lost if you navigate away from this page.'; + //Admin themes generated by this plugin should be fairly small. + this.maxImportFileSize = 500 * 1024; + /** + * Preview frame URL. + */ + this.currentPreviewUrl = null; + this.previewConnection = null; + this.$extraActionMenu = null; + this.$extraActionButton = null; + this.$importFileInput = null; + this.isImporting = ko.observable(false); + this.lastImportReport = ko.observable(null); + this.isImportReportVisible = ko.observable(true); + this.isDiscardingChanges = ko.observable(false); + this._isFrameLoading = false; + this.frameLoadingTimeoutId = null; + this.lastPreviewLoadTimestamp = new Date(0); + this.reloadWaitTimeoutId = null; + this.hasPendingPreviewReload = false; + this.settings = new CustomizerSettingsCollection(scriptData.ajaxUrl, scriptData.saveChangesetNonce, scriptData.trashChangesetNonce, scriptData.changesetName, scriptData.changesetItemCount, scriptData.changesetStatus); + _.forOwn(scriptData.settings, (data, id) => { + if (typeof id === 'string') { + this.settings.add(unserializeSetting(id, data)); + } + }); + let sectionIdCounter = 0; + this.interfaceStructure = unserializeUiElement(scriptData.interfaceStructure, this.settings.get.bind(this.settings), (data) => { + switch (data.t) { + case 'section': + data.component = 'ame-ac-section'; + //All sections must have unique IDs for navigation to work. + if (!data.id) { + data.id = 'autoID-' + (++sectionIdCounter); + } + break; + case 'control-group': + data.component = 'ame-ac-control-group'; + break; + case 'control': + //Tell controls that use number inputs to position the popup + //slider within the customizer sidebar. + if ((data.component === 'ame-number-input') + || (data.component === 'ame-box-dimensions')) { + data.params = data.params || {}; + data.params.popupSliderWithin = '#ame-ac-sidebar-content'; + } + //Replace regular separators with AC-specific ones. + if (data.component === 'ame-horizontal-separator') { + data.component = 'ame-ac-separator'; + } + } + }); + //Add the changeset name to the URL (if not already present). + const currentUrl = new URL(window.location.href); + if (currentUrl.searchParams.get('ame-ac-changeset') !== this.settings.changesetName()) { + currentUrl.searchParams.set('ame-ac-changeset', this.settings.changesetName()); + window.history.replaceState({}, '', currentUrl.href); + } + //When the changeset name changes, also change the URL. Discourage navigating + //to the old URL (no pushState()) because the name is only expected to change + //when the old changeset becomes invalid (e.g. it's deleted or published). + this.settings.changesetName.subscribe((changesetName) => { + const url = new URL(window.location.href); + url.searchParams.set('ame-ac-changeset', changesetName); + window.history.replaceState({}, '', url.href); + }); + this.$saveButton = $('#ame-ac-apply-changes'); + //The save button should be enabled when: + // - There are non-zero changes in the current changeset. + // - All settings are valid. + // - The changeset is not in the process of being published, deleted, etc. + // - The contents of the changeset can be modified (e.g. not already published). + const isSaveButtonEnabled = ko.pureComputed(() => { + const changeset = this.settings.getCurrentChangeset(); + return (changeset.isNonEmpty() + && changeset.canBeModified() + && !this.settings.isExclusiveOperationInProgress() + && !this.settings.hasValidationErrors()); + }); + //Update button state when the customizer loads. + this.$saveButton.prop('disabled', !isSaveButtonEnabled()); + //And also on changes. + isSaveButtonEnabled.subscribe((isEnabled) => { + var _a; + this.$saveButton.prop('disabled', !isEnabled); + //Change the text back to the default when the button is enabled. + if (isEnabled) { + this.$saveButton.val((_a = this.$saveButton.data('default-text')) !== null && _a !== void 0 ? _a : 'Save Changes'); + } + }); + //Handle the "Save Changes" button. + this.$saveButton.on('click', () => { + //Show the spinner. + const $spinner = $('#ame-ac-primary-actions .spinner'); + $spinner.css('visibility', 'visible').show(); + const publishFailNoticeId = 'ame-ac-publish-failed-notice'; + //Remove the previous error notification, if any. + $('#' + publishFailNoticeId).remove(); + const promise = this.settings.publishChangeset(); + promise.fail((error) => { + //Show a dismissible error notification. + let message = 'An unexpected error occurred while saving changes.'; + if (typeof error === 'string') { + message = error; + } + else if (error instanceof Error) { + message = error.message; + } + else if (typeof error.responseJSON === 'object') { + message = _.get(error.responseJSON, ['data', 'message'], message); + } + const $notice = $('
    ') + .attr('id', publishFailNoticeId) + .addClass('notice notice-error is-dismissible') + .text(message); + //WordPress won't automatically add the dismiss button to a dynamically + //generated notice like this, so we have to do it. + $notice.append($('') + .append('Dismiss this notice') + .on('click', (event) => { + event.preventDefault(); + $notice.remove(); //Not as fancy as WP does it. + })); + const $container = $('#ame-ac-global-notification-area'); + $container.append($notice); + }); + promise.done(() => { + var _a; + this.$saveButton.val((_a = this.$saveButton.data('published-text')) !== null && _a !== void 0 ? _a : 'Saved'); + //The preview could be stale. For example, the color scheme module + //switches between "actual" and "preview" color schemes dynamically, + //but the "actual" scheme could change after applying new settings. + //Let's reload the preview frame to make sure it's up-to-date. + this.queuePreviewFrameReload(); + }); + promise.always(() => { + $spinner.css('visibility', 'hidden'); + }); + }); + //Prevent the user from interacting with settings while the changeset is being modified. + this.settings.isExclusiveOperationInProgress.subscribe((isInProgress) => { + $('#ame-ac-sidebar-blocker-overlay').toggle(isInProgress); + }); + //Show a general overlay with a progress spinner while something is happening. + this.isGeneralOverlayVisible = ko.pureComputed(() => { + return this.isImporting() || this.isDiscardingChanges(); + }); + //Initialize the "download admin theme" dialog. + this.downloadThemeDialog = new DownloadThemeDialog(() => this.settings.getCurrentChangeset().name(), () => this.settings.savePendingSettings()); + //Toggle available extra actions based on changeset status. + this.importActionEnabled = ko.pureComputed(() => { + const changeset = this.settings.getCurrentChangeset(); + return changeset && changeset.canBeModified() + && !this.settings.isExclusiveOperationInProgress(); + }); + this.importActionEnabled.subscribe((isEnabled) => { + if (this.$extraActionMenu) { + this.$extraActionMenu.find('.ame-ac-import-theme-action') + .toggleClass('ui-state-disabled', !isEnabled); + } + }); + this.discardChangesActionEnabled = ko.pureComputed(() => { + const changeset = this.settings.getCurrentChangeset(); + return changeset && changeset.isNonEmpty() && changeset.canBeModified() + && !this.settings.isExclusiveOperationInProgress(); + }); + this.discardChangesActionEnabled.subscribe((isEnabled) => { + if (this.$extraActionMenu) { + this.$extraActionMenu.find('.ame-ac-discard-changes-action') + .toggleClass('ui-state-disabled', !isEnabled); + } + }); + this.downloadThemeActionEnabled = ko.pureComputed(() => { + return !this.settings.isExclusiveOperationInProgress(); + }); + this.downloadThemeActionEnabled.subscribe((isEnabled) => { + if (this.$extraActionMenu) { + this.$extraActionMenu.find('.ame-ac-download-theme-action') + .toggleClass('ui-state-disabled', !isEnabled); + } + }); + this.sectionNavigation = new SectionNavigation(); + //Set up the preview frame. + this.$previewFrame = $('iframe#ame-ac-preview'); + this.initialPreviewUrl = scriptData.initialPreviewUrl; + this.refreshPreviewNonce = scriptData.refreshPreviewNonce; + this.$previewFrame.on('load', () => { + var _a; + this.isFrameLoading = false; + //The URL that was actually loaded might not match the one that + //was requested (e.g. because there was a redirect). + this.currentPreviewUrl = null; + //Close the previous postMessage connection. + if (this.previewConnection) { + this.previewConnection.disconnect(); + this.previewConnection = null; + } + const frame = this.$previewFrame.get(0); + if (!frame || !(frame instanceof HTMLIFrameElement)) { + return; + } + //Try to get the preview URL from the iframe. + try { + const url = (_a = frame.contentWindow) === null || _a === void 0 ? void 0 : _a.location.href; + if (url) { + this.currentPreviewUrl = url; + } + } + catch (e) { + //We can't get the URL directly, probably because it's a cross-origin iframe. + } + this.previewConnection = AmeAcCommunicator.connectToChild(frame, { + 'setPreviewUrl': (url) => { + if (this.isPreviewableUrl(url)) { + this.previewUrl = url; + } + }, + 'notifyPreviewUrlChanged': (url) => { + this.currentPreviewUrl = url; + } + }, this.allowedCommOrigins, scriptData.isWpDebugEnabled); + this.previewConnection.promise.then((connection) => { + if (typeof connection === 'undefined') { + //This should never happen, but the type checker doesn't know that. + throw new Error('Unexpected error: Connection apparently succeeded, but the connection object is undefined'); + } + connection.execute('getCurrentUrl').then((url) => { + if (url && (typeof url === 'string')) { + this.currentPreviewUrl = url; + } + }); + //Notify other scripts that the preview frame is loaded and + //the postMessage connection is ready for use. + $('body').trigger('adminMenuEditor:acPreviewConnectionReady'); + }); + }); + this.previewUrl = this.initialPreviewUrl; + //Notify other scripts. This lets them register custom controls and so on. + $('#ame-ac-admin-customizer').trigger('adminMenuEditor:acRegister', [this]); + const throttledReloadPreview = _.throttle(() => { + this.queuePreviewFrameReload(); + }, 1000, //The reload method does its own throttling, so we use a low wait time here. + { leading: true, trailing: true }); + //Refresh the preview when any setting changes. + this.settings.addChangeListener((setting, newValue) => { + if (setting.supportsPostMessage + && this.previewConnection + && this.previewConnection.isConnected) { + this.previewConnection.execute('previewSetting', setting.id, newValue); + } + else { + throttledReloadPreview(); + } + }); + const registerUnloadPrompt = () => { + //Ask for confirmation when the user tries to leave the page and the changeset + //has unpublished changes. + $(window).on('beforeunload.ame-ac-exit-confirm', (event) => { + if (this.hasUnpublishedChanges()) { + event.preventDefault(); + //Note: The confirmation prompt will only be displayed if the user + //has interacted with the page (e.g. clicked something). + //As of this writing, MDN says that some browsers still don't support triggering + //an "unsaved changes" prompt with event.preventDefault(). You need to set + //event.returnValue to a string or return a string from the event handler. + //Modern browsers will ignore the content and display their own generic message. + return this.exitPromptMessage; + } + }); + }; + /* + Allegedly, registering a beforeunload handler can cause the browser to + disable some optimizations, so let's only do it when the user changes + something or the changeset already contains some changes. + */ + if (this.settings.getCurrentChangeset().isNonEmpty()) { + registerUnloadPrompt(); + } + else { + const listenerId = this.settings.addChangeListener(() => { + //Remove the listener after it has been triggered once. + this.settings.removeChangeListener(listenerId); + registerUnloadPrompt(); + }); + } + } + getSettingObservable(settingId, defaultValue) { + //Let's just implement this temporarily while working on refactoring this + //stuff to use KO components. + return this.settings + .get(settingId) + .map(setting => setting.value) + .getOrElse(ko.observable(defaultValue)); + } + getAllSettingValues() { + throw new Error('Method not implemented.'); + } + get previewUrl() { + return this.currentPreviewUrl; + } + set previewUrl(url) { + if (url === this.currentPreviewUrl) { + return; + } + //The URL starts out as null, but it cannot be set to NULL again after + //the preview frame has been loaded. + if (url === null) { + throw new Error('Cannot directly set preview URL to null'); + } + if (this.isPreviewableUrl(url)) { + this.navigatePreviewFrame(url); + } + } + navigatePreviewFrame(url = null, forceReload = false) { + const oldUrl = this.previewUrl; + if (url === null) { + url = oldUrl !== null && oldUrl !== void 0 ? oldUrl : this.initialPreviewUrl; + } + const isSameUrl = (oldUrl === url); + if (isSameUrl && !forceReload) { + return; + } + //If there are any unsaved changes, let's include them in the preview by simulating + //a form submission and sending the changes as form data. The server-side component + //will merge these changes with existing changeset data. + const unsavedChanges = this.settings.unsavedChanges; + const simulateFormSubmission = !_.isEmpty(unsavedChanges); + const parsedUrl = new URL(url); + //If we're not using form submission, add a special parameter + //to the URL to force a refresh. + const refreshParam = '_ame-ac-refresh-trigger'; + if (isSameUrl && !simulateFormSubmission) { + parsedUrl.searchParams.set(refreshParam, Date.now() + '_' + Math.random()); + } + else { + //Otherwise, remove the parameter just to be safe. + parsedUrl.searchParams.delete(refreshParam); + } + //Ensure that the changeset used in the preview matches the current + //changeset and preview is enabled. This is just a precaution. Normally, + //the preview script automatically changes link URLs. + parsedUrl.searchParams.set('ame-ac-changeset', this.settings.changesetName()); + parsedUrl.searchParams.set('ame-ac-preview', '1'); + this.hasPendingPreviewReload = false; //Reloading now, so no longer pending. + this.isFrameLoading = true; + //console.info('navigatePreviewFrame: Navigating to ' + parsedUrl.href); + if (simulateFormSubmission) { + const formData = { + action: 'ws_ame_ac_refresh_preview_frame', + "ame-ac-changeset": this.settings.changesetName(), + modified: JSON.stringify(unsavedChanges), + nonce: this.refreshPreviewNonce + }; + const $form = $('
    ') + .attr('method', 'post') + .attr('action', parsedUrl.href) + .attr('target', 'ame-ac-preview-frame') + .appendTo('body'); + let key; + for (key in formData) { + const value = formData[key]; + $('') + .attr('type', 'hidden') + .attr('name', key) + .val(value) + .appendTo($form); + } + this.currentPreviewUrl = parsedUrl.href; + $form.trigger('submit'); + $form.remove(); + } + else { + this.currentPreviewUrl = parsedUrl.href; + this.$previewFrame.attr('src', this.currentPreviewUrl); + } + } + set isFrameLoading(isLoading) { + const wasLoadingBefore = this._isFrameLoading; + if (!isLoading && (isLoading === wasLoadingBefore)) { + return; + } + //In some circumstances, we may start to load a new URL before + //the previous one has finished loading. This is valid and should + //reset the load timeout. + $('#ame-ac-preview-refresh-indicator').toggleClass('ame-ac-show-indicator', isLoading); + if (this.frameLoadingTimeoutId) { + clearTimeout(this.frameLoadingTimeoutId); + this.frameLoadingTimeoutId = null; + } + if (isLoading) { + //As a precaution, we'll assume that if the frame doesn't load in a reasonable + //time, it will never finish loading. + this.frameLoadingTimeoutId = window.setTimeout(() => { + if (this.isFrameLoading) { + this.isFrameLoading = false; + } + }, 20000); + } + this._isFrameLoading = isLoading; + if (wasLoadingBefore && !isLoading) { + this.lastPreviewLoadTimestamp = new Date(); + } + //Once the frame is loaded, trigger any pending reload. + if (!isLoading && this.hasPendingPreviewReload) { + this.hasPendingPreviewReload = false; + this.queuePreviewFrameReload(); + } + } + get isFrameLoading() { + return this._isFrameLoading; + } + queuePreviewFrameReload() { + if (this.reloadWaitTimeoutId) { + return; //The frame will reload soon. + } + if (this.isFrameLoading) { + this.hasPendingPreviewReload = true; + return; + } + //To avoid stressing the server, wait at least X ms after the last + //load completes before reloading the frame. + const reloadWaitTime = 2000; + const now = new Date(); + const timeSinceLastLoad = now.getTime() - this.lastPreviewLoadTimestamp.getTime(); + if (timeSinceLastLoad < reloadWaitTime) { + this.reloadWaitTimeoutId = window.setTimeout(() => { + this.reloadWaitTimeoutId = null; + this.queuePreviewFrameReload(); + }, reloadWaitTime - timeSinceLastLoad); + return; + } + //Actually reload the frame. + this.navigatePreviewFrame(null, true); + } + onBindingsApplied(rootElement) { + //Navigate to the root section. In the current implementation this can't happen + //until bindings have been applied, so it's not part of the constructor. + this.navigateToRootSection(); + //Initialize the action menu. + this.$extraActionButton = jQuery('#ame-ac-extra-actions-trigger', rootElement); + this.$extraActionMenu = jQuery('#ame-ac-extra-actions-menu', rootElement).menu(); + //Update menu states. + this.importActionEnabled.notifySubscribers(this.importActionEnabled()); + this.discardChangesActionEnabled.notifySubscribers(this.discardChangesActionEnabled()); + //Get the file picker. + this.$importFileInput = jQuery('#ame-ac-import-admin-theme-file', rootElement); + } + navigateToRootSection() { + this.sectionNavigation.navigateToSection('ame-ac-section-structure-root'); + } + // noinspection JSUnusedGlobalSymbols -- Used in at least one add-on. + /** + * Execute an RPC method in the preview frame. + * + * @param {string} methodName + * @param {*} args + */ + executeRpcMethod(methodName, ...args) { + if (!this.previewConnection || !this.previewConnection.isConnected) { + return $.Deferred().reject('The preview frame is not connected.').promise(); + } + return this.previewConnection.execute(methodName, ...args); + } + confirmExit() { + if (this.hasUnpublishedChanges()) { + if (window.confirm(this.exitPromptMessage)) { + //Remove the confirmation prompt that appears when leaving the page. + //We don't want to show two prompts. + $(window).off('beforeunload.ame-ac-exit-confirm'); + return true; + } + return false; + } + return true; + } + hasUnpublishedChanges() { + const changeset = this.settings.getCurrentChangeset(); + return (changeset.isNonEmpty() + && !changeset.wasPublished() + && (changeset.status() !== 'trash') //Can't publish a trashed changeset. + ); + } + // noinspection JSUnusedGlobalSymbols -- Used in the Knockout template. + toggleExtraActionMenu() { + if (!this.$extraActionMenu) { + return; + } + this.$extraActionMenu.toggle(); + if (this.$extraActionMenu.is(':visible')) { + //Position the menu below the button. + const $button = $('#ame-ac-extra-actions-trigger'); + this.$extraActionMenu.position({ + my: 'right top', + at: 'right bottom', + of: $button, + collision: 'flipfit' + }); + //Hide the menu when the user clicks outside the menu or the button. + $(document).on('mousedown.ameAcExtraMenuHide', this.handleClickOutsideActionMenu.bind(this)); + } + else { + //Remove the click listener if it's still active. + $(document).off('mousedown.ameAcExtraMenuHide'); + } + } + handleClickOutsideActionMenu(event) { + if (!this.$extraActionMenu + || !this.$extraActionMenu.is(':visible') + || !this.$extraActionButton) { + //The event listener should not be active if the menu is not visible. + $(document).off('mousedown.ameAcExtraMenuHide'); + return; + } + const menuElement = this.$extraActionMenu.get(0); + const buttonElement = this.$extraActionButton.get(0); + const isClickOutsideMenu = !menuElement.contains(event.target); + const isClickOutsideButton = !buttonElement.contains(event.target); + if (isClickOutsideMenu && isClickOutsideButton) { + this.hideExtraActionMenu(); + } + } + hideExtraActionMenu() { + if (!this.$extraActionMenu) { + return; + } + this.$extraActionMenu.hide(); + //Stop listening for clicks outside the menu. + $(document).off('mousedown.ameAcExtraMenuHide'); + } + actionOpenDownloadDialog() { + if (!this.downloadThemeActionEnabled()) { + return; + } + this.downloadThemeDialog.isOpen(true); + this.isImportReportVisible(false); + this.hideExtraActionMenu(); + } + actionOpenImportDialog() { + if (!this.importActionEnabled()) { + //Can't import if there is no changeset or the changeset can't be edited. + //The menu item should be disabled in this case, but we'll check anyway. + return false; + } + this.hideExtraActionMenu(); + //Allow the default action to proceed, which will open the file picker. + return true; + } + actionDiscardChanges() { + if (!this.discardChangesActionEnabled()) { + return; + } + this.hideExtraActionMenu(); + if (this.settings.isExclusiveOperationInProgress()) { + alert('Another operation is in progress. Please wait for it to complete before discarding changes.'); + return; + } + if (!confirm('Are you sure you want to discard your unsaved changes?')) { + return; + } + this.isImportReportVisible(false); + this.isDiscardingChanges(true); + this.settings.trashChangeset() + .then(() => { + //Reload the customizer with a new changeset. + //First, to get the customizer's base URL, get the current URL + //and remove all query parameters except "page". + const url = new URL(window.location.href); + const page = url.searchParams.get('page'); + url.search = ''; + url.searchParams.set('page', page || 'ame-admin-customizer'); + //Don't need the hash either. + url.hash = ''; + //Add a random parameter to force a reload. + url.searchParams.set('_ame-ac-reload', Math.random().toString(36).substring(7)); + //Navigate to the new URL. + window.location.href = url.toString(); + //Note that the isDiscardingChanges flag is not reset here, + //so the progress overlay will stay visible until the page reloads. + }) + .fail((requestObject) => { + let message = requestObject.statusText || 'Unknown error.'; + if (typeof requestObject.responseJSON === 'object') { + const customMessage = _.get(requestObject.responseJSON, ['data', 'message']); + if (typeof customMessage === 'string') { + message = customMessage; + } + } + alert('Error: ' + message); + this.isDiscardingChanges(false); + }); + } + handleImportFileSelection() { + if (!this.$importFileInput) { + return; + } + const fileInput = this.$importFileInput.get(0); + if (!fileInput || !fileInput.files || (fileInput.files.length < 1)) { + return; + } + //Get the first file. Normally, there should only be one. + const selectedFile = fileInput.files.item(0); + if (!selectedFile) { + return; + } + //Limit the file size. + if (selectedFile.size > this.maxImportFileSize) { + alert('Error: The selected file is too large. The maximum file size is ' + + Math.round(this.maxImportFileSize / 1024) + ' KiB'); + //Clear the file input. + this.$importFileInput.val(''); + return; + } + this.isImporting(true); + this.lastImportReport(null); + JSZip.loadAsync(selectedFile).then((zip) => { + const metadataFileRegex = /^([\\/]?[a-zA-Z0-9_-]+[\\/])metadata\.json$/; + const foundMetadataFiles = zip.file(metadataFileRegex); + if (!foundMetadataFiles || (foundMetadataFiles.length < 1)) { + throw new Error('The selected file is not an admin theme generated by this tool.'); + } + const metadataFile = foundMetadataFiles[0]; + //Get the directory name and separator from the metadata file path. + //The prefix will usually be something like "admin-theme-slug/". + const matches = metadataFileRegex.exec(metadataFile.name); + let directoryPrefix; + if (!matches || (matches.length < 2)) { + throw new Error('The directory structure of this ZIP file is not recognized.'); + } + else { + directoryPrefix = matches[1]; + } + const settingsFile = zip.file(directoryPrefix + 'settings.json'); + if (!settingsFile) { + throw new Error('The selected ZIP file is missing a settings.json file.'); + } + //Read both files. + return Promise.all([ + metadataFile.async('string'), + settingsFile.async('string') + ]); + }, (error) => { + const errorMessage = error.message || error; + throw new Error('Error reading "' + selectedFile.name + '": ' + errorMessage); + }).then((fileContents) => { + if (!fileContents) { + throw new Error('Failed to read settings and metadata from the ZIP file.'); + } + const metadata = this.parseImportedAdminThemeFile(fileContents[0], 'metadata.json', AdminThemeMetadata); + const settings = this.parseImportedAdminThemeFile(fileContents[1], 'settings.json', AdminThemeSettings); + const report = new AdminThemeImportReport(selectedFile.name, metadata); + //Import metadata. + this.downloadThemeDialog.meta(new ObservableThemeMetadata(metadata)); + //Import settings. + for (const [settingId, value] of Object.entries(settings)) { + report.totalSettings++; + const foundSetting = this.settings.get(settingId); + foundSetting.forEach((setting) => { + const oldValue = setting.value(); + const errors = setting.tryUpdate(value); + if (errors && errors.length) { + report.invalidSettings++; + } + else { + report.importedSettings++; + if (oldValue != value) { + report.differentImportedSettings++; + } + } + }); + if (foundSetting.isEmpty()) { + report.skippedSettings++; + } + } + this.lastImportReport(report); + this.isImportReportVisible(true); + }).catch((error) => { + //Error handling: Show the error message to the user. + let errorMessage; + if (error instanceof Error) { + errorMessage = error.message; + } + else { + errorMessage = String(error); + } + alert('Error: ' + errorMessage); + }).finally(() => { + var _a; + this.isImporting(false); + (_a = this.$importFileInput) === null || _a === void 0 ? void 0 : _a.val(''); + }); + } + parseImportedAdminThemeFile(content, name, schema) { + try { + const parsedJson = JSON.parse(content); + return schema.parse(parsedJson); + } + catch (error) { + let errorMessage; + if (error instanceof _zod_lib_index_js__WEBPACK_IMPORTED_MODULE_11__.ZodError) { + //Convert issues to a newline-separated string. + errorMessage = error.issues.map((issue) => { + return issue.path.join('.') + ': ' + issue.message; + }).join('\n'); + } + else if (error instanceof Error) { + errorMessage = error.message; + } + else { + errorMessage = String(error); + } + //Add the file name to the error message. + throw new Error('Error parsing ' + name + ':\n' + errorMessage); + } + } + dismissImportReport() { + this.isImportReportVisible(false); + } + } + AmeAdminCustomizer.AdminCustomizer = AdminCustomizer; +})(AmeAdminCustomizer || (AmeAdminCustomizer = {})); +jQuery(function () { + //Give other scripts a chance to load before we start. + //Some of them also use jQuery to run when the DOM is ready. + setTimeout(() => { + window.wsAdminCustomizer = new AmeAdminCustomizer.AdminCustomizer(wsAmeAdminCustomizerData); + const rootElement = document.getElementById('ame-ac-admin-customizer'); + if (rootElement === null) { + throw new Error('The root element for the admin customizer was not found.'); + } + ko.applyBindings(window.wsAdminCustomizer, rootElement); + //Notify the customizer that bindings have been applied. It needs to do some + //additional setup that can't be done until the DOM structure is ready. + setTimeout(() => { + window.wsAdminCustomizer.onBindingsApplied(rootElement); + }, 5); //Components are rendered asynchronously. + }, 20); +}); + + +/***/ }), + +/***/ "./extras/modules/admin-customizer/admin-customizer-base.js": +/*!******************************************************************!*\ + !*** ./extras/modules/admin-customizer/admin-customizer-base.js ***! + \******************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AmeAdminCustomizerBase": () => (/* binding */ AmeAdminCustomizerBase) +/* harmony export */ }); + +var AmeAdminCustomizerBase; +(function (AmeAdminCustomizerBase) { + class AdminCustomizerBase { + constructor(scriptData) { + this.allowedCommOrigins = scriptData.allowedCommOrigins; + if (this.allowedCommOrigins.length === 0) { + this.allowedCommOrigins = [window.location.origin]; + } + this.allowedPreviewUrls = scriptData.allowedPreviewUrls; + this.parsedAllowedUrls = this.allowedPreviewUrls.map(url => new URL(url)); + } + isPreviewableUrl(url) { + if (typeof url === 'string') { + url = new URL(url); + } + if (typeof url.protocol === 'undefined') { + return false; + } + //Only HTTP(S) links are previewable. + if ((url.protocol !== 'http:') && (url.protocol !== 'https:')) { + return false; + } + //Check against the list of allowed URLs. + for (const allowedUrl of this.parsedAllowedUrls) { + //Protocol and host must match. The path must start with the path + //of the allowed URL (possibly without a trailing slash). + if ((url.protocol === allowedUrl.protocol) && (url.host === allowedUrl.host)) { + const allowedPath = allowedUrl.pathname.replace(/\/$/, ''); + if (url.pathname.indexOf(allowedPath) === 0) { + return true; + } + } + } + return false; + } + } + AmeAdminCustomizerBase.AdminCustomizerBase = AdminCustomizerBase; +})(AmeAdminCustomizerBase || (AmeAdminCustomizerBase = {})); +//# sourceMappingURL=admin-customizer-base.js.map + +/***/ }), + +/***/ "./extras/modules/admin-customizer/ko-components/ame-ac-content-section.js": +/*!*********************************************************************************!*\ + !*** ./extras/modules/admin-customizer/ko-components/ame-ac-content-section.js ***! + \*********************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _ame_ac_section_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ame-ac-section.js */ "./extras/modules/admin-customizer/ko-components/ame-ac-section.js"); +/* harmony import */ var _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../pro-customizables/ko-components/control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + + +class AmeAcContentSection extends _ame_ac_section_js__WEBPACK_IMPORTED_MODULE_0__.AmeAcSection { + constructor(params, $element) { + super(params, $element); + if ((typeof params.parentSectionLevel === 'function') && ko.isObservable(params.parentSectionLevel)) { + this.parentSectionLevel = params.parentSectionLevel; + } + else { + this.parentSectionLevel = null; + } + this.contentSectionLevel = ko.pureComputed(() => { + let parentLevel = 0; + if (this.parentSectionLevel !== null) { + parentLevel = this.parentSectionLevel(); + } + return parentLevel + 1; + }); + //Tell child sections about our section level. + this.childComponents().forEach((child) => { + if (child.name === 'ame-ac-content-section') { + child.params.parentSectionLevel = this.contentSectionLevel; + } + }); + this.sectionLevelClass = ko.pureComputed(() => { + const level = this.contentSectionLevel(); + return 'ame-ac-content-section-' + level; + }); + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_1__.createComponentConfig)(AmeAcContentSection, ` +
  • +

    +
  • + + + +`)); +//# sourceMappingURL=ame-ac-content-section.js.map + +/***/ }), + +/***/ "./extras/modules/admin-customizer/ko-components/ame-ac-control-group.js": +/*!*******************************************************************************!*\ + !*** ./extras/modules/admin-customizer/ko-components/ame-ac-control-group.js ***! + \*******************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../pro-customizables/ko-components/control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); +/* harmony import */ var _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../pro-customizables/assets/customizable.js */ "./extras/pro-customizables/assets/customizable.js"); + + +var ControlGroup = _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__.AmeCustomizable.ControlGroup; +class AmeAcControlGroup extends _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoContainerViewModel { + constructor(params, $element) { + var _a, _b; + super(params, $element); + this.labelFor = (_b = ((_a = this.uiElement) === null || _a === void 0 ? void 0 : _a.labelFor)) !== null && _b !== void 0 ? _b : null; + this.titleDisabled = (typeof params.titleDisabled !== 'undefined') ? (!!params.titleDisabled) : false; + } + getExpectedUiElementType() { + return ControlGroup; + } + mapChildToComponentBinding(child) { + if (child.component) { + return _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.ComponentBindingOptions.fromElement(child); + } + return super.mapChildToComponentBinding(child); + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createComponentConfig)(AmeAcControlGroup, ` +
  • + + + + + + + + +
      +
    • + + + + +
    • +
    +
  • +`)); +//# sourceMappingURL=ame-ac-control-group.js.map + +/***/ }), + +/***/ "./extras/modules/admin-customizer/ko-components/ame-ac-control.js": +/*!*************************************************************************!*\ + !*** ./extras/modules/admin-customizer/ko-components/ame-ac-control.js ***! + \*************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../pro-customizables/ko-components/control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); +/* harmony import */ var _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../pro-customizables/assets/customizable.js */ "./extras/pro-customizables/assets/customizable.js"); + + +var Control = _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__.AmeCustomizable.Control; +class MissingComponentError extends Error { + constructor(uiElement) { + super(`The UI element "${uiElement.label}" [${uiElement.id}] is missing a component name.`); + this.uiElement = uiElement; + } +} +class AmeAcControl extends _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoControlViewModel { + constructor(params, $element) { + super(params, $element); + //uiElement is required for this component. + if (!this.uiElement) { + throw new Error('The uiElement parameter is required for AmeAcControl'); + } + this.wrapperLabelEnabled = (this.uiElement.label !== '') && (!this.uiElement.includesOwnLabel); + this.labelForId = this.uiElement.labelTargetId; + if (!this.uiElement.component) { + throw new MissingComponentError(this.uiElement); + } + } + getExpectedUiElementType() { + return Control; + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createComponentConfig)(AmeAcControl, ` +
  • + + + + + + + + + +
  • +`)); +//# sourceMappingURL=ame-ac-control.js.map + +/***/ }), + +/***/ "./extras/modules/admin-customizer/ko-components/ame-ac-section-link.js": +/*!******************************************************************************!*\ + !*** ./extras/modules/admin-customizer/ko-components/ame-ac-section-link.js ***! + \******************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../pro-customizables/ko-components/control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); +/* harmony import */ var _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../pro-customizables/assets/customizable.js */ "./extras/pro-customizables/assets/customizable.js"); +/* harmony import */ var _ame_ac_section_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./ame-ac-section.js */ "./extras/modules/admin-customizer/ko-components/ame-ac-section.js"); + + +var Section = _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__.AmeCustomizable.Section; + +class AmeAcSectionLink extends _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoContainerViewModel { + constructor(params, $element) { + super(params, $element); + //uiElement is required for this component. + if (!this.uiElement) { + throw new Error('The uiElement parameter is required for AmeAcSectionLink'); + } + this.targetElementId = _ame_ac_section_js__WEBPACK_IMPORTED_MODULE_2__.AmeAcSection.getSectionElementId(this.uiElement); + } + getExpectedUiElementType() { + return Section; + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createComponentConfig)(AmeAcSectionLink, ` + +`)); +//# sourceMappingURL=ame-ac-section-link.js.map + +/***/ }), + +/***/ "./extras/modules/admin-customizer/ko-components/ame-ac-section.js": +/*!*************************************************************************!*\ + !*** ./extras/modules/admin-customizer/ko-components/ame-ac-section.js ***! + \*************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AmeAcSection": () => (/* binding */ AmeAcSection), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../pro-customizables/ko-components/control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); +/* harmony import */ var _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../pro-customizables/assets/customizable.js */ "./extras/pro-customizables/assets/customizable.js"); + + +var Section = _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__.AmeCustomizable.Section; +var Control = _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__.AmeCustomizable.Control; +var ControlGroup = _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__.AmeCustomizable.ControlGroup; +class AmeAcSection extends _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoContainerViewModel { + constructor(params, $element) { + super(params, $element); + //Must have an uiElement. + if (this.uiElement === null) { + throw new Error('AmeAcSection must have an uiElement.'); + } + this.elementId = AmeAcSection.getSectionElementId(this.uiElement); + if ((typeof params.breadcrumbs !== 'undefined') && ko.isObservable(params.breadcrumbs)) { + this.breadcrumbs = params.breadcrumbs; + } + else { + this.breadcrumbs = null; + } + //To keep the header text alignment consistent when navigating between sections, + //let's show something even if there are no breadcrumbs. + const emptyBreadcrumbText = 'Admin Menu Editor Pro'; + this.breadcrumbText = ko.pureComputed(() => { + if (this.breadcrumbs === null) { + return emptyBreadcrumbText; + } + const breadcrumbs = this.breadcrumbs(); + if (breadcrumbs.length < 1) { + return emptyBreadcrumbText; + } + let titles = breadcrumbs.map(crumb => crumb.title); + //Show the root section differently, "Admin Customizer" is too long. + //Not sure about what text to use here, could matching the Theme Customizer be confusing? + //Alternatives: 🛠️🎨, use \uFE0E to render the emoji without colors (only works for some). + //Alternatives: ⋯ and … + titles[0] = 'Customizing'; + //Due to space constraints, show only the last 2 breadcrumbs. + if (titles.length > 2) { + titles = titles.slice(titles.length - 2); + } + return titles.join(' \u25B8 '); + }); + } + getExpectedUiElementType() { + return Section; + } + mapChildToComponentBinding(child) { + if (child instanceof Section) { + if (child.preferredRole === 'content') { + return _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.ComponentBindingOptions.fromElement(child, 'ame-ac-content-section'); + } + else { + return _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.ComponentBindingOptions.fromElement(child, 'ame-ac-section-link'); + } + } + else if (child instanceof ControlGroup) { + return _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.ComponentBindingOptions.fromElement(child, 'ame-ac-control-group'); + } + else if ((child instanceof Control) + && (['ame-ac-separator', 'ame-horizontal-separator'].indexOf(child.component) < 0)) { + //Wrap each control in a control group if it's not already in one. + //Separators are an exception because they're cosmetic and need different styling. + const controlGroup = child.createControlGroup(); + return this.mapChildToComponentBinding(controlGroup); + } + else { + return _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.ComponentBindingOptions.fromElement(child); + } + } + static getSectionElementId(section) { + const prefix = 'ame-ac-section-'; + if (section.id) { + return prefix + section.id; + } + const slug = section.title.toLowerCase().replace(/[^a-z0-9]/g, '-'); + if (slug !== '') { + return prefix + slug; + } + throw new Error('Cannot generate a section element ID because the section does not have an ID or a title.'); + } + dispose() { + super.dispose(); + this.childComponents.dispose(); + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createComponentConfig)(AmeAcSection, ` +
      + + + + +
    +`)); +//# sourceMappingURL=ame-ac-section.js.map + +/***/ }), + +/***/ "./extras/modules/admin-customizer/ko-components/ame-ac-separator.js": +/*!***************************************************************************!*\ + !*** ./extras/modules/admin-customizer/ko-components/ame-ac-separator.js ***! + \***************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../pro-customizables/ko-components/control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + +class AmeAcSeparator extends _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createComponentConfig)(AmeAcSeparator, ` +
  • +`)); +//# sourceMappingURL=ame-ac-separator.js.map + +/***/ }), + +/***/ "./extras/modules/admin-customizer/ko-components/ame-ac-structure.js": +/*!***************************************************************************!*\ + !*** ./extras/modules/admin-customizer/ko-components/ame-ac-structure.js ***! + \***************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../pro-customizables/ko-components/control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); +/* harmony import */ var _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../pro-customizables/assets/customizable.js */ "./extras/pro-customizables/assets/customizable.js"); + + +var Section = _pro_customizables_assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__.AmeCustomizable.Section; +class AmeAcStructure extends _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoRendererViewModel { + constructor(params, $element) { + var _a; + super(params, $element); + this.allSections = []; + const rootSection = new Section({ + t: 'section', + id: 'structure-root', + title: (_a = this.structure.title) !== null && _a !== void 0 ? _a : 'Root', + }, this.structure.children); + //Recursively collect all sections. + function collectChildSections(section, accumulator = []) { + accumulator.push(section); + for (const child of section.children) { + if (child instanceof Section) { + collectChildSections(child, accumulator); + } + } + return accumulator; + } + this.allSections = collectChildSections(rootSection); + //Give the breadcrumb list to each section, if available. + if (typeof params.breadcrumbs !== 'undefined') { + for (const section of this.allSections) { + section.componentParams.breadcrumbs = params.breadcrumbs; + } + } + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createRendererComponentConfig)(AmeAcStructure, ` + + + +`)); +//# sourceMappingURL=ame-ac-structure.js.map + +/***/ }), + +/***/ "./extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js": +/*!***********************************************************************************!*\ + !*** ./extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js ***! + \***********************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../pro-customizables/ko-components/control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + +class AmeAcValidationErrors extends _pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + if (typeof params.errors !== 'undefined') { + if (Array.isArray(params.errors)) { + this.errors = params.errors; + } + else if (ko.isObservable(params.errors)) { + this.errors = params.errors; + } + else { + throw new Error('The "errors" parameter must be an array or an observable array.'); + } + } + else { + console.log('Params:', params); + throw new Error('The "errors" parameter is required for the AmeAcValidationErrors component.'); + } + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_pro_customizables_ko_components_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createComponentConfig)(AmeAcValidationErrors, ` +
      +
    • + +
    • +
    +`)); +//# sourceMappingURL=ame-ac-validation-errors.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-box-dimensions/ame-box-dimensions.js": +/*!*****************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-box-dimensions/ame-box-dimensions.js ***! + \*****************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); +/* harmony import */ var _lazy_popup_slider_adapter_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../lazy-popup-slider-adapter.js */ "./extras/pro-customizables/ko-components/lazy-popup-slider-adapter.js"); + + + +const allDimensionKeys = [ + 'top', 'bottom', 'left', 'right', + 'topLeft', 'topRight', 'bottomLeft', 'bottomRight' +]; +function isDimensionKey(key) { + return allDimensionKeys.includes(key); +} +const DefaultDimensionsInOrder = [ + ['top', 'Top'], + ['bottom', 'Bottom'], + ['left', 'Left'], + ['right', 'Right'], +]; +const SideDimensions = ['top', 'bottom', 'left', 'right']; +const SymmetricDimensionMap = { + 'vertical': ['top', 'bottom'], + 'horizontal': ['left', 'right'], +}; +function isSymmetricDimensionKey(key) { + return SymmetricDimensionMap.hasOwnProperty(key); +} +let nextId = 0; +class AmeBoxDimensions extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + this.inputIdPrefix = '_ame-box-dimensions-c-input-' + (nextId++); + this.unitElementId = '_ame-box-dimensions-c-unit-' + (nextId++); + if ((typeof params['dimensionNames'] !== 'undefined') && Array.isArray(params['dimensionNames'])) { + this.dimensionsInOrder = params['dimensionNames']; + } + else { + this.dimensionsInOrder = DefaultDimensionsInOrder; + } + //Make observable proxies for the individual dimension settings. + const temp = {}; + for (const [dimensionKey, dimensionName] of this.dimensionsInOrder) { + const setting = this.settings['value.' + dimensionKey]; + if (!setting || (typeof setting !== 'object')) { + throw new Error(`Missing setting for the "${dimensionName}" side.`); + } + temp[dimensionKey] = ko.computed({ + read: () => { + return setting.value(); + }, + write: (newValue) => { + if (newValue === '') { + newValue = null; + } + setting.value(newValue); + }, + deferEvaluation: true, + }).extend({ 'ameNumericInput': true }); + } + this.dimensions = temp; + //Similarly, make an observable for the unit setting. + const unitSetting = this.settings['value.unit']; + if (!unitSetting || (typeof unitSetting !== 'object')) { + throw new Error('Missing setting for the unit.'); + } + this.unitSetting = unitSetting; + const defaultDropdownOptions = { + options: [], + optionsText: 'text', + optionsValue: 'value' + }; + if (params.unitDropdownOptions && (typeof params.unitDropdownOptions === 'object')) { + const unitDropdownOptions = params.unitDropdownOptions; + this.unitDropdownOptions = { + options: unitDropdownOptions['options'] || defaultDropdownOptions.options, + optionsText: unitDropdownOptions['optionsText'] || defaultDropdownOptions.optionsText, + optionsValue: unitDropdownOptions['optionsValue'] || defaultDropdownOptions.optionsValue, + }; + } + else { + this.unitDropdownOptions = defaultDropdownOptions; + } + this.isLinkActive = ko.observable(false); + //Enable the link button by default if all dimensions are equal. Exception: null values. + //Dimensions can have different defaults, so null doesn't necessarily mean that they + //are actually equal. + const firstKey = Object.keys(this.dimensions)[0]; + const firstValue = this.dimensions[firstKey](); + if ((firstValue !== null) && (firstValue !== '')) { + let areAllDimensionsEqual = true; + for (const [dimensionKey] of this.dimensionsInOrder) { + if (this.dimensions[dimensionKey]() !== firstValue) { + areAllDimensionsEqual = false; + break; + } + } + this.isLinkActive(areAllDimensionsEqual); + } + //When "link" mode is enabled, keep all dimensions in sync. + let isUpdatingAllDimensions = false; //Prevent infinite loops. + const updateAllDimensions = (newValue) => { + if (!isUpdatingAllDimensions && this.isLinkActive()) { + isUpdatingAllDimensions = true; + newValue = this.normalizeValue(newValue); + for (const observable of Object.values(this.dimensions)) { + observable(newValue); + } + isUpdatingAllDimensions = false; + } + }; + for (const dimensionKey of Object.keys(this.dimensions)) { + this.dimensions[dimensionKey].subscribe(updateAllDimensions); + } + //In "symmetric" mode, the top/bottom and left/right dimensions are always equal. + //The control will only show "vertical" and "horizontal" inputs. + this.symmetricModeEnabled = ko.observable(this.decideSymmetricMode(params)); + //Create computed observables for the "vertical" and "horizontal" dimensions. + this.symmetricValues = {}; + for (const name in SymmetricDimensionMap) { + if (!isSymmetricDimensionKey(name) || !SymmetricDimensionMap.hasOwnProperty(name)) { + continue; + } + const sides = SymmetricDimensionMap[name]; + this.symmetricValues[name] = ko.computed({ + read: () => { + if (this.symmetricModeEnabled()) { + return this.dimensions[sides[0]](); + } + else { + return null; + } + }, + write: (newValue) => { + if (this.symmetricModeEnabled()) { + newValue = this.normalizeValue(newValue); + for (const side of sides) { + this.dimensions[side](newValue); + } + } + }, + deferEvaluation: true + }).extend({ 'ameNumericInput': true }); + } + //The control displays a different set of inputs depending on the current mode. + this.inputsInOrder = ko.pureComputed(() => { + let result; + if (this.symmetricModeEnabled()) { + result = [ + ['vertical', 'Vertical'], + ['horizontal', 'Horizontal'], + ]; + } + else { + result = this.dimensionsInOrder; + } + return result; + }); + let sliderOptions = { + 'positionParentSelector': '.ame-single-box-dimension', + 'verticalOffset': -2, + }; + if (typeof params.popupSliderWithin === 'string') { + sliderOptions.positionWithinClosest = params.popupSliderWithin; + } + this.sliderAdapter = new _lazy_popup_slider_adapter_js__WEBPACK_IMPORTED_MODULE_1__.LazyPopupSliderAdapter(params.sliderRanges ? params.sliderRanges : null, '.ame-box-dimensions-control', 'input.ame-box-dimensions-input', sliderOptions); + } + get classes() { + return ['ame-box-dimensions-control', ...super.classes]; + } + //noinspection JSUnusedGlobalSymbols -- Used in the template. + /** + * Get an observable for a specific dimension or a pair of dimensions. + * + * Unfortunately, Knockout doesn't seem to support nested indexed accessors + * like "dimensions[$data[0]]", so we have to use a method instead. + */ + getInputObservable(key) { + if (this.symmetricModeEnabled() && isSymmetricDimensionKey(key)) { + return this.symmetricValues[key]; + } + if (isDimensionKey(key)) { + return this.dimensions[key]; + } + throw new Error('Invalid input key for the current mode: ' + key); + } + getInputIdFor(key) { + return this.inputIdPrefix + '-' + key; + } + // noinspection JSUnusedGlobalSymbols -- Used in the template. + getInputAttributes(key) { + return { + id: this.getInputIdFor(key), + 'data-unit-element-id': this.unitElementId, + 'data-ame-box-dimension': key, + }; + } + // noinspection JSUnusedGlobalSymbols -- Used in the template. + getSettingFor(key) { + const settingName = 'value.' + key; + if (settingName in this.settings) { + return this.settings[settingName]; + } + if (this.symmetricModeEnabled() && isSymmetricDimensionKey(key)) { + for (const dimension of SymmetricDimensionMap[key]) { + //Since both symmetric dimensions are always equal, we can use + //either of the two settings. + const settingName = 'value.' + dimension; + if (settingName in this.settings) { + return this.settings[dimension]; + } + } + } + return null; + } + // noinspection JSUnusedGlobalSymbols -- Actually used in the template. + toggleLink() { + this.isLinkActive(!this.isLinkActive()); + //When enabling "link" mode, fill all inputs with the same value. + //Use the first non-empty value. + if (this.isLinkActive()) { + let firstValue = null; + for (const dimensionObservable of Object.values(this.dimensions)) { + const value = dimensionObservable(); + if ((value !== null) && (value !== '')) { + firstValue = value; + break; + } + } + if (firstValue !== null) { + firstValue = this.normalizeValue(firstValue); + for (const dimensionObservable of Object.values(this.dimensions)) { + dimensionObservable(firstValue); + } + } + } + } + normalizeValue(value) { + if (value === null) { + return null; + } + //Convert strings to numbers, and invalid strings to null. + if (typeof value === 'string') { + value = parseFloat(value); + if (isNaN(value)) { + return null; + } + } + return value; + } + /** + * Determine whether the control should be in "symmetric" mode. + */ + decideSymmetricMode(componentParams) { + //This mode is off by default and can be enabled by setting the "symmetricMode" parameter. + let enableMode = (typeof componentParams['symmetricMode'] !== 'undefined') + ? (!!componentParams['symmetricMode']) + : false; + if (!enableMode) { + return false; + } + //Symmetric mode can't be enabled if the control doesn't have all side dimensions. + const hasAllSideDimensions = SideDimensions.every((key) => { + return (key in this.dimensions); + }); + if (!hasAllSideDimensions) { + return false; + } + //It also can only be enabled if top/bottom and left/right dimensions are equal. + return ((this.dimensions['top']() === this.dimensions['bottom']()) + && (this.dimensions['left']() === this.dimensions['right']())); + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeBoxDimensions, ` +
    + +
    + + +
    + + + +
    +`)); +//# sourceMappingURL=ame-box-dimensions.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-choice-control/ame-choice-control.js": +/*!*****************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-choice-control/ame-choice-control.js ***! + \*****************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AmeChoiceControl": () => (/* binding */ AmeChoiceControl), +/* harmony export */ "ChoiceControlOption": () => (/* binding */ ChoiceControlOption) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + +class ChoiceControlOption { + constructor(data) { + this.value = data.value; + this.label = data.label; + this.description = data.description || ''; + this.enabled = (typeof data.enabled === 'undefined') || data.enabled; + this.icon = data.icon || ''; + } +} +class AmeChoiceControl extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + this.options = ko.observableArray([]); + if ((typeof params['options'] !== 'undefined') && Array.isArray(params.options)) { + this.options(params.options.map((optionData) => new ChoiceControlOption(optionData))); + } + } +} +//# sourceMappingURL=ame-choice-control.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-code-editor/ame-code-editor.js": +/*!***********************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-code-editor/ame-code-editor.js ***! + \***********************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + + +/** + * Code editor control with syntax highlighting. + * + * This control uses the custom Knockout binding "ameCodeMirror" to do the heavy + * lifting. The binding is defined in ko-extensions.ts. + * + * Note: The user can disable syntax highlighting. In that case, this control + * should behave like a normal textarea. + */ +class AmeCodeEditor extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + if ((typeof params.editorSettings === 'object') && (params.editorSettings !== null)) { + this.editorSettings = params.editorSettings; + } + else { + this.editorSettings = false; + } + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeCodeEditor, ` +
    + +
    + + + +`)); +//# sourceMappingURL=ame-code-editor.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-color-picker/ame-color-picker.js": +/*!*************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-color-picker/ame-color-picker.js ***! + \*************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + +/** + * A wrapper for the WordPress color picker. + * + * Note that the custom 'ameColorPicker' binding must be available when this component + * is used. You must enqueue the 'ame-ko-extensions' script for this to work. + */ +class AmeColorPicker extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + } + koDescendantsComplete(node) { + //Make the color picker input visible. Its visibility is set to hidden by default. + if (node.nodeType === Node.COMMENT_NODE) { + //The component was bound to a comment node. The real element + //should be the next non-comment sibling. + let nextElement; + do { + nextElement = node.nextElementSibling; + } while (nextElement && (nextElement.nodeType === Node.COMMENT_NODE)); + if (!nextElement) { + return; //This should never happen. + } + node = nextElement; + } + if (!node || (node.nodeType !== Node.ELEMENT_NODE)) { + return; //This should never happen. + } + const $picker = jQuery(node); + //This should be a .wp-picker-container element that contains an input. + const $input = $picker.find('input.ame-color-picker'); + if ($input.length > 0) { + $input.css('visibility', 'visible'); + } + } + get classes() { + return ['ame-color-picker', 'ame-color-picker-component', ...super.classes]; + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeColorPicker, ` + +`)); +//# sourceMappingURL=ame-color-picker.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-components.js": +/*!******************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-components.js ***! + \******************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "registerBaseComponents": () => (/* binding */ registerBaseComponents) +/* harmony export */ }); +/* harmony import */ var _ame_box_dimensions_ame_box_dimensions_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ame-box-dimensions/ame-box-dimensions.js */ "./extras/pro-customizables/ko-components/ame-box-dimensions/ame-box-dimensions.js"); +/* harmony import */ var _ame_color_picker_ame_color_picker_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ame-color-picker/ame-color-picker.js */ "./extras/pro-customizables/ko-components/ame-color-picker/ame-color-picker.js"); +/* harmony import */ var _ame_font_style_picker_ame_font_style_picker_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./ame-font-style-picker/ame-font-style-picker.js */ "./extras/pro-customizables/ko-components/ame-font-style-picker/ame-font-style-picker.js"); +/* harmony import */ var _ame_image_selector_ame_image_selector_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./ame-image-selector/ame-image-selector.js */ "./extras/pro-customizables/ko-components/ame-image-selector/ame-image-selector.js"); +/* harmony import */ var _ame_number_input_ame_number_input_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./ame-number-input/ame-number-input.js */ "./extras/pro-customizables/ko-components/ame-number-input/ame-number-input.js"); +/* harmony import */ var _ame_nested_description_ame_nested_description_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./ame-nested-description/ame-nested-description.js */ "./extras/pro-customizables/ko-components/ame-nested-description/ame-nested-description.js"); +/* harmony import */ var _ame_radio_button_bar_ame_radio_button_bar_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ame-radio-button-bar/ame-radio-button-bar.js */ "./extras/pro-customizables/ko-components/ame-radio-button-bar/ame-radio-button-bar.js"); +/* harmony import */ var _ame_radio_group_ame_radio_group_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./ame-radio-group/ame-radio-group.js */ "./extras/pro-customizables/ko-components/ame-radio-group/ame-radio-group.js"); +/* harmony import */ var _ame_select_box_ame_select_box_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./ame-select-box/ame-select-box.js */ "./extras/pro-customizables/ko-components/ame-select-box/ame-select-box.js"); +/* harmony import */ var _ame_sibling_description_ame_sibling_description_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./ame-sibling-description/ame-sibling-description.js */ "./extras/pro-customizables/ko-components/ame-sibling-description/ame-sibling-description.js"); +/* harmony import */ var _ame_static_html_ame_static_html_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./ame-static-html/ame-static-html.js */ "./extras/pro-customizables/ko-components/ame-static-html/ame-static-html.js"); +/* harmony import */ var _ame_text_input_ame_text_input_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./ame-text-input/ame-text-input.js */ "./extras/pro-customizables/ko-components/ame-text-input/ame-text-input.js"); +/* harmony import */ var _ame_toggle_checkbox_ame_toggle_checkbox_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./ame-toggle-checkbox/ame-toggle-checkbox.js */ "./extras/pro-customizables/ko-components/ame-toggle-checkbox/ame-toggle-checkbox.js"); +/* harmony import */ var _ame_unit_dropdown_ame_unit_dropdown_js__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./ame-unit-dropdown/ame-unit-dropdown.js */ "./extras/pro-customizables/ko-components/ame-unit-dropdown/ame-unit-dropdown.js"); +/* harmony import */ var _ame_wp_editor_ame_wp_editor_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./ame-wp-editor/ame-wp-editor.js */ "./extras/pro-customizables/ko-components/ame-wp-editor/ame-wp-editor.js"); +/* harmony import */ var _ame_horizontal_separator_ame_horizontal_separator_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./ame-horizontal-separator/ame-horizontal-separator.js */ "./extras/pro-customizables/ko-components/ame-horizontal-separator/ame-horizontal-separator.js"); +/* harmony import */ var _ame_code_editor_ame_code_editor_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./ame-code-editor/ame-code-editor.js */ "./extras/pro-customizables/ko-components/ame-code-editor/ame-code-editor.js"); +/* + * This utility module imports all the base Knockout components and exports + * a function that can be used to register the components with Knockout. + */ + + + + + + + + + + + + + + + + + +let componentsRegistered = false; +/** + * Register the base Knockout components that are part of AME. + * + * It's safe to call this function multiple times. It will only register the components once. + */ +function registerBaseComponents() { + if (componentsRegistered) { + return; + } + ko.components.register('ame-box-dimensions', _ame_box_dimensions_ame_box_dimensions_js__WEBPACK_IMPORTED_MODULE_0__["default"]); + ko.components.register('ame-color-picker', _ame_color_picker_ame_color_picker_js__WEBPACK_IMPORTED_MODULE_1__["default"]); + ko.components.register('ame-font-style-picker', _ame_font_style_picker_ame_font_style_picker_js__WEBPACK_IMPORTED_MODULE_2__["default"]); + ko.components.register('ame-image-selector', _ame_image_selector_ame_image_selector_js__WEBPACK_IMPORTED_MODULE_3__["default"]); + ko.components.register('ame-number-input', _ame_number_input_ame_number_input_js__WEBPACK_IMPORTED_MODULE_4__["default"]); + ko.components.register('ame-nested-description', _ame_nested_description_ame_nested_description_js__WEBPACK_IMPORTED_MODULE_5__["default"]); + ko.components.register('ame-radio-button-bar', _ame_radio_button_bar_ame_radio_button_bar_js__WEBPACK_IMPORTED_MODULE_6__["default"]); + ko.components.register('ame-radio-group', _ame_radio_group_ame_radio_group_js__WEBPACK_IMPORTED_MODULE_7__["default"]); + ko.components.register('ame-select-box', _ame_select_box_ame_select_box_js__WEBPACK_IMPORTED_MODULE_8__["default"]); + ko.components.register('ame-sibling-description', _ame_sibling_description_ame_sibling_description_js__WEBPACK_IMPORTED_MODULE_9__["default"]); + ko.components.register('ame-static-html', _ame_static_html_ame_static_html_js__WEBPACK_IMPORTED_MODULE_10__["default"]); + ko.components.register('ame-text-input', _ame_text_input_ame_text_input_js__WEBPACK_IMPORTED_MODULE_11__["default"]); + ko.components.register('ame-toggle-checkbox', _ame_toggle_checkbox_ame_toggle_checkbox_js__WEBPACK_IMPORTED_MODULE_12__["default"]); + ko.components.register('ame-unit-dropdown', _ame_unit_dropdown_ame_unit_dropdown_js__WEBPACK_IMPORTED_MODULE_13__["default"]); + ko.components.register('ame-wp-editor', _ame_wp_editor_ame_wp_editor_js__WEBPACK_IMPORTED_MODULE_14__["default"]); + ko.components.register('ame-horizontal-separator', _ame_horizontal_separator_ame_horizontal_separator_js__WEBPACK_IMPORTED_MODULE_15__["default"]); + ko.components.register('ame-code-editor', _ame_code_editor_ame_code_editor_js__WEBPACK_IMPORTED_MODULE_16__["default"]); + componentsRegistered = true; +} +//# sourceMappingURL=ame-components.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-description/ame-description.js": +/*!***********************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-description/ame-description.js ***! + \***********************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AmeDescriptionComponent": () => (/* binding */ AmeDescriptionComponent) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + +/** + * Base class for description components. + */ +class AmeDescriptionComponent extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoComponentViewModel { + constructor(params, $element) { + super(params, $element); + this.description = params.description || ''; + } +} +//# sourceMappingURL=ame-description.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-font-style-picker/ame-font-style-picker.js": +/*!***********************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-font-style-picker/ame-font-style-picker.js ***! + \***********************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + +//Note: Font style picker CSS is already included in the main 'controls.scss' file +//and won't be duplicated or included here. Instead, load that stylesheet when +//using any control components. +/** + * Font style options that can be selected in the picker component. + * + * Regrettably, these are duplicated from the PHP version of the control. + * Once browsers support importing JSON files, we can move this to a separate + * file and use that file in both places. + */ +const fontStyleOptions = { + "font-style": [ + { + "value": null, + "text": "Default font style", + "label": "—" + }, + { + "value": "italic", + "text": "Italic", + "label": "" + } + ], + "text-transform": [ + { + "value": null, + "text": "Default letter case", + "label": "—" + }, + { + "value": "uppercase", + "text": "Uppercase", + "label": { + 'text-transform': 'uppercase' + } + }, + { + "value": "lowercase", + "text": "Lowercase", + "label": { + 'text-transform': 'lowercase' + } + }, + { + "value": "capitalize", + "text": "Capitalize each word", + "label": { + 'text-transform': 'capitalize' + } + } + ], + "font-variant": [ + { + "value": null, + "text": "Default font variant", + "label": "—" + }, + { + "value": "small-caps", + "text": "Small caps", + "label": { + 'font-variant': 'small-caps' + } + } + ], + "text-decoration": [ + { + "value": null, + "text": "Default text decoration", + "label": "—" + }, + { + "value": "underline", + "text": "Underline", + "label": "" + }, + { + "value": "line-through", + "text": "Strikethrough", + "label": "" + } + ] +}; +//Generate label HTML for options that don't have it yet. +function makeFontSample(styles) { + let styleString = ''; + for (const [property, value] of Object.entries(styles)) { + styleString += `${property}: ${value};`; + } + return `ab`; +} +let flattenedOptions = []; +for (const [property, options] of Object.entries(fontStyleOptions)) { + options.forEach((option) => { + //Skip null values. They're used to indicate the default option, + //and we don't need those in the Knockout version of this control. + if (option.value === null) { + return; + } + let labelString; + if (typeof option.label === 'object') { + labelString = makeFontSample(option.label); + } + else { + labelString = option.label; + } + flattenedOptions.push({ + 'value': option.value, + 'text': option.text || '', + 'property': property, + 'label': labelString + }); + }); +} +class AmeFontStylePicker extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + this.options = flattenedOptions; + } + get classes() { + return ['ame-font-style-control', ...super.classes]; + } + // noinspection JSUnusedGlobalSymbols -- Used in the template, below. + isOptionSelected(property, value) { + if (this.settings.hasOwnProperty(property)) { + return (this.settings[property].value() === value); + } + return false; + } + // noinspection JSUnusedGlobalSymbols -- Used in the template. + toggleOption(property, value) { + if (!this.settings.hasOwnProperty(property)) { + return; + } + const targetSetting = this.settings[property]; + if (targetSetting.value() === value) { + //When the user clicks on the currently selected option, reset it to the default. + targetSetting.tryUpdate(null); + } + else { + //Otherwise, set the new value. + targetSetting.tryUpdate(value); + } + } +} +//Note: This weird spacing in the template string is intentional. It's used to +//remove whitespace nodes from the DOM, which would otherwise slightly change +//the layout of the control. +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeFontStylePicker, ` +
    + +
    +`)); +//# sourceMappingURL=ame-font-style-picker.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-horizontal-separator/ame-horizontal-separator.js": +/*!*****************************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-horizontal-separator/ame-horizontal-separator.js ***! + \*****************************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + +class AmeHorizontalSeparator extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeHorizontalSeparator, ` +
    +`)); +//# sourceMappingURL=ame-horizontal-separator.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-image-selector/ame-image-selector.js": +/*!*****************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-image-selector/ame-image-selector.js ***! + \*****************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + + +/** + * Image selector control. + * + * This implementation hands off the work to the existing AmeImageSelectorApi.ImageSelector + * class to avoid duplicating the effort. That class is not a module because it is also + * used for the more progressive-enhancement-y PHP-rendered controls, so we can't import it. + */ +class AmeImageSelector extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + this.selectorInstance = null; + //Verify that our dependencies are available. + if (typeof AmeImageSelectorApi === 'undefined') { + throw new Error('AmeImageSelectorApi is not available. Remember to enqueue "ame-image-selector-control-v2".'); + } + if (typeof AmeImageSelectorApi.ImageSelector === 'undefined') { + throw new Error('AmeImageSelectorApi.ImageSelector is not available. This is probably a bug.'); + } + this.externalUrlsAllowed = !!params.externalUrlsAllowed; + this.canSelectMedia = !!params.canSelectMedia; + this.imageProxy = this.settings.value.value; + } + get classes() { + return [ + 'ame-image-selector-v2', + ...super.classes, + ]; + } + koDescendantsComplete() { + const $container = this.findChild('.ame-image-selector-v2'); + if ($container.length === 0) { + return; + } + this.selectorInstance = new AmeImageSelectorApi.ImageSelector($container, { + externalUrlsAllowed: this.externalUrlsAllowed, + canSelectMedia: this.canSelectMedia, + }, this.imageProxy()); + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeImageSelector, ` +
    + +
    +`)); +//# sourceMappingURL=ame-image-selector.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-nested-description/ame-nested-description.js": +/*!*************************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-nested-description/ame-nested-description.js ***! + \*************************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); +/* harmony import */ var _ame_description_ame_description_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ame-description/ame-description.js */ "./extras/pro-customizables/ko-components/ame-description/ame-description.js"); + + +/** + * A simple component that displays the description of a UI element. + * + * Like AmeSiblingDescription, but intended to be rendered inside + * the parent control or container, not as a sibling. + */ +class AmeNestedDescription extends _ame_description_ame_description_js__WEBPACK_IMPORTED_MODULE_1__.AmeDescriptionComponent { +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createComponentConfig)(AmeNestedDescription, ` +
    +`)); +//# sourceMappingURL=ame-nested-description.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-number-input/ame-number-input.js": +/*!*************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-number-input/ame-number-input.js ***! + \*************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AmeNumberInput": () => (/* binding */ AmeNumberInput), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); +/* harmony import */ var _assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../assets/customizable.js */ "./extras/pro-customizables/assets/customizable.js"); +/// + + +var Control = _assets_customizable_js__WEBPACK_IMPORTED_MODULE_1__.AmeCustomizable.Control; +class AmeNumberInput extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoDependentControl { + constructor(params, $element) { + super(params, $element); + this.sliderRanges = null; + this.slider = null; + this.numericValue = this.valueProxy.extend({ 'ameNumericInput': true }); + this.unitText = params.unitText || ''; + this.hasUnitDropdown = params.hasUnitDropdown || false; + this.unitElementId = params.unitElementId || ''; + if (this.hasUnitDropdown && params.unitDropdownOptions) { + this.unitDropdownOptions = { + options: params.unitDropdownOptions.options || [], + optionsText: params.unitDropdownOptions.optionsText || 'text', + optionsValue: params.unitDropdownOptions.optionsValue || 'value' + }; + } + else { + this.unitDropdownOptions = null; + } + this.min = params.min || null; + this.max = params.max || null; + this.step = params.step || null; + if (params.sliderRanges) { + this.sliderRanges = params.sliderRanges; + } + this.popupSliderWithin = (typeof params.popupSliderWithin === 'string') ? params.popupSliderWithin : null; + this.inputClasses.unshift('ame-input-with-popup-slider', 'ame-number-input'); + } + get classes() { + const classes = ['ame-number-input-control']; + if (this.sliderRanges !== null) { + classes.push('ame-container-with-popup-slider'); + } + classes.push(...super.classes); + return classes; + } + get inputClasses() { + const classes = ['ame-input-with-popup-slider', 'ame-number-input']; + classes.push(...super.inputClasses); + return classes; + } + getAdditionalInputAttributes() { + let attributes = super.getAdditionalInputAttributes(); + if (this.min !== null) { + attributes['min'] = this.min.toString(); + } + if (this.max !== null) { + attributes['max'] = this.max.toString(); + } + if (this.step !== null) { + attributes['step'] = this.step.toString(); + } + if (this.unitElementId) { + attributes['data-unit-element-id'] = this.unitElementId; + } + return attributes; + } + // noinspection JSUnusedGlobalSymbols -- Used in the Knockout template in this same file. + showPopupSlider($data, event) { + if ((this.sliderRanges === null) || (typeof AmePopupSlider === 'undefined')) { + return; + } + //Some sanity checks. + if (!event.target) { + return; + } + const $input = jQuery(event.target); + if ($input.is(':disabled') || !$input.is('input')) { + return; + } + const $container = $input.closest('.ame-container-with-popup-slider'); + if ($container.length < 1) { + return; + } + //Initialize the slider if it's not already initialized. + if (!this.slider) { + let sliderOptions = {}; + if (this.popupSliderWithin) { + sliderOptions.positionWithinClosest = this.popupSliderWithin; + } + //In HTML, we would pass the range data as a "data-slider-ranges" attribute, + //but here we can just set the data directly. + $input.data('slider-ranges', this.sliderRanges); + this.slider = AmePopupSlider.createSlider($container, sliderOptions); + } + this.slider.showForInput($input); + } + getExpectedUiElementType() { + return Control; + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeNumberInput, ` +
    +
    + + + + + + + + +
    +
    +`)); +//# sourceMappingURL=ame-number-input.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-radio-button-bar/ame-radio-button-bar.js": +/*!*********************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-radio-button-bar/ame-radio-button-bar.js ***! + \*********************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); +/* harmony import */ var _ame_choice_control_ame_choice_control_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ame-choice-control/ame-choice-control.js */ "./extras/pro-customizables/ko-components/ame-choice-control/ame-choice-control.js"); + + + +class AmeRadioButtonBar extends _ame_choice_control_ame_choice_control_js__WEBPACK_IMPORTED_MODULE_1__.AmeChoiceControl { + constructor(params, $element) { + super(params, $element); + } + get classes() { + return ['ame-radio-button-bar-control', ...super.classes]; + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeRadioButtonBar, ` +
    + + + +
    +`)); +//# sourceMappingURL=ame-radio-button-bar.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-radio-group/ame-radio-group.js": +/*!***********************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-radio-group/ame-radio-group.js ***! + \***********************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); +/* harmony import */ var _ame_choice_control_ame_choice_control_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ame-choice-control/ame-choice-control.js */ "./extras/pro-customizables/ko-components/ame-choice-control/ame-choice-control.js"); + + + +// noinspection JSUnusedGlobalSymbols -- Enum keys like "Paragraph" are used when serializing wrapStyle in PHP. +var WrapStyle; +(function (WrapStyle) { + WrapStyle["LineBreak"] = "br"; + WrapStyle["Paragraph"] = "p"; + WrapStyle["None"] = ""; +})(WrapStyle || (WrapStyle = {})); +function isWrapStyle(value) { + if (typeof value !== 'string') { + return false; + } + return (typeof WrapStyle[value] === 'string'); +} +let nextRadioGroupId = 1; +class AmeRadioGroup extends _ame_choice_control_ame_choice_control_js__WEBPACK_IMPORTED_MODULE_1__.AmeChoiceControl { + constructor(params, $element) { + super(params, $element); + this.wrapStyle = WrapStyle.None; + this.childByValue = new Map(); + if ((typeof params['valueChildIndexes'] === 'object') && Array.isArray(params.valueChildIndexes)) { + const children = ko.unwrap(this.inputChildren); + for (const [value, index] of params.valueChildIndexes) { + if (!children || !children[index]) { + throw new Error('The "' + this.label + '" radio group has no children, but its valueChildIndexes' + + ' requires child #' + index + ' to be associated with value "' + value + '".'); + } + this.childByValue.set(value, children[index]); + } + } + this.wrapStyle = isWrapStyle(params.wrapStyle) ? WrapStyle[params.wrapStyle] : WrapStyle.None; + if (this.childByValue.size > 0) { + this.wrapStyle = WrapStyle.None; + } + this.radioInputPrefix = (typeof params.radioInputPrefix === 'string') + ? params.radioInputPrefix + : ('ame-rg-input-' + nextRadioGroupId++ + '-'); + } + get classes() { + const result = ['ame-radio-group-component', ...super.classes]; + if (this.childByValue.size > 0) { + result.push('ame-rg-has-nested-controls'); + } + return result; + } + // noinspection JSUnusedGlobalSymbols -- Used in the template below. + getChoiceChild(value) { + return this.childByValue.get(value) || null; + } + // noinspection JSUnusedGlobalSymbols -- Used in the template. + /** + * Get the ID attribute for a radio input. + * + * Note: This must match the algorithm used by the PHP version of this control + * to work correctly with the BorderStyleSelector control that adds style samples + * to each choice and uses the ID to link them to the inputs (so that clicking + * the sample selects the option). + */ + getRadioInputId(choice) { + let sanitizedValue = (choice.value !== null) ? choice.value.toString() : ''; + //Emulate the sanitize_key() function from WordPress core. + sanitizedValue = sanitizedValue.toLowerCase().replace(/[^a-z0-9_\-]/gi, ''); + return this.radioInputPrefix + sanitizedValue; + } +} +const choiceTemplate = ` + +`; +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeRadioGroup, ` +
    + + + ${choiceTemplate}
    + + +

    ${choiceTemplate}

    + + + ${choiceTemplate} + + + + + +
    +`)); +//# sourceMappingURL=ame-radio-group.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-select-box/ame-select-box.js": +/*!*********************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-select-box/ame-select-box.js ***! + \*********************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _ame_choice_control_ame_choice_control_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../ame-choice-control/ame-choice-control.js */ "./extras/pro-customizables/ko-components/ame-choice-control/ame-choice-control.js"); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + + +class AmeSelectBox extends _ame_choice_control_ame_choice_control_js__WEBPACK_IMPORTED_MODULE_0__.AmeChoiceControl { + constructor(params, $element) { + super(params, $element); + } + get classes() { + return ['ame-select-box-control', ...super.classes]; + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_1__.createControlComponentConfig)(AmeSelectBox, ` + + + + +`)); +//# sourceMappingURL=ame-select-box.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-sibling-description/ame-sibling-description.js": +/*!***************************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-sibling-description/ame-sibling-description.js ***! + \***************************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); +/* harmony import */ var _ame_description_ame_description_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ame-description/ame-description.js */ "./extras/pro-customizables/ko-components/ame-description/ame-description.js"); + + +/** + * A simple component that displays the description of a UI element. + * + * This should be rendered as a sibling of the UI element's component, + * typically immediately after it. + * + * Caution: HTML is allowed in the description. + */ +class AmeSiblingDescription extends _ame_description_ame_description_js__WEBPACK_IMPORTED_MODULE_1__.AmeDescriptionComponent { +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createComponentConfig)(AmeSiblingDescription, ` +

    +`)); +//# sourceMappingURL=ame-sibling-description.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-static-html/ame-static-html.js": +/*!***********************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-static-html/ame-static-html.js ***! + \***********************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + +class AmeStaticHtml extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + this.containerType = 'span'; + this.htmlContent = (typeof params.html === 'string') ? params.html : ''; + if (typeof params.container === 'string') { + this.containerType = params.container; + } + } +} +//Note: The HTML content has to be in a container element because Knockout doesn't allow +//using the "html" binding with virtual elements. +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeStaticHtml, ` + +
    + + + + +`)); +//# sourceMappingURL=ame-static-html.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-text-input/ame-text-input.js": +/*!*********************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-text-input/ame-text-input.js ***! + \*********************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AmeTextInput": () => (/* binding */ AmeTextInput), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + +class AmeTextInput extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoDependentControl { + constructor(params, $element) { + super(params, $element); + this.inputType = 'text'; + this.isCode = params.isCode || false; + this.inputType = params.inputType || 'text'; + } + get inputClasses() { + const classes = ['regular-text']; + if (this.isCode) { + classes.push('code'); + } + classes.push('ame-text-input-control', ...super.inputClasses); + return classes; + } + getAdditionalInputAttributes() { + return Object.assign({ 'type': this.inputType }, super.getAdditionalInputAttributes()); + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeTextInput, ` + + + + +`)); +//# sourceMappingURL=ame-text-input.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-toggle-checkbox/ame-toggle-checkbox.js": +/*!*******************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-toggle-checkbox/ame-toggle-checkbox.js ***! + \*******************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + +class AmeToggleCheckbox extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + this.onValue = (typeof params.onValue !== 'undefined') ? params.onValue : true; + this.offValue = (typeof params.offValue !== 'undefined') ? params.offValue : false; + if (typeof this.settings['value'] === 'undefined') { + this.isChecked = ko.pureComputed(() => false); + } + else { + this.isChecked = ko.computed({ + read: () => { + return this.settings.value.value() === ko.unwrap(this.onValue); + }, + write: (newValue) => { + this.settings.value.value(ko.unwrap(newValue ? this.onValue : this.offValue)); + }, + deferEvaluation: true + }); + } + } + get classes() { + return ['ame-toggle-checkbox-control', ...super.classes]; + } +} +//Unlike the HTML version of this control, the Knockout version doesn't have +//a second, hidden checkbox. This is because the component is entirely JS-based +//and doesn't need to be submitted as part of a form. +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeToggleCheckbox, ` + +`)); +//# sourceMappingURL=ame-toggle-checkbox.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-unit-dropdown/ame-unit-dropdown.js": +/*!***************************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-unit-dropdown/ame-unit-dropdown.js ***! + \***************************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AmeUnitDropdown": () => (/* binding */ AmeUnitDropdown), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + +class AmeUnitDropdown extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + this.dropdownData = params.optionData || { + options: [], + optionsText: 'text', + optionsValue: 'value' + }; + this.selectId = params.id || ''; + } +} +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeUnitDropdown, ` + +`)); +//# sourceMappingURL=ame-unit-dropdown.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/ame-wp-editor/ame-wp-editor.js": +/*!*******************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/ame-wp-editor/ame-wp-editor.js ***! + \*******************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _control_base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../control-base.js */ "./extras/pro-customizables/ko-components/control-base.js"); + + +//Note: Requires Lodash, but does not explicitly import it because this plugin +//already uses Lodash as a global variable (wsAmeLodash) in many places. Code +//that uses this component should make sure that Lodash is loaded. +let autoAssignedIdCounter = 0; +/** + * List of visual editor buttons that are visible in the "teeny" mode. + * + * Found in /wp-includes/class-wp-editor.php, the editor_settings() method. + * The relevant code is around line #601 (as of WP 6.1.1). + */ +const TeenyButtons = [ + 'bold', + 'italic', + 'underline', + 'blockquote', + 'strikethrough', + 'bullist', + 'numlist', + 'alignleft', + 'aligncenter', + 'alignright', + 'undo', + 'redo', + 'link', + 'fullscreen' +]; +/** + * List of Quicktags editor buttons that are visible by default. + * + * The default list of text editor buttons used by wp.editor.initialize() + * doesn't match the defaults used by wp_editor() in PHP. Let's copy the list + * from /includes/class-wp-editor.php. + */ +const DefaultQuicktagsButtons = [ + 'strong', 'em', 'link', 'block', 'del', 'ins', 'img', 'ul', 'ol', 'li', 'code', 'more', 'close' +]; +class AmeWpEditor extends _control_base_js__WEBPACK_IMPORTED_MODULE_0__.KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + this.editorId = null; + this.isWpEditorInitialized = false; + const textSetting = this.settings.value; + if (typeof textSetting === 'undefined') { + throw new Error('Visual Editor control is missing the required setting'); + } + this.rows = params.rows || 6; + this.isTeeny = !!params.teeny; + } + getAdditionalInputAttributes() { + return Object.assign({ rows: this.rows.toString() }, super.getAdditionalInputAttributes()); + } + koDescendantsComplete() { + const $textArea = this.findChild('textarea.ame-wp-editor-textarea'); + if ($textArea.length === 0) { + return; + } + const currentValue = this.valueProxy(); + $textArea.val((currentValue === null) ? '' : currentValue.toString()); + //The textarea must have an ID for wp.editor.initialize() to work. + { + let editorId = $textArea.attr('id'); + if (!editorId) { + editorId = 'ws-ame-wp-editor-aid-' + (autoAssignedIdCounter++); + $textArea.attr('id', editorId); + } + this.editorId = editorId; + } + //Update the setting when the contents of the underlying textarea change. + //This happens when the user selects the "Text" tab in the editor, or when + //TinyMCE is unavailable (e.g. if the "Disable the visual editor when writing" + //option is checked in the user's profile). + $textArea.on('change input', this.throttleUpdates(() => $textArea.val())); + let editorSettings = { + tinymce: { + wpautop: true + }, + quicktags: { + //The default list of text editor buttons used by wp.editor.initialize() + //doesn't match the defaults used by wp_editor() in PHP. Let's copy the list + //from /includes/class-wp-editor.php. + buttons: DefaultQuicktagsButtons.join(','), + }, + //Include the "Add Media" button. + mediaButtons: true, + }; + if (typeof window['tinymce'] === 'undefined') { + //TinyMCE is disabled or not available. + editorSettings.tinymce = false; + } + if (this.isTeeny && (typeof editorSettings.tinymce === 'object')) { + editorSettings.tinymce.toolbar1 = TeenyButtons.join(','); + editorSettings.tinymce.toolbar2 = ''; + } + const $document = jQuery(document); + const self = this; + //After the editor finishes initializing, add an event listener to update + //the setting when the contents of the visual editor change. + $document.on('tinymce-editor-init', function addMceChangeListener(event, editor) { + if (editor.id !== self.editorId) { + return; //Not our editor. + } + //According to the TinyMCE documentation, the "Change" event is fired + //when "changes [...] cause an undo level to be added". This could be + //too frequent for our purposes, so we'll throttle the callback. + editor.on('Change', self.throttleUpdates(() => editor.getContent())); + $document.off('tinymce-editor-init', addMceChangeListener); + }); + //Unfortunately, as of WP 6.2-beta, wp.editor.initialize() doesn't add + //the "wp-editor-container" wrapper when only the Quicktags editor is used. + //This means the editor won't be styled correctly. Let's fix that. + $document.on('quicktags-init', function maybeAddEditorWrapper(event, editor) { + if (!editor || (editor.id !== self.editorId)) { + return; + } + if (editor.canvas) { + const $textarea = jQuery(editor.canvas); + const $wrapper = $textarea.closest('.wp-editor-container'); + if ($wrapper.length === 0) { + //Also include the toolbar in the wrapper. + const $toolbar = $textarea.prevAll('.quicktags-toolbar').first(); + $textarea.add($toolbar).wrapAll('
    '); + } + } + $document.off('quicktags-init', maybeAddEditorWrapper); + }); + //Finally, initialize the editor. + wp.editor.initialize($textArea.attr('id'), editorSettings); + this.isWpEditorInitialized = true; + } + /** + * Create a throttled function that updates the setting. + * + * There are multiple ways to get the contents of the editor (e.g. TinyMCE mode + * vs a plain textarea), so using a utility function helps avoid code duplication. + * + * @param valueGetter + * @protected + */ + throttleUpdates(valueGetter) { + const textSetting = this.settings.value; + return wsAmeLodash.throttle(function () { + textSetting.value(valueGetter()); + return void 0; + }, 1000, { leading: true, trailing: true }); + } + dispose() { + //Destroy the editor. It's not clear whether this is necessary, but it's + //probably a good idea to give WP a chance to clean up. + if (this.isWpEditorInitialized && (this.editorId !== null)) { + wp.editor.remove(this.editorId); + this.isWpEditorInitialized = false; + } + super.dispose(); + } +} +//Note: The class of the textarea element is set directly instead of using a binding +//because it must always have the "wp-editor-area" class for it to render correctly +//(apparently, wp.editor.initialize() does not automatically add that class). +//Knockout should not be able to remove the class. +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_control_base_js__WEBPACK_IMPORTED_MODULE_0__.createControlComponentConfig)(AmeWpEditor, ` + +`)); +//# sourceMappingURL=ame-wp-editor.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/control-base.js": +/*!****************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/control-base.js ***! + \****************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ComponentBindingOptions": () => (/* binding */ ComponentBindingOptions), +/* harmony export */ "KoComponentViewModel": () => (/* binding */ KoComponentViewModel), +/* harmony export */ "KoContainerViewModel": () => (/* binding */ KoContainerViewModel), +/* harmony export */ "KoControlViewModel": () => (/* binding */ KoControlViewModel), +/* harmony export */ "KoDependentControl": () => (/* binding */ KoDependentControl), +/* harmony export */ "KoRendererViewModel": () => (/* binding */ KoRendererViewModel), +/* harmony export */ "KoStandaloneControl": () => (/* binding */ KoStandaloneControl), +/* harmony export */ "createComponentConfig": () => (/* binding */ createComponentConfig), +/* harmony export */ "createControlComponentConfig": () => (/* binding */ createControlComponentConfig), +/* harmony export */ "createRendererComponentConfig": () => (/* binding */ createRendererComponentConfig) +/* harmony export */ }); +/* harmony import */ var _assets_customizable_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../assets/customizable.js */ "./extras/pro-customizables/assets/customizable.js"); + +var Setting = _assets_customizable_js__WEBPACK_IMPORTED_MODULE_0__.AmeCustomizable.Setting; +var InterfaceStructure = _assets_customizable_js__WEBPACK_IMPORTED_MODULE_0__.AmeCustomizable.InterfaceStructure; +var Control = _assets_customizable_js__WEBPACK_IMPORTED_MODULE_0__.AmeCustomizable.Control; +class KoComponentViewModel { + constructor(params, $element) { + var _a; + this.params = params; + this.$element = $element; + this.isBoundToComment = ($element[0]) && ($element[0].nodeType === Node.COMMENT_NODE); + this.uiElement = null; + const expectedType = this.getExpectedUiElementType(); + if (expectedType !== null) { + if ((typeof params.uiElement !== 'undefined') + && (params.uiElement instanceof expectedType)) { + this.uiElement = params.uiElement; + } + else { + throw new Error('uiElement is not a ' + expectedType.name + ' instance.'); + } + } + else if ((typeof params.uiElement !== 'undefined') && !(this instanceof KoStandaloneControl)) { + console.warn('Unexpected "uiElement" parameter for ' + this.constructor.name + + ' that did not expect an UI element. Did you forget to override getExpectedUiElementType() ?', params.uiElement); + } + if (typeof params.children !== 'undefined') { + if (Array.isArray(params.children) || this.isObservableArray(params.children)) { + this.inputChildren = params.children; + } + else { + throw new Error('Invalid "children" parameter: expected an array or an observable array.'); + } + } + else { + this.inputChildren = []; + } + this.customClasses = ((typeof params.classes === 'object') && Array.isArray(params.classes)) ? params.classes : []; + this.customStyles = ((typeof params.styles === 'object') && (params.styles !== null)) ? params.styles : {}; + if (typeof params.enabled !== 'undefined') { + if (ko.isObservable(params.enabled)) { + this.isEnabled = params.enabled; + } + else { + this.isEnabled = ko.pureComputed(() => !!params.enabled); + } + } + else { + this.isEnabled = ko.pureComputed(() => true); + } + //Get the description either from the "description" parameter or from the UI element. + this.description = params.description + ? ko.unwrap(params.description.toString()) + : (((_a = this.uiElement) === null || _a === void 0 ? void 0 : _a.description) || ''); + } + dispose() { + //Does nothing by default. + } + getExpectedUiElementType() { + return null; + } + get classes() { + return [].concat(this.customClasses); + } + // noinspection JSUnusedGlobalSymbols -- Used in Knockout templates. + get classString() { + return this.classes.join(' '); + } + get styles() { + return Object.assign({}, this.customStyles); + } + findChild(selector, allowSiblingSearch = null) { + if (allowSiblingSearch === null) { + //Enable only if the component is bound to a comment (i.e. ""). + allowSiblingSearch = this.isBoundToComment; + } + if (this.isBoundToComment) { + if (allowSiblingSearch) { + return this.$element.nextAll(selector).first(); + } + else { + //We would never find anything because a comment node has no children. + return jQuery(); + } + } + return this.$element.find(selector); + } + isObservableArray(value) { + return (typeof value === 'object') + && (value !== null) + && (typeof value.slice === 'function') + && (typeof value.indexOf === 'function') + && (ko.isObservable(value)); + } +} +function makeCreateVmFunctionForComponent(ctor) { + return function (params, componentInfo) { + const $element = jQuery(componentInfo.element); + return new ctor(params, $element); + }; +} +function createComponentConfig(ctor, templateString) { + return { + viewModel: { + createViewModel: makeCreateVmFunctionForComponent(ctor), + }, + template: templateString, + }; +} +//endregion +//region Container +class ComponentBindingOptions { + // noinspection JSUnusedGlobalSymbols -- the uiElement property is used in the KO template of AC control groups. + constructor(name, params, uiElement) { + this.name = name; + this.params = params; + this.uiElement = uiElement; + if (name === '') { + throw new Error('Component name cannot be empty.'); + } + } + static fromElement(element, overrideComponentName = null) { + if (!element.component && (overrideComponentName === null)) { + throw new Error(`Cannot create component binding options for UI element "${element.id}" without a component name.`); + } + return new ComponentBindingOptions(overrideComponentName || element.component, element.getComponentParams(), element); + } +} +class KoContainerViewModel extends KoComponentViewModel { + constructor(params, $element) { + if (typeof params.children === 'undefined') { + throw new Error('Missing "children" parameter.'); + } + super(params, $element); + this.title = ko.pureComputed(() => { + if (typeof params.title !== 'undefined') { + let title = ko.unwrap(params.title); + if ((title !== null) && (typeof title !== 'undefined')) { + return title.toString(); + } + } + if (this.uiElement) { + return this.uiElement.title; + } + return ''; + }); + this.childComponents = ko.pureComputed(() => { + const result = ko.unwrap(this.inputChildren) + .map(child => this.mapChildToComponentBinding(child)) + .filter(binding => binding !== null); + //TypeScript does not recognize that the filter() call above removes + //all null values, so we need an explicit cast. + return result; + }); + } + mapChildToComponentBinding(child) { + //Does not map any children by default. + return null; + } + dispose() { + super.dispose(); + this.childComponents.dispose(); + } +} +//endregion +//region Control +class KoControlViewModel extends KoComponentViewModel { + constructor(params, $element) { + var _a; + super(params, $element); + this.settings = + ((typeof params.settings === 'object') && isSettingMap(params.settings)) + ? params.settings + : {}; + if (typeof this.settings.value !== 'undefined') { + this.valueProxy = this.settings.value.value; + } + else { + this.valueProxy = ko.pureComputed(() => { + console.error('Missing "value" setting for a control component.', this.settings, params); + return ''; + }); + } + //Input ID will be provided by the server if applicable. + this.primaryInputId = (typeof params.primaryInputId === 'string') ? params.primaryInputId : null; + this.inputAttributes = ko.pureComputed(() => { + var _a; + const attributes = ((_a = this.uiElement) === null || _a === void 0 ? void 0 : _a.inputAttributes) || {}; + const inputId = this.getPrimaryInputId(); + if (inputId !== null) { + attributes.id = inputId; + } + //Note: The "name" field is not used because these controls are entirely JS-driven. + const additionalAttributes = this.getAdditionalInputAttributes(); + for (const key in additionalAttributes) { + if (!additionalAttributes.hasOwnProperty(key)) { + continue; + } + attributes[key] = additionalAttributes[key]; + } + return attributes; + }); + if ((typeof params.label !== 'undefined') && (params.label !== null)) { + const unwrappedLabel = ko.unwrap(params.label); + this.label = (typeof unwrappedLabel === 'undefined') ? '' : unwrappedLabel.toString(); + } + else { + this.label = ((_a = this.uiElement) === null || _a === void 0 ? void 0 : _a.label) || ''; + } + } + get inputClasses() { + var _a; + return ((_a = this.uiElement) === null || _a === void 0 ? void 0 : _a.inputClasses) || []; + } + // noinspection JSUnusedGlobalSymbols -- Used in Knockout templates. + get inputClassString() { + return this.inputClasses.join(' '); + } + getAdditionalInputAttributes() { + return {}; + } + getPrimaryInputId() { + return this.primaryInputId; + } +} +function isSettingMap(value) { + if (value === null) { + return false; + } + if (typeof value !== 'object') { + return false; + } + const valueAsRecord = value; + for (const key in valueAsRecord) { + if (!valueAsRecord.hasOwnProperty(key)) { + continue; + } + if (!(valueAsRecord[key] instanceof Setting)) { + return false; + } + } + return true; +} +/** + * A control that doesn't use or need a UI element instance, but can still have + * settings and other parameters typically associated with controls. + */ +class KoStandaloneControl extends KoControlViewModel { +} +/** + * A control that requires a UI element of the "Control" class. + */ +class KoDependentControl extends KoControlViewModel { + getExpectedUiElementType() { + return Control; + } +} +function createControlComponentConfig(ctor, templateString) { + return { + viewModel: { + createViewModel: makeCreateVmFunctionForComponent(ctor), + }, + template: templateString, + }; +} +//endregion +//region Renderer +class KoRendererViewModel extends KoComponentViewModel { + constructor(params, $element) { + super(params, $element); + if ((typeof params.structure !== 'object') || !(params.structure instanceof InterfaceStructure)) { + throw new Error('Invalid interface structure for a renderer component.'); + } + this.structure = params.structure; + } +} +function createRendererComponentConfig(ctor, templateString) { + return { + viewModel: { + createViewModel: makeCreateVmFunctionForComponent(ctor), + }, + template: templateString, + }; +} +//endregion +//# sourceMappingURL=control-base.js.map + +/***/ }), + +/***/ "./extras/pro-customizables/ko-components/lazy-popup-slider-adapter.js": +/*!*****************************************************************************!*\ + !*** ./extras/pro-customizables/ko-components/lazy-popup-slider-adapter.js ***! + \*****************************************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "LazyPopupSliderAdapter": () => (/* binding */ LazyPopupSliderAdapter) +/* harmony export */ }); +/// +/** + * This is a wrapper for the popup slider that initializes the slider on first use. + * It's useful for Knockout components. + */ +class LazyPopupSliderAdapter { + constructor(sliderRanges, containerSelector = '.ame-container-with-popup-slider', inputSelector = 'input', sliderOptions = {}) { + this.sliderRanges = sliderRanges; + this.containerSelector = containerSelector; + this.inputSelector = inputSelector; + this.sliderOptions = sliderOptions; + this.slider = null; + if (!sliderOptions.hasOwnProperty('ranges')) { + sliderOptions.ranges = sliderRanges; + } + this.handleKoClickEvent = ($data, event) => { + //Verify that this is one of the inputs we're interested in. + //Also, disabled inputs should not trigger the slider. + if (event.target === null) { + return; + } + const $input = jQuery(event.target); + if ($input.is(':disabled') || !$input.is(this.inputSelector)) { + return; + } + //Short-circuit if the slider is already initialized. + if (this.slider) { + this.slider.showForInput($input); + return; + } + //Some sanity checks. + if (typeof AmePopupSlider === 'undefined') { + return; + } + const $container = $input.closest(this.containerSelector); + if ($container.length < 1) { + return; + } + this.initSlider($container); + if (this.slider !== null) { + //TS doesn't realize that this.initSlider() will initialize the slider. + this.slider.showForInput($input); + } + }; + } + /** + * Initialize the slider if it's not already initialized. + */ + initSlider($container) { + if (this.slider) { + return; + } + //In HTML, we would pass the range data as a "data-slider-ranges" attribute, + //but here they are passed via the "ranges" option (see the constructor). + this.slider = AmePopupSlider.createSlider($container, this.sliderOptions); + } +} +//# sourceMappingURL=lazy-popup-slider-adapter.js.map + +/***/ }) + +}, +/******/ __webpack_require__ => { // webpackRuntimeModules +/******/ var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId)) +/******/ __webpack_require__.O(0, ["customizable"], () => (__webpack_exec__("./extras/modules/admin-customizer/admin-customizer.ts"))); +/******/ var __webpack_exports__ = __webpack_require__.O(); +/******/ } +]); +//# sourceMappingURL=admin-customizer.bundle.js.map \ No newline at end of file diff --git a/dist/admin-customizer.bundle.js.map b/dist/admin-customizer.bundle.js.map new file mode 100644 index 0000000..877d9e3 --- /dev/null +++ b/dist/admin-customizer.bundle.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-customizer.bundle.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,IAAI;AAC7D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,oBAAoB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,gCAAgC;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,8BAA8B;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qDAAqD;AACrD;AACA,0DAA0D;AAC1D,iCAAiC;AACjC;AACA;AACA;AACA;AACA;AACA;AACA,qDAAqD;AACrD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sCAAsC,eAAe,aAAa,eAAe;AACjF;AACA;AACA;AACA,yDAAyD,2DAA2D;AACpH;AACA;AACA,wDAAwD,kCAAkC;AAC1F;AACA;AACA;AACA;AACA;AACA,+DAA+D,+BAA+B;AAC9F;AACA;AACA,sDAAsD,+BAA+B,cAAc,eAAe;AAClH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,8DAA8D,0BAA0B;AACxF;AACA,qCAAqC,SAAS,oDAAoD,0BAA0B;AAC5H;AACA;AACA;AACA,iEAAiE,4BAA4B;AAC7F;AACA;AACA,+DAA+D,0BAA0B;AACzF;AACA;AACA;AACA;AACA;AACA;AACA,qCAAqC,iBAAiB;AACtD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,gDAAgD,sEAAsE,EAAE,eAAe;AACvI;AACA,iDAAiD,iEAAiE,EAAE,eAAe;AACnI;AACA,4CAA4C;AAC5C;AACA;AACA;AACA,0CAA0C,EAAE,cAAc;AAC1D;AACA,0CAA0C;AAC1C;AACA;AACA;AACA,0CAA0C,EAAE,gCAAgC;AAC5E;AACA;AACA;AACA;AACA;AACA,gDAAgD,qEAAqE,EAAE,eAAe;AACtI;AACA,iDAAiD,iEAAiE,EAAE,eAAe;AACnI;AACA,4CAA4C;AAC5C;AACA;AACA;AACA,uCAAuC,EAAE,cAAc;AACvD;AACA,4CAA4C;AAC5C;AACA;AACA;AACA,uCAAuC,EAAE,cAAc;AACvD;AACA,0CAA0C;AAC1C;AACA;AACA;AACA,0CAA0C,EAAE,gCAAgC;AAC5E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sDAAsD,iBAAiB;AACvE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,YAAY,mCAAmC;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,wCAAwC,kCAAkC;AAC1E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,aAAa;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA,CAAC;AACD,4BAA4B,wBAAwB;AACpD,yBAAyB,wBAAwB;AACjD;AACA;AACA;AACA;;AAEA;AACA;AACA,sEAAsE,UAAU;AAChF;AACA,CAAC,8BAA8B;;AAE/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,4DAA4D;AACxE;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA,qBAAqB;AACrB;AACA,qBAAqB;AACrB;AACA,iBAAiB;AACjB;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA,yCAAyC,mCAAmC;AAC5E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA,+CAA+C,mCAAmC;AAClF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yBAAyB;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA,sBAAsB,gCAAgC;AACtD,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,8BAA8B;AACpD,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4BAA4B,GAAG;AAC/B;AACA,sCAAsC,GAAG;AACzC,8BAA8B,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,GAAG;AACtF;AACA;AACA,4DAA4D,GAAG,mFAAmF,GAAG;AACrJ;AACA,sCAAsC,sBAAsB,sCAAsC,uBAAuB,OAAO,GAAG,cAAc;AACjJ;AACA,qCAAqC,yBAAyB,4DAA4D,EAAE,SAAS,IAAI,MAAM,EAAE,iCAAiC,EAAE,SAAS,IAAI,yBAAyB,IAAI,GAAG,EAAE,aAAa,IAAI,GAAG,IAAI,WAAW,IAAI,GAAG,EAAE,WAAW,IAAI,GAAG,IAAI,WAAW,IAAI,GAAG,EAAE,WAAW,IAAI,GAAG,IAAI,WAAW,IAAI,GAAG,EAAE,WAAW,IAAI,GAAG,IAAI,WAAW,IAAI,GAAG,EAAE,WAAW,IAAI,GAAG,IAAI,WAAW,IAAI,GAAG,EAAE,WAAW,IAAI,GAAG,IAAI,WAAW,IAAI,mCAAmC,EAAE,SAAS,IAAI,MAAM,EAAE,iCAAiC,EAAE,SAAS,IAAI,0DAA0D,GAAG;AACnoB;AACA,yBAAyB,sBAAsB,IAAI,gBAAgB;AACnE,sDAAsD,EAAE,SAAS,IAAI,MAAM,EAAE,iCAAiC,EAAE,SAAS,IAAI;AAC7H,+BAA+B,IAAI,GAAG,EAAE,aAAa,IAAI,GAAG,IAAI,WAAW,IAAI,GAAG,EAAE,WAAW,IAAI,GAAG,IAAI,WAAW,IAAI,GAAG,EAAE,WAAW,IAAI,GAAG,IAAI,WAAW,IAAI,GAAG,EAAE,WAAW,IAAI,GAAG,IAAI,WAAW,IAAI,GAAG,EAAE,WAAW,IAAI,GAAG,IAAI,WAAW,IAAI,GAAG,EAAE,WAAW,IAAI,GAAG,IAAI,WAAW,IAAI,mCAAmC,EAAE,SAAS,IAAI,MAAM,EAAE,iCAAiC,EAAE,SAAS,IAAI;AAC3Y;AACA;AACA;AACA;AACA,oCAAoC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,gBAAgB,UAAU,EAAE,OAAO,EAAE;AACvH;AACA;AACA,oCAAoC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,gBAAgB;AAClG;AACA;AACA;AACA;AACA,oCAAoC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE;AAC9F;AACA;AACA,oCAAoC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;AACzE;AACA;AACA;AACA;AACA,oCAAoC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE;AACxG;AACA;AACA,oCAAoC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;AACzE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA,4CAA4C,cAAc;AAC1D,SAAS;AACT;AACA;AACA,4CAA4C,qBAAqB;AACjE,SAAS;AACT;AACA;AACA,4CAA4C,qBAAqB;AACjE,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yBAAyB;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yBAAyB;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sCAAsC,iDAAiD;AACvF;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sCAAsC,yBAAyB;AAC/D;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sCAAsC,uBAAuB;AAC7D;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA,gCAAgC,+CAA+C;AAC/E;AACA;AACA,gCAAgC,6CAA6C;AAC7E;AACA;AACA,gCAAgC,+CAA+C;AAC/E;AACA;AACA,gCAAgC,8CAA8C;AAC9E;AACA;AACA,gCAAgC,8CAA8C;AAC9E;AACA;AACA,gCAAgC,+CAA+C;AAC/E;AACA;AACA,gCAAgC,8CAA8C;AAC9E;AACA;AACA,gCAAgC,4CAA4C;AAC5E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,gBAAgB,cAAc;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA,aAAa;AACb;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yBAAyB,wDAAwD;AACjF,SAAS;AACT;AACA;AACA;AACA;AACA,yBAAyB,wDAAwD;AACjF,SAAS;AACT;AACA;AACA;AACA;AACA,2BAA2B,kDAAkD;AAC7E,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAgB;AAChB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,aAAa;AAC9C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA,gBAAgB,cAAc;AAC9B,gBAAgB,yBAAyB;AACzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uBAAuB,6BAA6B;AACpD;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA,+BAA+B,6BAA6B;AAC5D,iCAAiC,uCAAuC;AACxE,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,2BAA2B,6BAA6B;AACxD;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA,aAAa;AACb;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA,oBAAoB;AACpB,SAAS;AACT;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc;AACd,YAAY;AACZ;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU;AACV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU;AACV;AACA;AACA;AACA,8BAA8B,eAAe;AAC7C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU;AACV;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,gBAAgB,MAAM;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA,8BAA8B;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAgB,MAAM;AACtB;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mEAAmE,cAAc;AACjF;AACA;AACA;AACA,8DAA8D,uBAAuB,sBAAsB,cAAc;AACzH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA,yBAAyB;AACzB;AACA;AACA;AACA,yBAAyB;AACzB;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA,4BAA4B,kBAAkB;AAC9C;AACA;AACA;AACA;AACA,yBAAyB;AACzB;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA,gBAAgB,cAAc;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,gBAAgB,cAAc;AAC9B;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT,iCAAiC;AACjC;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAgB,cAAc;AAC9B;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA,gBAAgB,cAAc;AAC9B;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yBAAyB;AACzB,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,gBAAgB,cAAc;AAC9B;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uBAAuB,sDAAsD;AAC7E,SAAS;AACT;AACA;AACA;AACA;AACA,uBAAuB,sDAAsD;AAC7E,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAgB,MAAM;AACtB;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB,aAAa;AACb;AACA,yBAAyB;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAgB,MAAM;AACtB;AACA,mCAAmC,6CAA6C;AAChF;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA,gBAAgB,MAAM;AACtB;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAgB,cAAc;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA,yBAAyB;AACzB;AACA;AACA;AACA,mCAAmC,6CAA6C;AAChF;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC;AACjC,qBAAqB;AACrB,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA,yBAAyB;AACzB;AACA;AACA;AACA,mCAAmC,6CAA6C;AAChF;AACA;AACA;AACA,uGAAuG,qCAAqC;AAC5I,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,kBAAkB,2CAA2C;AAC7D;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,gBAAgB,MAAM;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,gBAAgB,MAAM;AACtB;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,6BAA6B;AAC7B;AACA,yBAAyB;AACzB;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yBAAyB;AACzB;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,gBAAgB,MAAM;AACtB;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAgB,cAAc;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA,kCAAkC;AAClC;AACA;AACA;AACA;AACA;AACA;AACA,gDAAgD,cAAc;AAC9D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4BAA4B;AAC5B;AACA;AACA,qDAAqD,aAAa;AAClE,+BAA+B,sCAAsC;AACrE;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,sDAAsD;AACvD;AACA;AACA;AACA,sCAAsC,SAAS;AAC/C,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yCAAyC,sBAAsB;AAC/D,yCAAyC,sBAAsB;AAC/D;AACA;AACA;AACA,KAAK;AACL,yCAAyC,sBAAsB;AAC/D,qCAAqC,sBAAsB;AAC3D;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,cAAc;AAChC,wBAAwB,oBAAoB;AAC5C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mCAAmC,+BAA+B;AAClE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;;AAE+pD;;;;;;;;;;;;;;;;;;;;;;;;;;;ACh3HnpD;AAEb,gDAAgD;AAChD,qDAAqD;AACrD,+CAA+C;AAE0D;AACV;AAC9B;AACJ;AACS;AACT;AACW;AACI;AACV;AACD;AACe;AACpB;AAKrD,IAAU,kBAAkB,CAw3DlC;AAx3DD,WAAiB,kBAAkB;IAElC,IAAO,iBAAiB,GAAG,wGAAiC,CAAC;IAG7D,IAAO,oBAAoB,GAAG,2GAAoC,CAAC;IACnE,IAAO,kBAAkB,GAAG,yGAAkC,CAAC;IAI/D,MAAM,CAAC,GAAG,MAAM,CAAC;IACjB,MAAM,CAAC,GAAG,WAAW,CAAC;IAEtB,0GAAsB,EAAE,CAAC;IACzB,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,kBAAkB,EAAE,0EAAc,CAAC,CAAC;IAC3D,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,gBAAgB,EAAE,wEAAY,CAAC,CAAC;IACvD,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,qBAAqB,EAAE,6EAAgB,CAAC,CAAC;IAChE,E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A;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA,SAAS;AACT;AACA;AACA,iEAAe,uGAAqB;AACpC;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;ACtCgJ;AAC5D;AACpF,mBAAmB,mGAA4B;AAC/C,gCAAgC,kGAAoB;AACpD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mBAAmB,iHAAmC;AACtD;AACA;AACA;AACA;AACA,iEAAe,uGAAqB;AACpC;AACA;AACA;AACA;AACA,mCAAmC,cAAc;AACjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;AC9CqH;AACjC;AACpF,cAAc,8FAAuB;AACrC;AACA;AACA,iCAAiC,gBAAgB,KAAK,aAAa;AACnE;AACA;AACA;AACA,2BAA2B,gGAAkB;AAC7C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,uGAAqB;AACpC;AACA;AACA;AACA,sEAAsE,gBAAgB;AACtF;AACA;AACA;AACA;AACA;AACA,sBAAsB,mEAAmE;AACzF;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;;ACvCuH;AACnC;AACpF,cAAc,8FAAuB;AACc;AACnD,+BAA+B,kGAAoB;AACnD;AACA;AACA;AACA;AACA;AACA;AACA,+BAA+B,gFAAgC;AAC/D;AACA;AACA;AACA;AACA;AACA,iEAAe,uGAAqB;AACpC,mDAAmD,mCAAmC;AACtF;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;;ACtBgJ;AAC5D;AACpF,cAAc,8FAAuB;AACrC,cAAc,8FAAuB;AACrC,mBAAmB,mGAA4B;AACxC,2BAA2B,kGAAoB;AACtD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uBAAuB,iHAAmC;AAC1D;AACA;AACA,uBAAuB,iHAAmC;AAC1D;AACA;AACA;AACA,mBAAmB,iHAAmC;AACtD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mBAAmB,iHAAmC;AACtD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,uGAAqB;AACpC,8CAA8C,cAAc;AAC5D;AACA;AACA;AACA;AACA;AACA,sDAAsD,oDAAoD;AAC1G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;ACzGsH;AACtH,6BAA6B,iGAAmB;AAChD;AACA;AACA;AACA;AACA,iEAAe,uGAAqB;AACpC;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;ACT8H;AAC1C;AACpF,cAAc,8FAAuB;AACrC,6BAA6B,iGAAmB;AAChD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,+GAA6B;AAC5C;AACA,sBAAsB,4DAA4D;AAClF;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;ACrCsH;AACtH,oCAAoC,iGAAmB;AACvD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,uGAAqB;AACpC;AACA;AACA,6EAA6E,qBAAqB;AAClG;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;AC5Ba;AAC0E;AACd;AACzE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,+BAA+B,iEAAmB;AAClD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4DAA4D,cAAc;AAC1E;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA,aAAa,WAAW,yBAAyB;AACjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,6CAA6C;AAC7C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA,aAAa,WAAW,yBAAyB;AACjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,iFAAsB;AACvD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,6BAA6B,0CAA0C;AACvE;AACA;AACA;AACA;AACA,yEAAyE,mBAAmB;AAC5F;AACA;AACA;AACA,2DAA2D,uBAAuB;AAClF;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;ACrSyD;AAClD;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,+BAA+B,iEAAmB;AACzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACnBa;AAC0E;AACvF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4BAA4B,iEAAmB;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,0CAA0C,2BAA2B;AAC3F;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;AChCuF;AACvF;AACA;AACA;AACA;AACA;AACA;AACA,6BAA6B,iEAAmB;AAChD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc;AACd;AACA,wBAAwB;AACxB;AACA;AACA;AACA;AACA,oBAAoB;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3CA;AACA;AACA;AACA;AAC0E;AACN;AACc;AACR;AACN;AACkB;AACP;AACd;AACH;AAC2B;AACxB;AACH;AACe;AACN;AACZ;AACiC;AAC3B;AACjE;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA,iDAAiD,iFAAgB;AACjE,+CAA+C,6EAAc;AAC7D,oDAAoD,uFAAkB;AACtE,iDAAiD,iFAAgB;AACjE,+CAA+C,6EAAc;AAC7D,qDAAqD,yFAAoB;AACzE,mDAAmD,qFAAiB;AACpE,8CAA8C,2EAAa;AAC3D,6CAA6C,yEAAY;AACzD,sDAAsD,2FAAqB;AAC3E,8CAA8C,4EAAa;AAC3D,6CAA6C,0EAAY;AACzD,kDAAkD,oFAAiB;AACnE,gDAAgD,gFAAe;AAC/D,4CAA4C,wEAAW;AACvD,uDAAuD,8FAAsB;AAC7E,8CAA8C,4EAAa;AAC3D;AACA;AACA;;;;;;;;;;;;;;;AClD0D;AAC1D;AACA;AACA;AACO,sCAAsC,kEAAoB;AACjE;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACVuF;AACvF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,6BAA6B;AAC7B,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,6BAA6B;AAC7B,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,6BAA6B;AAC7B,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,6BAA6B;AAC7B,SAAS;AACT;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0B,SAAS,IAAI,OAAO;AAC9C;AACA,mDAAmD,YAAY;AAC/D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT,KAAK;AACL;AACA,iCAAiC,iEAAmB;AACpD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA;AACA,oEAAoE,oBAAoB;AACxF;AACA;AACA;AACA;AACA,mCAAmC,wDAAwD;AAC3F;AACA;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;ACnKuF;AACvF,qCAAqC,iEAAmB;AACxD;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;ACTa;AAC0E;AACvF;AACA;AACA;AACA;AACA;AACA;AACA;AACA,+BAA+B,iEAAmB;AAClD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA,8DAA8D,wBAAwB;AACtF;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;AC/C2D;AACqB;AAChF;AACA;AACA;AACA;AACA;AACA;AACA,mCAAmC,wFAAuB;AAC1D;AACA,iEAAe,uEAAqB;AACpC;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;;ACbA;AACsF;AACvB;AAC/D,cAAc,4EAAuB;AAC9B,6BAA6B,gEAAkB;AACtD;AACA;AACA;AACA;AACA,qDAAqD,yBAAyB;AAC9E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,2EAA2E,qBAAqB;AAChG;AACA;AACA;AACA;AACA;AACA,yCAAyC,gDAAgD;AACzF;AACA;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;ACnHa;AACqD;AACa;AAC/E,gCAAgC,uFAAgB;AAChD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA,2BAA2B,mBAAmB;AAC9C;AACA;AACA;AACA,8DAA8D,2BAA2B;AACzF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;AC9Ba;AACqD;AACa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,8BAA8B;AAC/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4BAA4B,uFAAgB;AAC5C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ,wEAAwE;AAChF;AACA;AACA,UAAU,sCAAsC;AAChD;AACA;AACA,uBAAuB,yCAAyC,2BAA2B;AAC3F;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA;AACA,MAAM,gBAAgB;AACtB;AACA;AACA,SAAS,eAAe;AACxB;AACA;AACA,MAAM;AACN;AACA;AACA;AACA,2BAA2B,8CAA8C;AACzE;AACA;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;AClG+E;AACb;AAClE,2BAA2B,uFAAgB;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA;AACA,sBAAsB,0CAA0C,2BAA2B;AAC3F;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;ACjB2D;AACqB;AAChF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oCAAoC,wFAAuB;AAC3D;AACA,iEAAe,uEAAqB;AACpC;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;ACfuF;AACvF,4BAA4B,iEAAmB;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;ACrBsF;AAC/E,2BAA2B,gEAAkB;AACpD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,+BAA+B,wBAAwB;AACvD;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA,sBAAsB,0CAA0C,2BAA2B;AAC3F;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;AC1BuF;AACvF,gCAAgC,iEAAmB;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA,iBAAiB;AACjB;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA;AACA;AACA;AACA,uBAAuB,yCAAyC,2BAA2B;AAC3F;AACA;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;ACtCuF;AAChF,8BAA8B,iEAAmB;AACxD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA;AACA,SAAS,aAAa;AACtB,EAAE,EAAC;AACH;;;;;;;;;;;;;;;ACjBa;AAC0E;AACvF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0B,iEAAmB;AAC7C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,+BAA+B,4BAA4B;AAC3D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,wBAAwB;AACxB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU,+BAA+B;AAClD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe,8EAA4B;AAC3C;AACA,EAAE,EAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;ACpK4D;AAC5D,cAAc,4EAAuB;AACrC,yBAAyB,uFAAkC;AAC3D,cAAc,4EAAuB;AAC9B;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,+BAA+B;AAC/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uFAAuF,WAAW;AAClG;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;AC5RA;AACA;AACA;AACA;AACA;AACO;AACP,iIAAiI;AACjI;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","sources":["webpack:///./extras/zod/lib/index.js","webpack:///./extras/modules/admin-customizer/admin-customizer.ts","webpack:///./extras/modules/admin-customizer/admin-customizer-base.js","webpack:///./extras/modules/admin-customizer/ko-components/ame-ac-content-section.js","webpack:///./extras/modules/admin-customizer/ko-components/ame-ac-control-group.js","webpack:///./extras/modules/admin-customizer/ko-components/ame-ac-control.js","webpack:///./extras/modules/admin-customizer/ko-components/ame-ac-section-link.js","webpack:///./extras/modules/admin-customizer/ko-components/ame-ac-section.js","webpack:///./extras/modules/admin-customizer/ko-components/ame-ac-separator.js","webpack:///./extras/modules/admin-customizer/ko-components/ame-ac-structure.js","webpack:///./extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js","webpack:///./extras/pro-customizables/ko-components/ame-box-dimensions/ame-box-dimensions.js","webpack:///./extras/pro-customizables/ko-components/ame-choice-control/ame-choice-control.js","webpack:///./extras/pro-customizables/ko-components/ame-code-editor/ame-code-editor.js","webpack:///./extras/pro-customizables/ko-components/ame-color-picker/ame-color-picker.js","webpack:///./extras/pro-customizables/ko-components/ame-components.js","webpack:///./extras/pro-customizables/ko-components/ame-description/ame-description.js","webpack:///./extras/pro-customizables/ko-components/ame-font-style-picker/ame-font-style-picker.js","webpack:///./extras/pro-customizables/ko-components/ame-horizontal-separator/ame-horizontal-separator.js","webpack:///./extras/pro-customizables/ko-components/ame-image-selector/ame-image-selector.js","webpack:///./extras/pro-customizables/ko-components/ame-nested-description/ame-nested-description.js","webpack:///./extras/pro-customizables/ko-components/ame-number-input/ame-number-input.js","webpack:///./extras/pro-customizables/ko-components/ame-radio-button-bar/ame-radio-button-bar.js","webpack:///./extras/pro-customizables/ko-components/ame-radio-group/ame-radio-group.js","webpack:///./extras/pro-customizables/ko-components/ame-select-box/ame-select-box.js","webpack:///./extras/pro-customizables/ko-components/ame-sibling-description/ame-sibling-description.js","webpack:///./extras/pro-customizables/ko-components/ame-static-html/ame-static-html.js","webpack:///./extras/pro-customizables/ko-components/ame-text-input/ame-text-input.js","webpack:///./extras/pro-customizables/ko-components/ame-toggle-checkbox/ame-toggle-checkbox.js","webpack:///./extras/pro-customizables/ko-components/ame-unit-dropdown/ame-unit-dropdown.js","webpack:///./extras/pro-customizables/ko-components/ame-wp-editor/ame-wp-editor.js","webpack:///./extras/pro-customizables/ko-components/control-base.js","webpack:///./extras/pro-customizables/ko-components/lazy-popup-slider-adapter.js"],"sourcesContent":["var util;\n(function (util) {\n util.assertEqual = (val) => val;\n function assertIs(_arg) { }\n util.assertIs = assertIs;\n function assertNever(_x) {\n throw new Error();\n }\n util.assertNever = assertNever;\n util.arrayToEnum = (items) => {\n const obj = {};\n for (const item of items) {\n obj[item] = item;\n }\n return obj;\n };\n util.getValidEnumValues = (obj) => {\n const validKeys = util.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== \"number\");\n const filtered = {};\n for (const k of validKeys) {\n filtered[k] = obj[k];\n }\n return util.objectValues(filtered);\n };\n util.objectValues = (obj) => {\n return util.objectKeys(obj).map(function (e) {\n return obj[e];\n });\n };\n util.objectKeys = typeof Object.keys === \"function\" // eslint-disable-line ban/ban\n ? (obj) => Object.keys(obj) // eslint-disable-line ban/ban\n : (object) => {\n const keys = [];\n for (const key in object) {\n if (Object.prototype.hasOwnProperty.call(object, key)) {\n keys.push(key);\n }\n }\n return keys;\n };\n util.find = (arr, checker) => {\n for (const item of arr) {\n if (checker(item))\n return item;\n }\n return undefined;\n };\n util.isInteger = typeof Number.isInteger === \"function\"\n ? (val) => Number.isInteger(val) // eslint-disable-line ban/ban\n : (val) => typeof val === \"number\" && isFinite(val) && Math.floor(val) === val;\n function joinValues(array, separator = \" | \") {\n return array\n .map((val) => (typeof val === \"string\" ? `'${val}'` : val))\n .join(separator);\n }\n util.joinValues = joinValues;\n util.jsonStringifyReplacer = (_, value) => {\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n return value;\n };\n})(util || (util = {}));\nvar objectUtil;\n(function (objectUtil) {\n objectUtil.mergeShapes = (first, second) => {\n return {\n ...first,\n ...second, // second overwrites first\n };\n };\n})(objectUtil || (objectUtil = {}));\nconst ZodParsedType = util.arrayToEnum([\n \"string\",\n \"nan\",\n \"number\",\n \"integer\",\n \"float\",\n \"boolean\",\n \"date\",\n \"bigint\",\n \"symbol\",\n \"function\",\n \"undefined\",\n \"null\",\n \"array\",\n \"object\",\n \"unknown\",\n \"promise\",\n \"void\",\n \"never\",\n \"map\",\n \"set\",\n]);\nconst getParsedType = (data) => {\n const t = typeof data;\n switch (t) {\n case \"undefined\":\n return ZodParsedType.undefined;\n case \"string\":\n return ZodParsedType.string;\n case \"number\":\n return isNaN(data) ? ZodParsedType.nan : ZodParsedType.number;\n case \"boolean\":\n return ZodParsedType.boolean;\n case \"function\":\n return ZodParsedType.function;\n case \"bigint\":\n return ZodParsedType.bigint;\n case \"symbol\":\n return ZodParsedType.symbol;\n case \"object\":\n if (Array.isArray(data)) {\n return ZodParsedType.array;\n }\n if (data === null) {\n return ZodParsedType.null;\n }\n if (data.then &&\n typeof data.then === \"function\" &&\n data.catch &&\n typeof data.catch === \"function\") {\n return ZodParsedType.promise;\n }\n if (typeof Map !== \"undefined\" && data instanceof Map) {\n return ZodParsedType.map;\n }\n if (typeof Set !== \"undefined\" && data instanceof Set) {\n return ZodParsedType.set;\n }\n if (typeof Date !== \"undefined\" && data instanceof Date) {\n return ZodParsedType.date;\n }\n return ZodParsedType.object;\n default:\n return ZodParsedType.unknown;\n }\n};\n\nconst ZodIssueCode = util.arrayToEnum([\n \"invalid_type\",\n \"invalid_literal\",\n \"custom\",\n \"invalid_union\",\n \"invalid_union_discriminator\",\n \"invalid_enum_value\",\n \"unrecognized_keys\",\n \"invalid_arguments\",\n \"invalid_return_type\",\n \"invalid_date\",\n \"invalid_string\",\n \"too_small\",\n \"too_big\",\n \"invalid_intersection_types\",\n \"not_multiple_of\",\n \"not_finite\",\n]);\nconst quotelessJson = (obj) => {\n const json = JSON.stringify(obj, null, 2);\n return json.replace(/\"([^\"]+)\":/g, \"$1:\");\n};\nclass ZodError extends Error {\n constructor(issues) {\n super();\n this.issues = [];\n this.addIssue = (sub) => {\n this.issues = [...this.issues, sub];\n };\n this.addIssues = (subs = []) => {\n this.issues = [...this.issues, ...subs];\n };\n const actualProto = new.target.prototype;\n if (Object.setPrototypeOf) {\n // eslint-disable-next-line ban/ban\n Object.setPrototypeOf(this, actualProto);\n }\n else {\n this.__proto__ = actualProto;\n }\n this.name = \"ZodError\";\n this.issues = issues;\n }\n get errors() {\n return this.issues;\n }\n format(_mapper) {\n const mapper = _mapper ||\n function (issue) {\n return issue.message;\n };\n const fieldErrors = { _errors: [] };\n const processError = (error) => {\n for (const issue of error.issues) {\n if (issue.code === \"invalid_union\") {\n issue.unionErrors.map(processError);\n }\n else if (issue.code === \"invalid_return_type\") {\n processError(issue.returnTypeError);\n }\n else if (issue.code === \"invalid_arguments\") {\n processError(issue.argumentsError);\n }\n else if (issue.path.length === 0) {\n fieldErrors._errors.push(mapper(issue));\n }\n else {\n let curr = fieldErrors;\n let i = 0;\n while (i < issue.path.length) {\n const el = issue.path[i];\n const terminal = i === issue.path.length - 1;\n if (!terminal) {\n curr[el] = curr[el] || { _errors: [] };\n // if (typeof el === \"string\") {\n // curr[el] = curr[el] || { _errors: [] };\n // } else if (typeof el === \"number\") {\n // const errorArray: any = [];\n // errorArray._errors = [];\n // curr[el] = curr[el] || errorArray;\n // }\n }\n else {\n curr[el] = curr[el] || { _errors: [] };\n curr[el]._errors.push(mapper(issue));\n }\n curr = curr[el];\n i++;\n }\n }\n }\n };\n processError(this);\n return fieldErrors;\n }\n toString() {\n return this.message;\n }\n get message() {\n return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2);\n }\n get isEmpty() {\n return this.issues.length === 0;\n }\n flatten(mapper = (issue) => issue.message) {\n const fieldErrors = {};\n const formErrors = [];\n for (const sub of this.issues) {\n if (sub.path.length > 0) {\n fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];\n fieldErrors[sub.path[0]].push(mapper(sub));\n }\n else {\n formErrors.push(mapper(sub));\n }\n }\n return { formErrors, fieldErrors };\n }\n get formErrors() {\n return this.flatten();\n }\n}\nZodError.create = (issues) => {\n const error = new ZodError(issues);\n return error;\n};\n\nconst errorMap = (issue, _ctx) => {\n let message;\n switch (issue.code) {\n case ZodIssueCode.invalid_type:\n if (issue.received === ZodParsedType.undefined) {\n message = \"Required\";\n }\n else {\n message = `Expected ${issue.expected}, received ${issue.received}`;\n }\n break;\n case ZodIssueCode.invalid_literal:\n message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util.jsonStringifyReplacer)}`;\n break;\n case ZodIssueCode.unrecognized_keys:\n message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, \", \")}`;\n break;\n case ZodIssueCode.invalid_union:\n message = `Invalid input`;\n break;\n case ZodIssueCode.invalid_union_discriminator:\n message = `Invalid discriminator value. Expected ${util.joinValues(issue.options)}`;\n break;\n case ZodIssueCode.invalid_enum_value:\n message = `Invalid enum value. Expected ${util.joinValues(issue.options)}, received '${issue.received}'`;\n break;\n case ZodIssueCode.invalid_arguments:\n message = `Invalid function arguments`;\n break;\n case ZodIssueCode.invalid_return_type:\n message = `Invalid function return type`;\n break;\n case ZodIssueCode.invalid_date:\n message = `Invalid date`;\n break;\n case ZodIssueCode.invalid_string:\n if (typeof issue.validation === \"object\") {\n if (\"includes\" in issue.validation) {\n message = `Invalid input: must include \"${issue.validation.includes}\"`;\n if (typeof issue.validation.position === \"number\") {\n message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`;\n }\n }\n else if (\"startsWith\" in issue.validation) {\n message = `Invalid input: must start with \"${issue.validation.startsWith}\"`;\n }\n else if (\"endsWith\" in issue.validation) {\n message = `Invalid input: must end with \"${issue.validation.endsWith}\"`;\n }\n else {\n util.assertNever(issue.validation);\n }\n }\n else if (issue.validation !== \"regex\") {\n message = `Invalid ${issue.validation}`;\n }\n else {\n message = \"Invalid\";\n }\n break;\n case ZodIssueCode.too_small:\n if (issue.type === \"array\")\n message = `Array must contain ${issue.exact ? \"exactly\" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`;\n else if (issue.type === \"string\")\n message = `String must contain ${issue.exact ? \"exactly\" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`;\n else if (issue.type === \"number\")\n message = `Number must be ${issue.exact\n ? `exactly equal to `\n : issue.inclusive\n ? `greater than or equal to `\n : `greater than `}${issue.minimum}`;\n else if (issue.type === \"date\")\n message = `Date must be ${issue.exact\n ? `exactly equal to `\n : issue.inclusive\n ? `greater than or equal to `\n : `greater than `}${new Date(Number(issue.minimum))}`;\n else\n message = \"Invalid input\";\n break;\n case ZodIssueCode.too_big:\n if (issue.type === \"array\")\n message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`;\n else if (issue.type === \"string\")\n message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`;\n else if (issue.type === \"number\")\n message = `Number must be ${issue.exact\n ? `exactly`\n : issue.inclusive\n ? `less than or equal to`\n : `less than`} ${issue.maximum}`;\n else if (issue.type === \"bigint\")\n message = `BigInt must be ${issue.exact\n ? `exactly`\n : issue.inclusive\n ? `less than or equal to`\n : `less than`} ${issue.maximum}`;\n else if (issue.type === \"date\")\n message = `Date must be ${issue.exact\n ? `exactly`\n : issue.inclusive\n ? `smaller than or equal to`\n : `smaller than`} ${new Date(Number(issue.maximum))}`;\n else\n message = \"Invalid input\";\n break;\n case ZodIssueCode.custom:\n message = `Invalid input`;\n break;\n case ZodIssueCode.invalid_intersection_types:\n message = `Intersection results could not be merged`;\n break;\n case ZodIssueCode.not_multiple_of:\n message = `Number must be a multiple of ${issue.multipleOf}`;\n break;\n case ZodIssueCode.not_finite:\n message = \"Number must be finite\";\n break;\n default:\n message = _ctx.defaultError;\n util.assertNever(issue);\n }\n return { message };\n};\n\nlet overrideErrorMap = errorMap;\nfunction setErrorMap(map) {\n overrideErrorMap = map;\n}\nfunction getErrorMap() {\n return overrideErrorMap;\n}\n\nconst makeIssue = (params) => {\n const { data, path, errorMaps, issueData } = params;\n const fullPath = [...path, ...(issueData.path || [])];\n const fullIssue = {\n ...issueData,\n path: fullPath,\n };\n let errorMessage = \"\";\n const maps = errorMaps\n .filter((m) => !!m)\n .slice()\n .reverse();\n for (const map of maps) {\n errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message;\n }\n return {\n ...issueData,\n path: fullPath,\n message: issueData.message || errorMessage,\n };\n};\nconst EMPTY_PATH = [];\nfunction addIssueToContext(ctx, issueData) {\n const issue = makeIssue({\n issueData: issueData,\n data: ctx.data,\n path: ctx.path,\n errorMaps: [\n ctx.common.contextualErrorMap,\n ctx.schemaErrorMap,\n getErrorMap(),\n errorMap, // then global default map\n ].filter((x) => !!x),\n });\n ctx.common.issues.push(issue);\n}\nclass ParseStatus {\n constructor() {\n this.value = \"valid\";\n }\n dirty() {\n if (this.value === \"valid\")\n this.value = \"dirty\";\n }\n abort() {\n if (this.value !== \"aborted\")\n this.value = \"aborted\";\n }\n static mergeArray(status, results) {\n const arrayValue = [];\n for (const s of results) {\n if (s.status === \"aborted\")\n return INVALID;\n if (s.status === \"dirty\")\n status.dirty();\n arrayValue.push(s.value);\n }\n return { status: status.value, value: arrayValue };\n }\n static async mergeObjectAsync(status, pairs) {\n const syncPairs = [];\n for (const pair of pairs) {\n syncPairs.push({\n key: await pair.key,\n value: await pair.value,\n });\n }\n return ParseStatus.mergeObjectSync(status, syncPairs);\n }\n static mergeObjectSync(status, pairs) {\n const finalObject = {};\n for (const pair of pairs) {\n const { key, value } = pair;\n if (key.status === \"aborted\")\n return INVALID;\n if (value.status === \"aborted\")\n return INVALID;\n if (key.status === \"dirty\")\n status.dirty();\n if (value.status === \"dirty\")\n status.dirty();\n if (typeof value.value !== \"undefined\" || pair.alwaysSet) {\n finalObject[key.value] = value.value;\n }\n }\n return { status: status.value, value: finalObject };\n }\n}\nconst INVALID = Object.freeze({\n status: \"aborted\",\n});\nconst DIRTY = (value) => ({ status: \"dirty\", value });\nconst OK = (value) => ({ status: \"valid\", value });\nconst isAborted = (x) => x.status === \"aborted\";\nconst isDirty = (x) => x.status === \"dirty\";\nconst isValid = (x) => x.status === \"valid\";\nconst isAsync = (x) => typeof Promise !== \"undefined\" && x instanceof Promise;\n\nvar errorUtil;\n(function (errorUtil) {\n errorUtil.errToObj = (message) => typeof message === \"string\" ? { message } : message || {};\n errorUtil.toString = (message) => typeof message === \"string\" ? message : message === null || message === void 0 ? void 0 : message.message;\n})(errorUtil || (errorUtil = {}));\n\nclass ParseInputLazyPath {\n constructor(parent, value, path, key) {\n this._cachedPath = [];\n this.parent = parent;\n this.data = value;\n this._path = path;\n this._key = key;\n }\n get path() {\n if (!this._cachedPath.length) {\n if (this._key instanceof Array) {\n this._cachedPath.push(...this._path, ...this._key);\n }\n else {\n this._cachedPath.push(...this._path, this._key);\n }\n }\n return this._cachedPath;\n }\n}\nconst handleResult = (ctx, result) => {\n if (isValid(result)) {\n return { success: true, data: result.value };\n }\n else {\n if (!ctx.common.issues.length) {\n throw new Error(\"Validation failed but no issues detected.\");\n }\n return {\n success: false,\n get error() {\n if (this._error)\n return this._error;\n const error = new ZodError(ctx.common.issues);\n this._error = error;\n return this._error;\n },\n };\n }\n};\nfunction processCreateParams(params) {\n if (!params)\n return {};\n const { errorMap, invalid_type_error, required_error, description } = params;\n if (errorMap && (invalid_type_error || required_error)) {\n throw new Error(`Can't use \"invalid_type_error\" or \"required_error\" in conjunction with custom error map.`);\n }\n if (errorMap)\n return { errorMap: errorMap, description };\n const customMap = (iss, ctx) => {\n if (iss.code !== \"invalid_type\")\n return { message: ctx.defaultError };\n if (typeof ctx.data === \"undefined\") {\n return { message: required_error !== null && required_error !== void 0 ? required_error : ctx.defaultError };\n }\n return { message: invalid_type_error !== null && invalid_type_error !== void 0 ? invalid_type_error : ctx.defaultError };\n };\n return { errorMap: customMap, description };\n}\nclass ZodType {\n constructor(def) {\n /** Alias of safeParseAsync */\n this.spa = this.safeParseAsync;\n this._def = def;\n this.parse = this.parse.bind(this);\n this.safeParse = this.safeParse.bind(this);\n this.parseAsync = this.parseAsync.bind(this);\n this.safeParseAsync = this.safeParseAsync.bind(this);\n this.spa = this.spa.bind(this);\n this.refine = this.refine.bind(this);\n this.refinement = this.refinement.bind(this);\n this.superRefine = this.superRefine.bind(this);\n this.optional = this.optional.bind(this);\n this.nullable = this.nullable.bind(this);\n this.nullish = this.nullish.bind(this);\n this.array = this.array.bind(this);\n this.promise = this.promise.bind(this);\n this.or = this.or.bind(this);\n this.and = this.and.bind(this);\n this.transform = this.transform.bind(this);\n this.brand = this.brand.bind(this);\n this.default = this.default.bind(this);\n this.catch = this.catch.bind(this);\n this.describe = this.describe.bind(this);\n this.pipe = this.pipe.bind(this);\n this.isNullable = this.isNullable.bind(this);\n this.isOptional = this.isOptional.bind(this);\n }\n get description() {\n return this._def.description;\n }\n _getType(input) {\n return getParsedType(input.data);\n }\n _getOrReturnCtx(input, ctx) {\n return (ctx || {\n common: input.parent.common,\n data: input.data,\n parsedType: getParsedType(input.data),\n schemaErrorMap: this._def.errorMap,\n path: input.path,\n parent: input.parent,\n });\n }\n _processInputParams(input) {\n return {\n status: new ParseStatus(),\n ctx: {\n common: input.parent.common,\n data: input.data,\n parsedType: getParsedType(input.data),\n schemaErrorMap: this._def.errorMap,\n path: input.path,\n parent: input.parent,\n },\n };\n }\n _parseSync(input) {\n const result = this._parse(input);\n if (isAsync(result)) {\n throw new Error(\"Synchronous parse encountered promise.\");\n }\n return result;\n }\n _parseAsync(input) {\n const result = this._parse(input);\n return Promise.resolve(result);\n }\n parse(data, params) {\n const result = this.safeParse(data, params);\n if (result.success)\n return result.data;\n throw result.error;\n }\n safeParse(data, params) {\n var _a;\n const ctx = {\n common: {\n issues: [],\n async: (_a = params === null || params === void 0 ? void 0 : params.async) !== null && _a !== void 0 ? _a : false,\n contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap,\n },\n path: (params === null || params === void 0 ? void 0 : params.path) || [],\n schemaErrorMap: this._def.errorMap,\n parent: null,\n data,\n parsedType: getParsedType(data),\n };\n const result = this._parseSync({ data, path: ctx.path, parent: ctx });\n return handleResult(ctx, result);\n }\n async parseAsync(data, params) {\n const result = await this.safeParseAsync(data, params);\n if (result.success)\n return result.data;\n throw result.error;\n }\n async safeParseAsync(data, params) {\n const ctx = {\n common: {\n issues: [],\n contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap,\n async: true,\n },\n path: (params === null || params === void 0 ? void 0 : params.path) || [],\n schemaErrorMap: this._def.errorMap,\n parent: null,\n data,\n parsedType: getParsedType(data),\n };\n const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx });\n const result = await (isAsync(maybeAsyncResult)\n ? maybeAsyncResult\n : Promise.resolve(maybeAsyncResult));\n return handleResult(ctx, result);\n }\n refine(check, message) {\n const getIssueProperties = (val) => {\n if (typeof message === \"string\" || typeof message === \"undefined\") {\n return { message };\n }\n else if (typeof message === \"function\") {\n return message(val);\n }\n else {\n return message;\n }\n };\n return this._refinement((val, ctx) => {\n const result = check(val);\n const setError = () => ctx.addIssue({\n code: ZodIssueCode.custom,\n ...getIssueProperties(val),\n });\n if (typeof Promise !== \"undefined\" && result instanceof Promise) {\n return result.then((data) => {\n if (!data) {\n setError();\n return false;\n }\n else {\n return true;\n }\n });\n }\n if (!result) {\n setError();\n return false;\n }\n else {\n return true;\n }\n });\n }\n refinement(check, refinementData) {\n return this._refinement((val, ctx) => {\n if (!check(val)) {\n ctx.addIssue(typeof refinementData === \"function\"\n ? refinementData(val, ctx)\n : refinementData);\n return false;\n }\n else {\n return true;\n }\n });\n }\n _refinement(refinement) {\n return new ZodEffects({\n schema: this,\n typeName: ZodFirstPartyTypeKind.ZodEffects,\n effect: { type: \"refinement\", refinement },\n });\n }\n superRefine(refinement) {\n return this._refinement(refinement);\n }\n optional() {\n return ZodOptional.create(this, this._def);\n }\n nullable() {\n return ZodNullable.create(this, this._def);\n }\n nullish() {\n return this.nullable().optional();\n }\n array() {\n return ZodArray.create(this, this._def);\n }\n promise() {\n return ZodPromise.create(this, this._def);\n }\n or(option) {\n return ZodUnion.create([this, option], this._def);\n }\n and(incoming) {\n return ZodIntersection.create(this, incoming, this._def);\n }\n transform(transform) {\n return new ZodEffects({\n ...processCreateParams(this._def),\n schema: this,\n typeName: ZodFirstPartyTypeKind.ZodEffects,\n effect: { type: \"transform\", transform },\n });\n }\n default(def) {\n const defaultValueFunc = typeof def === \"function\" ? def : () => def;\n return new ZodDefault({\n ...processCreateParams(this._def),\n innerType: this,\n defaultValue: defaultValueFunc,\n typeName: ZodFirstPartyTypeKind.ZodDefault,\n });\n }\n brand() {\n return new ZodBranded({\n typeName: ZodFirstPartyTypeKind.ZodBranded,\n type: this,\n ...processCreateParams(this._def),\n });\n }\n catch(def) {\n const catchValueFunc = typeof def === \"function\" ? def : () => def;\n return new ZodCatch({\n ...processCreateParams(this._def),\n innerType: this,\n catchValue: catchValueFunc,\n typeName: ZodFirstPartyTypeKind.ZodCatch,\n });\n }\n describe(description) {\n const This = this.constructor;\n return new This({\n ...this._def,\n description,\n });\n }\n pipe(target) {\n return ZodPipeline.create(this, target);\n }\n isOptional() {\n return this.safeParse(undefined).success;\n }\n isNullable() {\n return this.safeParse(null).success;\n }\n}\nconst cuidRegex = /^c[^\\s-]{8,}$/i;\nconst cuid2Regex = /^[a-z][a-z0-9]*$/;\nconst ulidRegex = /[0-9A-HJKMNP-TV-Z]{26}/;\nconst uuidRegex = /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i;\n// from https://stackoverflow.com/a/46181/1550155\n// old version: too slow, didn't support unicode\n// const emailRegex = /^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))$/i;\n//old email regex\n// const emailRegex = /^(([^<>()[\\].,;:\\s@\"]+(\\.[^<>()[\\].,;:\\s@\"]+)*)|(\".+\"))@((?!-)([^<>()[\\].,;:\\s@\"]+\\.)+[^<>()[\\].,;:\\s@\"]{1,})[^-<>()[\\].,;:\\s@\"]$/i;\n// eslint-disable-next-line\nconst emailRegex = /^(([^<>()[\\]\\\\.,;:\\s@\\\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\\\"]+)*)|(\\\".+\\\"))@((\\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\\])|(\\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\\.[A-Za-z]{2,})+))$/;\n// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression\nconst emojiRegex = /^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$/u;\nconst ipv4Regex = /^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/;\nconst ipv6Regex = /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/;\n// Adapted from https://stackoverflow.com/a/3143231\nconst datetimeRegex = (args) => {\n if (args.precision) {\n if (args.offset) {\n return new RegExp(`^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\.\\\\d{${args.precision}}(([+-]\\\\d{2}(:?\\\\d{2})?)|Z)$`);\n }\n else {\n return new RegExp(`^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\.\\\\d{${args.precision}}Z$`);\n }\n }\n else if (args.precision === 0) {\n if (args.offset) {\n return new RegExp(`^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}(([+-]\\\\d{2}(:?\\\\d{2})?)|Z)$`);\n }\n else {\n return new RegExp(`^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}Z$`);\n }\n }\n else {\n if (args.offset) {\n return new RegExp(`^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}(\\\\.\\\\d+)?(([+-]\\\\d{2}(:?\\\\d{2})?)|Z)$`);\n }\n else {\n return new RegExp(`^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}(\\\\.\\\\d+)?Z$`);\n }\n }\n};\nfunction isValidIP(ip, version) {\n if ((version === \"v4\" || !version) && ipv4Regex.test(ip)) {\n return true;\n }\n if ((version === \"v6\" || !version) && ipv6Regex.test(ip)) {\n return true;\n }\n return false;\n}\nclass ZodString extends ZodType {\n constructor() {\n super(...arguments);\n this._regex = (regex, validation, message) => this.refinement((data) => regex.test(data), {\n validation,\n code: ZodIssueCode.invalid_string,\n ...errorUtil.errToObj(message),\n });\n /**\n * @deprecated Use z.string().min(1) instead.\n * @see {@link ZodString.min}\n */\n this.nonempty = (message) => this.min(1, errorUtil.errToObj(message));\n this.trim = () => new ZodString({\n ...this._def,\n checks: [...this._def.checks, { kind: \"trim\" }],\n });\n this.toLowerCase = () => new ZodString({\n ...this._def,\n checks: [...this._def.checks, { kind: \"toLowerCase\" }],\n });\n this.toUpperCase = () => new ZodString({\n ...this._def,\n checks: [...this._def.checks, { kind: \"toUpperCase\" }],\n });\n }\n _parse(input) {\n if (this._def.coerce) {\n input.data = String(input.data);\n }\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.string) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.string,\n received: ctx.parsedType,\n }\n //\n );\n return INVALID;\n }\n const status = new ParseStatus();\n let ctx = undefined;\n for (const check of this._def.checks) {\n if (check.kind === \"min\") {\n if (input.data.length < check.value) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: check.value,\n type: \"string\",\n inclusive: true,\n exact: false,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"max\") {\n if (input.data.length > check.value) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: check.value,\n type: \"string\",\n inclusive: true,\n exact: false,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"length\") {\n const tooBig = input.data.length > check.value;\n const tooSmall = input.data.length < check.value;\n if (tooBig || tooSmall) {\n ctx = this._getOrReturnCtx(input, ctx);\n if (tooBig) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: check.value,\n type: \"string\",\n inclusive: true,\n exact: true,\n message: check.message,\n });\n }\n else if (tooSmall) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: check.value,\n type: \"string\",\n inclusive: true,\n exact: true,\n message: check.message,\n });\n }\n status.dirty();\n }\n }\n else if (check.kind === \"email\") {\n if (!emailRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"email\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"emoji\") {\n if (!emojiRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"emoji\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"uuid\") {\n if (!uuidRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"uuid\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"cuid\") {\n if (!cuidRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"cuid\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"cuid2\") {\n if (!cuid2Regex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"cuid2\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"ulid\") {\n if (!ulidRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"ulid\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"url\") {\n try {\n new URL(input.data);\n }\n catch (_a) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"url\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"regex\") {\n check.regex.lastIndex = 0;\n const testResult = check.regex.test(input.data);\n if (!testResult) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"regex\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"trim\") {\n input.data = input.data.trim();\n }\n else if (check.kind === \"includes\") {\n if (!input.data.includes(check.value, check.position)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_string,\n validation: { includes: check.value, position: check.position },\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"toLowerCase\") {\n input.data = input.data.toLowerCase();\n }\n else if (check.kind === \"toUpperCase\") {\n input.data = input.data.toUpperCase();\n }\n else if (check.kind === \"startsWith\") {\n if (!input.data.startsWith(check.value)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_string,\n validation: { startsWith: check.value },\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"endsWith\") {\n if (!input.data.endsWith(check.value)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_string,\n validation: { endsWith: check.value },\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"datetime\") {\n const regex = datetimeRegex(check);\n if (!regex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_string,\n validation: \"datetime\",\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"ip\") {\n if (!isValidIP(input.data, check.version)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"ip\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else {\n util.assertNever(check);\n }\n }\n return { status: status.value, value: input.data };\n }\n _addCheck(check) {\n return new ZodString({\n ...this._def,\n checks: [...this._def.checks, check],\n });\n }\n email(message) {\n return this._addCheck({ kind: \"email\", ...errorUtil.errToObj(message) });\n }\n url(message) {\n return this._addCheck({ kind: \"url\", ...errorUtil.errToObj(message) });\n }\n emoji(message) {\n return this._addCheck({ kind: \"emoji\", ...errorUtil.errToObj(message) });\n }\n uuid(message) {\n return this._addCheck({ kind: \"uuid\", ...errorUtil.errToObj(message) });\n }\n cuid(message) {\n return this._addCheck({ kind: \"cuid\", ...errorUtil.errToObj(message) });\n }\n cuid2(message) {\n return this._addCheck({ kind: \"cuid2\", ...errorUtil.errToObj(message) });\n }\n ulid(message) {\n return this._addCheck({ kind: \"ulid\", ...errorUtil.errToObj(message) });\n }\n ip(options) {\n return this._addCheck({ kind: \"ip\", ...errorUtil.errToObj(options) });\n }\n datetime(options) {\n var _a;\n if (typeof options === \"string\") {\n return this._addCheck({\n kind: \"datetime\",\n precision: null,\n offset: false,\n message: options,\n });\n }\n return this._addCheck({\n kind: \"datetime\",\n precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === \"undefined\" ? null : options === null || options === void 0 ? void 0 : options.precision,\n offset: (_a = options === null || options === void 0 ? void 0 : options.offset) !== null && _a !== void 0 ? _a : false,\n ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message),\n });\n }\n regex(regex, message) {\n return this._addCheck({\n kind: \"regex\",\n regex: regex,\n ...errorUtil.errToObj(message),\n });\n }\n includes(value, options) {\n return this._addCheck({\n kind: \"includes\",\n value: value,\n position: options === null || options === void 0 ? void 0 : options.position,\n ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message),\n });\n }\n startsWith(value, message) {\n return this._addCheck({\n kind: \"startsWith\",\n value: value,\n ...errorUtil.errToObj(message),\n });\n }\n endsWith(value, message) {\n return this._addCheck({\n kind: \"endsWith\",\n value: value,\n ...errorUtil.errToObj(message),\n });\n }\n min(minLength, message) {\n return this._addCheck({\n kind: \"min\",\n value: minLength,\n ...errorUtil.errToObj(message),\n });\n }\n max(maxLength, message) {\n return this._addCheck({\n kind: \"max\",\n value: maxLength,\n ...errorUtil.errToObj(message),\n });\n }\n length(len, message) {\n return this._addCheck({\n kind: \"length\",\n value: len,\n ...errorUtil.errToObj(message),\n });\n }\n get isDatetime() {\n return !!this._def.checks.find((ch) => ch.kind === \"datetime\");\n }\n get isEmail() {\n return !!this._def.checks.find((ch) => ch.kind === \"email\");\n }\n get isURL() {\n return !!this._def.checks.find((ch) => ch.kind === \"url\");\n }\n get isEmoji() {\n return !!this._def.checks.find((ch) => ch.kind === \"emoji\");\n }\n get isUUID() {\n return !!this._def.checks.find((ch) => ch.kind === \"uuid\");\n }\n get isCUID() {\n return !!this._def.checks.find((ch) => ch.kind === \"cuid\");\n }\n get isCUID2() {\n return !!this._def.checks.find((ch) => ch.kind === \"cuid2\");\n }\n get isULID() {\n return !!this._def.checks.find((ch) => ch.kind === \"ulid\");\n }\n get isIP() {\n return !!this._def.checks.find((ch) => ch.kind === \"ip\");\n }\n get minLength() {\n let min = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"min\") {\n if (min === null || ch.value > min)\n min = ch.value;\n }\n }\n return min;\n }\n get maxLength() {\n let max = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"max\") {\n if (max === null || ch.value < max)\n max = ch.value;\n }\n }\n return max;\n }\n}\nZodString.create = (params) => {\n var _a;\n return new ZodString({\n checks: [],\n typeName: ZodFirstPartyTypeKind.ZodString,\n coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false,\n ...processCreateParams(params),\n });\n};\n// https://stackoverflow.com/questions/3966484/why-does-modulus-operator-return-fractional-number-in-javascript/31711034#31711034\nfunction floatSafeRemainder(val, step) {\n const valDecCount = (val.toString().split(\".\")[1] || \"\").length;\n const stepDecCount = (step.toString().split(\".\")[1] || \"\").length;\n const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;\n const valInt = parseInt(val.toFixed(decCount).replace(\".\", \"\"));\n const stepInt = parseInt(step.toFixed(decCount).replace(\".\", \"\"));\n return (valInt % stepInt) / Math.pow(10, decCount);\n}\nclass ZodNumber extends ZodType {\n constructor() {\n super(...arguments);\n this.min = this.gte;\n this.max = this.lte;\n this.step = this.multipleOf;\n }\n _parse(input) {\n if (this._def.coerce) {\n input.data = Number(input.data);\n }\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.number) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.number,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n let ctx = undefined;\n const status = new ParseStatus();\n for (const check of this._def.checks) {\n if (check.kind === \"int\") {\n if (!util.isInteger(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: \"integer\",\n received: \"float\",\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"min\") {\n const tooSmall = check.inclusive\n ? input.data < check.value\n : input.data <= check.value;\n if (tooSmall) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: check.value,\n type: \"number\",\n inclusive: check.inclusive,\n exact: false,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"max\") {\n const tooBig = check.inclusive\n ? input.data > check.value\n : input.data >= check.value;\n if (tooBig) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: check.value,\n type: \"number\",\n inclusive: check.inclusive,\n exact: false,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"multipleOf\") {\n if (floatSafeRemainder(input.data, check.value) !== 0) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.not_multiple_of,\n multipleOf: check.value,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"finite\") {\n if (!Number.isFinite(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.not_finite,\n message: check.message,\n });\n status.dirty();\n }\n }\n else {\n util.assertNever(check);\n }\n }\n return { status: status.value, value: input.data };\n }\n gte(value, message) {\n return this.setLimit(\"min\", value, true, errorUtil.toString(message));\n }\n gt(value, message) {\n return this.setLimit(\"min\", value, false, errorUtil.toString(message));\n }\n lte(value, message) {\n return this.setLimit(\"max\", value, true, errorUtil.toString(message));\n }\n lt(value, message) {\n return this.setLimit(\"max\", value, false, errorUtil.toString(message));\n }\n setLimit(kind, value, inclusive, message) {\n return new ZodNumber({\n ...this._def,\n checks: [\n ...this._def.checks,\n {\n kind,\n value,\n inclusive,\n message: errorUtil.toString(message),\n },\n ],\n });\n }\n _addCheck(check) {\n return new ZodNumber({\n ...this._def,\n checks: [...this._def.checks, check],\n });\n }\n int(message) {\n return this._addCheck({\n kind: \"int\",\n message: errorUtil.toString(message),\n });\n }\n positive(message) {\n return this._addCheck({\n kind: \"min\",\n value: 0,\n inclusive: false,\n message: errorUtil.toString(message),\n });\n }\n negative(message) {\n return this._addCheck({\n kind: \"max\",\n value: 0,\n inclusive: false,\n message: errorUtil.toString(message),\n });\n }\n nonpositive(message) {\n return this._addCheck({\n kind: \"max\",\n value: 0,\n inclusive: true,\n message: errorUtil.toString(message),\n });\n }\n nonnegative(message) {\n return this._addCheck({\n kind: \"min\",\n value: 0,\n inclusive: true,\n message: errorUtil.toString(message),\n });\n }\n multipleOf(value, message) {\n return this._addCheck({\n kind: \"multipleOf\",\n value: value,\n message: errorUtil.toString(message),\n });\n }\n finite(message) {\n return this._addCheck({\n kind: \"finite\",\n message: errorUtil.toString(message),\n });\n }\n safe(message) {\n return this._addCheck({\n kind: \"min\",\n inclusive: true,\n value: Number.MIN_SAFE_INTEGER,\n message: errorUtil.toString(message),\n })._addCheck({\n kind: \"max\",\n inclusive: true,\n value: Number.MAX_SAFE_INTEGER,\n message: errorUtil.toString(message),\n });\n }\n get minValue() {\n let min = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"min\") {\n if (min === null || ch.value > min)\n min = ch.value;\n }\n }\n return min;\n }\n get maxValue() {\n let max = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"max\") {\n if (max === null || ch.value < max)\n max = ch.value;\n }\n }\n return max;\n }\n get isInt() {\n return !!this._def.checks.find((ch) => ch.kind === \"int\" ||\n (ch.kind === \"multipleOf\" && util.isInteger(ch.value)));\n }\n get isFinite() {\n let max = null, min = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"finite\" ||\n ch.kind === \"int\" ||\n ch.kind === \"multipleOf\") {\n return true;\n }\n else if (ch.kind === \"min\") {\n if (min === null || ch.value > min)\n min = ch.value;\n }\n else if (ch.kind === \"max\") {\n if (max === null || ch.value < max)\n max = ch.value;\n }\n }\n return Number.isFinite(min) && Number.isFinite(max);\n }\n}\nZodNumber.create = (params) => {\n return new ZodNumber({\n checks: [],\n typeName: ZodFirstPartyTypeKind.ZodNumber,\n coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,\n ...processCreateParams(params),\n });\n};\nclass ZodBigInt extends ZodType {\n constructor() {\n super(...arguments);\n this.min = this.gte;\n this.max = this.lte;\n }\n _parse(input) {\n if (this._def.coerce) {\n input.data = BigInt(input.data);\n }\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.bigint) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.bigint,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n let ctx = undefined;\n const status = new ParseStatus();\n for (const check of this._def.checks) {\n if (check.kind === \"min\") {\n const tooSmall = check.inclusive\n ? input.data < check.value\n : input.data <= check.value;\n if (tooSmall) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n type: \"bigint\",\n minimum: check.value,\n inclusive: check.inclusive,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"max\") {\n const tooBig = check.inclusive\n ? input.data > check.value\n : input.data >= check.value;\n if (tooBig) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n type: \"bigint\",\n maximum: check.value,\n inclusive: check.inclusive,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"multipleOf\") {\n if (input.data % check.value !== BigInt(0)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.not_multiple_of,\n multipleOf: check.value,\n message: check.message,\n });\n status.dirty();\n }\n }\n else {\n util.assertNever(check);\n }\n }\n return { status: status.value, value: input.data };\n }\n gte(value, message) {\n return this.setLimit(\"min\", value, true, errorUtil.toString(message));\n }\n gt(value, message) {\n return this.setLimit(\"min\", value, false, errorUtil.toString(message));\n }\n lte(value, message) {\n return this.setLimit(\"max\", value, true, errorUtil.toString(message));\n }\n lt(value, message) {\n return this.setLimit(\"max\", value, false, errorUtil.toString(message));\n }\n setLimit(kind, value, inclusive, message) {\n return new ZodBigInt({\n ...this._def,\n checks: [\n ...this._def.checks,\n {\n kind,\n value,\n inclusive,\n message: errorUtil.toString(message),\n },\n ],\n });\n }\n _addCheck(check) {\n return new ZodBigInt({\n ...this._def,\n checks: [...this._def.checks, check],\n });\n }\n positive(message) {\n return this._addCheck({\n kind: \"min\",\n value: BigInt(0),\n inclusive: false,\n message: errorUtil.toString(message),\n });\n }\n negative(message) {\n return this._addCheck({\n kind: \"max\",\n value: BigInt(0),\n inclusive: false,\n message: errorUtil.toString(message),\n });\n }\n nonpositive(message) {\n return this._addCheck({\n kind: \"max\",\n value: BigInt(0),\n inclusive: true,\n message: errorUtil.toString(message),\n });\n }\n nonnegative(message) {\n return this._addCheck({\n kind: \"min\",\n value: BigInt(0),\n inclusive: true,\n message: errorUtil.toString(message),\n });\n }\n multipleOf(value, message) {\n return this._addCheck({\n kind: \"multipleOf\",\n value,\n message: errorUtil.toString(message),\n });\n }\n get minValue() {\n let min = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"min\") {\n if (min === null || ch.value > min)\n min = ch.value;\n }\n }\n return min;\n }\n get maxValue() {\n let max = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"max\") {\n if (max === null || ch.value < max)\n max = ch.value;\n }\n }\n return max;\n }\n}\nZodBigInt.create = (params) => {\n var _a;\n return new ZodBigInt({\n checks: [],\n typeName: ZodFirstPartyTypeKind.ZodBigInt,\n coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false,\n ...processCreateParams(params),\n });\n};\nclass ZodBoolean extends ZodType {\n _parse(input) {\n if (this._def.coerce) {\n input.data = Boolean(input.data);\n }\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.boolean) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.boolean,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n}\nZodBoolean.create = (params) => {\n return new ZodBoolean({\n typeName: ZodFirstPartyTypeKind.ZodBoolean,\n coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,\n ...processCreateParams(params),\n });\n};\nclass ZodDate extends ZodType {\n _parse(input) {\n if (this._def.coerce) {\n input.data = new Date(input.data);\n }\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.date) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.date,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n if (isNaN(input.data.getTime())) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_date,\n });\n return INVALID;\n }\n const status = new ParseStatus();\n let ctx = undefined;\n for (const check of this._def.checks) {\n if (check.kind === \"min\") {\n if (input.data.getTime() < check.value) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n message: check.message,\n inclusive: true,\n exact: false,\n minimum: check.value,\n type: \"date\",\n });\n status.dirty();\n }\n }\n else if (check.kind === \"max\") {\n if (input.data.getTime() > check.value) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n message: check.message,\n inclusive: true,\n exact: false,\n maximum: check.value,\n type: \"date\",\n });\n status.dirty();\n }\n }\n else {\n util.assertNever(check);\n }\n }\n return {\n status: status.value,\n value: new Date(input.data.getTime()),\n };\n }\n _addCheck(check) {\n return new ZodDate({\n ...this._def,\n checks: [...this._def.checks, check],\n });\n }\n min(minDate, message) {\n return this._addCheck({\n kind: \"min\",\n value: minDate.getTime(),\n message: errorUtil.toString(message),\n });\n }\n max(maxDate, message) {\n return this._addCheck({\n kind: \"max\",\n value: maxDate.getTime(),\n message: errorUtil.toString(message),\n });\n }\n get minDate() {\n let min = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"min\") {\n if (min === null || ch.value > min)\n min = ch.value;\n }\n }\n return min != null ? new Date(min) : null;\n }\n get maxDate() {\n let max = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"max\") {\n if (max === null || ch.value < max)\n max = ch.value;\n }\n }\n return max != null ? new Date(max) : null;\n }\n}\nZodDate.create = (params) => {\n return new ZodDate({\n checks: [],\n coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,\n typeName: ZodFirstPartyTypeKind.ZodDate,\n ...processCreateParams(params),\n });\n};\nclass ZodSymbol extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.symbol) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.symbol,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n}\nZodSymbol.create = (params) => {\n return new ZodSymbol({\n typeName: ZodFirstPartyTypeKind.ZodSymbol,\n ...processCreateParams(params),\n });\n};\nclass ZodUndefined extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.undefined) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.undefined,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n}\nZodUndefined.create = (params) => {\n return new ZodUndefined({\n typeName: ZodFirstPartyTypeKind.ZodUndefined,\n ...processCreateParams(params),\n });\n};\nclass ZodNull extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.null) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.null,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n}\nZodNull.create = (params) => {\n return new ZodNull({\n typeName: ZodFirstPartyTypeKind.ZodNull,\n ...processCreateParams(params),\n });\n};\nclass ZodAny extends ZodType {\n constructor() {\n super(...arguments);\n // to prevent instances of other classes from extending ZodAny. this causes issues with catchall in ZodObject.\n this._any = true;\n }\n _parse(input) {\n return OK(input.data);\n }\n}\nZodAny.create = (params) => {\n return new ZodAny({\n typeName: ZodFirstPartyTypeKind.ZodAny,\n ...processCreateParams(params),\n });\n};\nclass ZodUnknown extends ZodType {\n constructor() {\n super(...arguments);\n // required\n this._unknown = true;\n }\n _parse(input) {\n return OK(input.data);\n }\n}\nZodUnknown.create = (params) => {\n return new ZodUnknown({\n typeName: ZodFirstPartyTypeKind.ZodUnknown,\n ...processCreateParams(params),\n });\n};\nclass ZodNever extends ZodType {\n _parse(input) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.never,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n}\nZodNever.create = (params) => {\n return new ZodNever({\n typeName: ZodFirstPartyTypeKind.ZodNever,\n ...processCreateParams(params),\n });\n};\nclass ZodVoid extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.undefined) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.void,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n}\nZodVoid.create = (params) => {\n return new ZodVoid({\n typeName: ZodFirstPartyTypeKind.ZodVoid,\n ...processCreateParams(params),\n });\n};\nclass ZodArray extends ZodType {\n _parse(input) {\n const { ctx, status } = this._processInputParams(input);\n const def = this._def;\n if (ctx.parsedType !== ZodParsedType.array) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.array,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n if (def.exactLength !== null) {\n const tooBig = ctx.data.length > def.exactLength.value;\n const tooSmall = ctx.data.length < def.exactLength.value;\n if (tooBig || tooSmall) {\n addIssueToContext(ctx, {\n code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small,\n minimum: (tooSmall ? def.exactLength.value : undefined),\n maximum: (tooBig ? def.exactLength.value : undefined),\n type: \"array\",\n inclusive: true,\n exact: true,\n message: def.exactLength.message,\n });\n status.dirty();\n }\n }\n if (def.minLength !== null) {\n if (ctx.data.length < def.minLength.value) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: def.minLength.value,\n type: \"array\",\n inclusive: true,\n exact: false,\n message: def.minLength.message,\n });\n status.dirty();\n }\n }\n if (def.maxLength !== null) {\n if (ctx.data.length > def.maxLength.value) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: def.maxLength.value,\n type: \"array\",\n inclusive: true,\n exact: false,\n message: def.maxLength.message,\n });\n status.dirty();\n }\n }\n if (ctx.common.async) {\n return Promise.all([...ctx.data].map((item, i) => {\n return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i));\n })).then((result) => {\n return ParseStatus.mergeArray(status, result);\n });\n }\n const result = [...ctx.data].map((item, i) => {\n return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i));\n });\n return ParseStatus.mergeArray(status, result);\n }\n get element() {\n return this._def.type;\n }\n min(minLength, message) {\n return new ZodArray({\n ...this._def,\n minLength: { value: minLength, message: errorUtil.toString(message) },\n });\n }\n max(maxLength, message) {\n return new ZodArray({\n ...this._def,\n maxLength: { value: maxLength, message: errorUtil.toString(message) },\n });\n }\n length(len, message) {\n return new ZodArray({\n ...this._def,\n exactLength: { value: len, message: errorUtil.toString(message) },\n });\n }\n nonempty(message) {\n return this.min(1, message);\n }\n}\nZodArray.create = (schema, params) => {\n return new ZodArray({\n type: schema,\n minLength: null,\n maxLength: null,\n exactLength: null,\n typeName: ZodFirstPartyTypeKind.ZodArray,\n ...processCreateParams(params),\n });\n};\nfunction deepPartialify(schema) {\n if (schema instanceof ZodObject) {\n const newShape = {};\n for (const key in schema.shape) {\n const fieldSchema = schema.shape[key];\n newShape[key] = ZodOptional.create(deepPartialify(fieldSchema));\n }\n return new ZodObject({\n ...schema._def,\n shape: () => newShape,\n });\n }\n else if (schema instanceof ZodArray) {\n return new ZodArray({\n ...schema._def,\n type: deepPartialify(schema.element),\n });\n }\n else if (schema instanceof ZodOptional) {\n return ZodOptional.create(deepPartialify(schema.unwrap()));\n }\n else if (schema instanceof ZodNullable) {\n return ZodNullable.create(deepPartialify(schema.unwrap()));\n }\n else if (schema instanceof ZodTuple) {\n return ZodTuple.create(schema.items.map((item) => deepPartialify(item)));\n }\n else {\n return schema;\n }\n}\nclass ZodObject extends ZodType {\n constructor() {\n super(...arguments);\n this._cached = null;\n /**\n * @deprecated In most cases, this is no longer needed - unknown properties are now silently stripped.\n * If you want to pass through unknown properties, use `.passthrough()` instead.\n */\n this.nonstrict = this.passthrough;\n // extend<\n // Augmentation extends ZodRawShape,\n // NewOutput extends util.flatten<{\n // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation\n // ? Augmentation[k][\"_output\"]\n // : k extends keyof Output\n // ? Output[k]\n // : never;\n // }>,\n // NewInput extends util.flatten<{\n // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation\n // ? Augmentation[k][\"_input\"]\n // : k extends keyof Input\n // ? Input[k]\n // : never;\n // }>\n // >(\n // augmentation: Augmentation\n // ): ZodObject<\n // extendShape,\n // UnknownKeys,\n // Catchall,\n // NewOutput,\n // NewInput\n // > {\n // return new ZodObject({\n // ...this._def,\n // shape: () => ({\n // ...this._def.shape(),\n // ...augmentation,\n // }),\n // }) as any;\n // }\n /**\n * @deprecated Use `.extend` instead\n * */\n this.augment = this.extend;\n }\n _getCached() {\n if (this._cached !== null)\n return this._cached;\n const shape = this._def.shape();\n const keys = util.objectKeys(shape);\n return (this._cached = { shape, keys });\n }\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.object) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.object,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const { status, ctx } = this._processInputParams(input);\n const { shape, keys: shapeKeys } = this._getCached();\n const extraKeys = [];\n if (!(this._def.catchall instanceof ZodNever &&\n this._def.unknownKeys === \"strip\")) {\n for (const key in ctx.data) {\n if (!shapeKeys.includes(key)) {\n extraKeys.push(key);\n }\n }\n }\n const pairs = [];\n for (const key of shapeKeys) {\n const keyValidator = shape[key];\n const value = ctx.data[key];\n pairs.push({\n key: { status: \"valid\", value: key },\n value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)),\n alwaysSet: key in ctx.data,\n });\n }\n if (this._def.catchall instanceof ZodNever) {\n const unknownKeys = this._def.unknownKeys;\n if (unknownKeys === \"passthrough\") {\n for (const key of extraKeys) {\n pairs.push({\n key: { status: \"valid\", value: key },\n value: { status: \"valid\", value: ctx.data[key] },\n });\n }\n }\n else if (unknownKeys === \"strict\") {\n if (extraKeys.length > 0) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.unrecognized_keys,\n keys: extraKeys,\n });\n status.dirty();\n }\n }\n else if (unknownKeys === \"strip\") ;\n else {\n throw new Error(`Internal ZodObject error: invalid unknownKeys value.`);\n }\n }\n else {\n // run catchall validation\n const catchall = this._def.catchall;\n for (const key of extraKeys) {\n const value = ctx.data[key];\n pairs.push({\n key: { status: \"valid\", value: key },\n value: catchall._parse(new ParseInputLazyPath(ctx, value, ctx.path, key) //, ctx.child(key), value, getParsedType(value)\n ),\n alwaysSet: key in ctx.data,\n });\n }\n }\n if (ctx.common.async) {\n return Promise.resolve()\n .then(async () => {\n const syncPairs = [];\n for (const pair of pairs) {\n const key = await pair.key;\n syncPairs.push({\n key,\n value: await pair.value,\n alwaysSet: pair.alwaysSet,\n });\n }\n return syncPairs;\n })\n .then((syncPairs) => {\n return ParseStatus.mergeObjectSync(status, syncPairs);\n });\n }\n else {\n return ParseStatus.mergeObjectSync(status, pairs);\n }\n }\n get shape() {\n return this._def.shape();\n }\n strict(message) {\n errorUtil.errToObj;\n return new ZodObject({\n ...this._def,\n unknownKeys: \"strict\",\n ...(message !== undefined\n ? {\n errorMap: (issue, ctx) => {\n var _a, _b, _c, _d;\n const defaultError = (_c = (_b = (_a = this._def).errorMap) === null || _b === void 0 ? void 0 : _b.call(_a, issue, ctx).message) !== null && _c !== void 0 ? _c : ctx.defaultError;\n if (issue.code === \"unrecognized_keys\")\n return {\n message: (_d = errorUtil.errToObj(message).message) !== null && _d !== void 0 ? _d : defaultError,\n };\n return {\n message: defaultError,\n };\n },\n }\n : {}),\n });\n }\n strip() {\n return new ZodObject({\n ...this._def,\n unknownKeys: \"strip\",\n });\n }\n passthrough() {\n return new ZodObject({\n ...this._def,\n unknownKeys: \"passthrough\",\n });\n }\n // const AugmentFactory =\n // (def: Def) =>\n // (\n // augmentation: Augmentation\n // ): ZodObject<\n // extendShape, Augmentation>,\n // Def[\"unknownKeys\"],\n // Def[\"catchall\"]\n // > => {\n // return new ZodObject({\n // ...def,\n // shape: () => ({\n // ...def.shape(),\n // ...augmentation,\n // }),\n // }) as any;\n // };\n extend(augmentation) {\n return new ZodObject({\n ...this._def,\n shape: () => ({\n ...this._def.shape(),\n ...augmentation,\n }),\n });\n }\n /**\n * Prior to zod@1.0.12 there was a bug in the\n * inferred type of merged objects. Please\n * upgrade if you are experiencing issues.\n */\n merge(merging) {\n const merged = new ZodObject({\n unknownKeys: merging._def.unknownKeys,\n catchall: merging._def.catchall,\n shape: () => ({\n ...this._def.shape(),\n ...merging._def.shape(),\n }),\n typeName: ZodFirstPartyTypeKind.ZodObject,\n });\n return merged;\n }\n // merge<\n // Incoming extends AnyZodObject,\n // Augmentation extends Incoming[\"shape\"],\n // NewOutput extends {\n // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation\n // ? Augmentation[k][\"_output\"]\n // : k extends keyof Output\n // ? Output[k]\n // : never;\n // },\n // NewInput extends {\n // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation\n // ? Augmentation[k][\"_input\"]\n // : k extends keyof Input\n // ? Input[k]\n // : never;\n // }\n // >(\n // merging: Incoming\n // ): ZodObject<\n // extendShape>,\n // Incoming[\"_def\"][\"unknownKeys\"],\n // Incoming[\"_def\"][\"catchall\"],\n // NewOutput,\n // NewInput\n // > {\n // const merged: any = new ZodObject({\n // unknownKeys: merging._def.unknownKeys,\n // catchall: merging._def.catchall,\n // shape: () =>\n // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()),\n // typeName: ZodFirstPartyTypeKind.ZodObject,\n // }) as any;\n // return merged;\n // }\n setKey(key, schema) {\n return this.augment({ [key]: schema });\n }\n // merge(\n // merging: Incoming\n // ): //ZodObject = (merging) => {\n // ZodObject<\n // extendShape>,\n // Incoming[\"_def\"][\"unknownKeys\"],\n // Incoming[\"_def\"][\"catchall\"]\n // > {\n // // const mergedShape = objectUtil.mergeShapes(\n // // this._def.shape(),\n // // merging._def.shape()\n // // );\n // const merged: any = new ZodObject({\n // unknownKeys: merging._def.unknownKeys,\n // catchall: merging._def.catchall,\n // shape: () =>\n // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()),\n // typeName: ZodFirstPartyTypeKind.ZodObject,\n // }) as any;\n // return merged;\n // }\n catchall(index) {\n return new ZodObject({\n ...this._def,\n catchall: index,\n });\n }\n pick(mask) {\n const shape = {};\n util.objectKeys(mask).forEach((key) => {\n if (mask[key] && this.shape[key]) {\n shape[key] = this.shape[key];\n }\n });\n return new ZodObject({\n ...this._def,\n shape: () => shape,\n });\n }\n omit(mask) {\n const shape = {};\n util.objectKeys(this.shape).forEach((key) => {\n if (!mask[key]) {\n shape[key] = this.shape[key];\n }\n });\n return new ZodObject({\n ...this._def,\n shape: () => shape,\n });\n }\n /**\n * @deprecated\n */\n deepPartial() {\n return deepPartialify(this);\n }\n partial(mask) {\n const newShape = {};\n util.objectKeys(this.shape).forEach((key) => {\n const fieldSchema = this.shape[key];\n if (mask && !mask[key]) {\n newShape[key] = fieldSchema;\n }\n else {\n newShape[key] = fieldSchema.optional();\n }\n });\n return new ZodObject({\n ...this._def,\n shape: () => newShape,\n });\n }\n required(mask) {\n const newShape = {};\n util.objectKeys(this.shape).forEach((key) => {\n if (mask && !mask[key]) {\n newShape[key] = this.shape[key];\n }\n else {\n const fieldSchema = this.shape[key];\n let newField = fieldSchema;\n while (newField instanceof ZodOptional) {\n newField = newField._def.innerType;\n }\n newShape[key] = newField;\n }\n });\n return new ZodObject({\n ...this._def,\n shape: () => newShape,\n });\n }\n keyof() {\n return createZodEnum(util.objectKeys(this.shape));\n }\n}\nZodObject.create = (shape, params) => {\n return new ZodObject({\n shape: () => shape,\n unknownKeys: \"strip\",\n catchall: ZodNever.create(),\n typeName: ZodFirstPartyTypeKind.ZodObject,\n ...processCreateParams(params),\n });\n};\nZodObject.strictCreate = (shape, params) => {\n return new ZodObject({\n shape: () => shape,\n unknownKeys: \"strict\",\n catchall: ZodNever.create(),\n typeName: ZodFirstPartyTypeKind.ZodObject,\n ...processCreateParams(params),\n });\n};\nZodObject.lazycreate = (shape, params) => {\n return new ZodObject({\n shape,\n unknownKeys: \"strip\",\n catchall: ZodNever.create(),\n typeName: ZodFirstPartyTypeKind.ZodObject,\n ...processCreateParams(params),\n });\n};\nclass ZodUnion extends ZodType {\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n const options = this._def.options;\n function handleResults(results) {\n // return first issue-free validation if it exists\n for (const result of results) {\n if (result.result.status === \"valid\") {\n return result.result;\n }\n }\n for (const result of results) {\n if (result.result.status === \"dirty\") {\n // add issues from dirty option\n ctx.common.issues.push(...result.ctx.common.issues);\n return result.result;\n }\n }\n // return invalid\n const unionErrors = results.map((result) => new ZodError(result.ctx.common.issues));\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_union,\n unionErrors,\n });\n return INVALID;\n }\n if (ctx.common.async) {\n return Promise.all(options.map(async (option) => {\n const childCtx = {\n ...ctx,\n common: {\n ...ctx.common,\n issues: [],\n },\n parent: null,\n };\n return {\n result: await option._parseAsync({\n data: ctx.data,\n path: ctx.path,\n parent: childCtx,\n }),\n ctx: childCtx,\n };\n })).then(handleResults);\n }\n else {\n let dirty = undefined;\n const issues = [];\n for (const option of options) {\n const childCtx = {\n ...ctx,\n common: {\n ...ctx.common,\n issues: [],\n },\n parent: null,\n };\n const result = option._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: childCtx,\n });\n if (result.status === \"valid\") {\n return result;\n }\n else if (result.status === \"dirty\" && !dirty) {\n dirty = { result, ctx: childCtx };\n }\n if (childCtx.common.issues.length) {\n issues.push(childCtx.common.issues);\n }\n }\n if (dirty) {\n ctx.common.issues.push(...dirty.ctx.common.issues);\n return dirty.result;\n }\n const unionErrors = issues.map((issues) => new ZodError(issues));\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_union,\n unionErrors,\n });\n return INVALID;\n }\n }\n get options() {\n return this._def.options;\n }\n}\nZodUnion.create = (types, params) => {\n return new ZodUnion({\n options: types,\n typeName: ZodFirstPartyTypeKind.ZodUnion,\n ...processCreateParams(params),\n });\n};\n/////////////////////////////////////////////////////\n/////////////////////////////////////////////////////\n////////// //////////\n////////// ZodDiscriminatedUnion //////////\n////////// //////////\n/////////////////////////////////////////////////////\n/////////////////////////////////////////////////////\nconst getDiscriminator = (type) => {\n if (type instanceof ZodLazy) {\n return getDiscriminator(type.schema);\n }\n else if (type instanceof ZodEffects) {\n return getDiscriminator(type.innerType());\n }\n else if (type instanceof ZodLiteral) {\n return [type.value];\n }\n else if (type instanceof ZodEnum) {\n return type.options;\n }\n else if (type instanceof ZodNativeEnum) {\n // eslint-disable-next-line ban/ban\n return Object.keys(type.enum);\n }\n else if (type instanceof ZodDefault) {\n return getDiscriminator(type._def.innerType);\n }\n else if (type instanceof ZodUndefined) {\n return [undefined];\n }\n else if (type instanceof ZodNull) {\n return [null];\n }\n else {\n return null;\n }\n};\nclass ZodDiscriminatedUnion extends ZodType {\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.object) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.object,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const discriminator = this.discriminator;\n const discriminatorValue = ctx.data[discriminator];\n const option = this.optionsMap.get(discriminatorValue);\n if (!option) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_union_discriminator,\n options: Array.from(this.optionsMap.keys()),\n path: [discriminator],\n });\n return INVALID;\n }\n if (ctx.common.async) {\n return option._parseAsync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n }\n else {\n return option._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n }\n }\n get discriminator() {\n return this._def.discriminator;\n }\n get options() {\n return this._def.options;\n }\n get optionsMap() {\n return this._def.optionsMap;\n }\n /**\n * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor.\n * However, it only allows a union of objects, all of which need to share a discriminator property. This property must\n * have a different value for each object in the union.\n * @param discriminator the name of the discriminator property\n * @param types an array of object schemas\n * @param params\n */\n static create(discriminator, options, params) {\n // Get all the valid discriminator values\n const optionsMap = new Map();\n // try {\n for (const type of options) {\n const discriminatorValues = getDiscriminator(type.shape[discriminator]);\n if (!discriminatorValues) {\n throw new Error(`A discriminator value for key \\`${discriminator}\\` could not be extracted from all schema options`);\n }\n for (const value of discriminatorValues) {\n if (optionsMap.has(value)) {\n throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`);\n }\n optionsMap.set(value, type);\n }\n }\n return new ZodDiscriminatedUnion({\n typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion,\n discriminator,\n options,\n optionsMap,\n ...processCreateParams(params),\n });\n }\n}\nfunction mergeValues(a, b) {\n const aType = getParsedType(a);\n const bType = getParsedType(b);\n if (a === b) {\n return { valid: true, data: a };\n }\n else if (aType === ZodParsedType.object && bType === ZodParsedType.object) {\n const bKeys = util.objectKeys(b);\n const sharedKeys = util\n .objectKeys(a)\n .filter((key) => bKeys.indexOf(key) !== -1);\n const newObj = { ...a, ...b };\n for (const key of sharedKeys) {\n const sharedValue = mergeValues(a[key], b[key]);\n if (!sharedValue.valid) {\n return { valid: false };\n }\n newObj[key] = sharedValue.data;\n }\n return { valid: true, data: newObj };\n }\n else if (aType === ZodParsedType.array && bType === ZodParsedType.array) {\n if (a.length !== b.length) {\n return { valid: false };\n }\n const newArray = [];\n for (let index = 0; index < a.length; index++) {\n const itemA = a[index];\n const itemB = b[index];\n const sharedValue = mergeValues(itemA, itemB);\n if (!sharedValue.valid) {\n return { valid: false };\n }\n newArray.push(sharedValue.data);\n }\n return { valid: true, data: newArray };\n }\n else if (aType === ZodParsedType.date &&\n bType === ZodParsedType.date &&\n +a === +b) {\n return { valid: true, data: a };\n }\n else {\n return { valid: false };\n }\n}\nclass ZodIntersection extends ZodType {\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n const handleParsed = (parsedLeft, parsedRight) => {\n if (isAborted(parsedLeft) || isAborted(parsedRight)) {\n return INVALID;\n }\n const merged = mergeValues(parsedLeft.value, parsedRight.value);\n if (!merged.valid) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_intersection_types,\n });\n return INVALID;\n }\n if (isDirty(parsedLeft) || isDirty(parsedRight)) {\n status.dirty();\n }\n return { status: status.value, value: merged.data };\n };\n if (ctx.common.async) {\n return Promise.all([\n this._def.left._parseAsync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n }),\n this._def.right._parseAsync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n }),\n ]).then(([left, right]) => handleParsed(left, right));\n }\n else {\n return handleParsed(this._def.left._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n }), this._def.right._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n }));\n }\n }\n}\nZodIntersection.create = (left, right, params) => {\n return new ZodIntersection({\n left: left,\n right: right,\n typeName: ZodFirstPartyTypeKind.ZodIntersection,\n ...processCreateParams(params),\n });\n};\nclass ZodTuple extends ZodType {\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.array) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.array,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n if (ctx.data.length < this._def.items.length) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: this._def.items.length,\n inclusive: true,\n exact: false,\n type: \"array\",\n });\n return INVALID;\n }\n const rest = this._def.rest;\n if (!rest && ctx.data.length > this._def.items.length) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: this._def.items.length,\n inclusive: true,\n exact: false,\n type: \"array\",\n });\n status.dirty();\n }\n const items = [...ctx.data]\n .map((item, itemIndex) => {\n const schema = this._def.items[itemIndex] || this._def.rest;\n if (!schema)\n return null;\n return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex));\n })\n .filter((x) => !!x); // filter nulls\n if (ctx.common.async) {\n return Promise.all(items).then((results) => {\n return ParseStatus.mergeArray(status, results);\n });\n }\n else {\n return ParseStatus.mergeArray(status, items);\n }\n }\n get items() {\n return this._def.items;\n }\n rest(rest) {\n return new ZodTuple({\n ...this._def,\n rest,\n });\n }\n}\nZodTuple.create = (schemas, params) => {\n if (!Array.isArray(schemas)) {\n throw new Error(\"You must pass an array of schemas to z.tuple([ ... ])\");\n }\n return new ZodTuple({\n items: schemas,\n typeName: ZodFirstPartyTypeKind.ZodTuple,\n rest: null,\n ...processCreateParams(params),\n });\n};\nclass ZodRecord extends ZodType {\n get keySchema() {\n return this._def.keyType;\n }\n get valueSchema() {\n return this._def.valueType;\n }\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.object) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.object,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const pairs = [];\n const keyType = this._def.keyType;\n const valueType = this._def.valueType;\n for (const key in ctx.data) {\n pairs.push({\n key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)),\n value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)),\n });\n }\n if (ctx.common.async) {\n return ParseStatus.mergeObjectAsync(status, pairs);\n }\n else {\n return ParseStatus.mergeObjectSync(status, pairs);\n }\n }\n get element() {\n return this._def.valueType;\n }\n static create(first, second, third) {\n if (second instanceof ZodType) {\n return new ZodRecord({\n keyType: first,\n valueType: second,\n typeName: ZodFirstPartyTypeKind.ZodRecord,\n ...processCreateParams(third),\n });\n }\n return new ZodRecord({\n keyType: ZodString.create(),\n valueType: first,\n typeName: ZodFirstPartyTypeKind.ZodRecord,\n ...processCreateParams(second),\n });\n }\n}\nclass ZodMap extends ZodType {\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.map) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.map,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const keyType = this._def.keyType;\n const valueType = this._def.valueType;\n const pairs = [...ctx.data.entries()].map(([key, value], index) => {\n return {\n key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, \"key\"])),\n value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, \"value\"])),\n };\n });\n if (ctx.common.async) {\n const finalMap = new Map();\n return Promise.resolve().then(async () => {\n for (const pair of pairs) {\n const key = await pair.key;\n const value = await pair.value;\n if (key.status === \"aborted\" || value.status === \"aborted\") {\n return INVALID;\n }\n if (key.status === \"dirty\" || value.status === \"dirty\") {\n status.dirty();\n }\n finalMap.set(key.value, value.value);\n }\n return { status: status.value, value: finalMap };\n });\n }\n else {\n const finalMap = new Map();\n for (const pair of pairs) {\n const key = pair.key;\n const value = pair.value;\n if (key.status === \"aborted\" || value.status === \"aborted\") {\n return INVALID;\n }\n if (key.status === \"dirty\" || value.status === \"dirty\") {\n status.dirty();\n }\n finalMap.set(key.value, value.value);\n }\n return { status: status.value, value: finalMap };\n }\n }\n}\nZodMap.create = (keyType, valueType, params) => {\n return new ZodMap({\n valueType,\n keyType,\n typeName: ZodFirstPartyTypeKind.ZodMap,\n ...processCreateParams(params),\n });\n};\nclass ZodSet extends ZodType {\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.set) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.set,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const def = this._def;\n if (def.minSize !== null) {\n if (ctx.data.size < def.minSize.value) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: def.minSize.value,\n type: \"set\",\n inclusive: true,\n exact: false,\n message: def.minSize.message,\n });\n status.dirty();\n }\n }\n if (def.maxSize !== null) {\n if (ctx.data.size > def.maxSize.value) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: def.maxSize.value,\n type: \"set\",\n inclusive: true,\n exact: false,\n message: def.maxSize.message,\n });\n status.dirty();\n }\n }\n const valueType = this._def.valueType;\n function finalizeSet(elements) {\n const parsedSet = new Set();\n for (const element of elements) {\n if (element.status === \"aborted\")\n return INVALID;\n if (element.status === \"dirty\")\n status.dirty();\n parsedSet.add(element.value);\n }\n return { status: status.value, value: parsedSet };\n }\n const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i)));\n if (ctx.common.async) {\n return Promise.all(elements).then((elements) => finalizeSet(elements));\n }\n else {\n return finalizeSet(elements);\n }\n }\n min(minSize, message) {\n return new ZodSet({\n ...this._def,\n minSize: { value: minSize, message: errorUtil.toString(message) },\n });\n }\n max(maxSize, message) {\n return new ZodSet({\n ...this._def,\n maxSize: { value: maxSize, message: errorUtil.toString(message) },\n });\n }\n size(size, message) {\n return this.min(size, message).max(size, message);\n }\n nonempty(message) {\n return this.min(1, message);\n }\n}\nZodSet.create = (valueType, params) => {\n return new ZodSet({\n valueType,\n minSize: null,\n maxSize: null,\n typeName: ZodFirstPartyTypeKind.ZodSet,\n ...processCreateParams(params),\n });\n};\nclass ZodFunction extends ZodType {\n constructor() {\n super(...arguments);\n this.validate = this.implement;\n }\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.function) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.function,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n function makeArgsIssue(args, error) {\n return makeIssue({\n data: args,\n path: ctx.path,\n errorMaps: [\n ctx.common.contextualErrorMap,\n ctx.schemaErrorMap,\n getErrorMap(),\n errorMap,\n ].filter((x) => !!x),\n issueData: {\n code: ZodIssueCode.invalid_arguments,\n argumentsError: error,\n },\n });\n }\n function makeReturnsIssue(returns, error) {\n return makeIssue({\n data: returns,\n path: ctx.path,\n errorMaps: [\n ctx.common.contextualErrorMap,\n ctx.schemaErrorMap,\n getErrorMap(),\n errorMap,\n ].filter((x) => !!x),\n issueData: {\n code: ZodIssueCode.invalid_return_type,\n returnTypeError: error,\n },\n });\n }\n const params = { errorMap: ctx.common.contextualErrorMap };\n const fn = ctx.data;\n if (this._def.returns instanceof ZodPromise) {\n return OK(async (...args) => {\n const error = new ZodError([]);\n const parsedArgs = await this._def.args\n .parseAsync(args, params)\n .catch((e) => {\n error.addIssue(makeArgsIssue(args, e));\n throw error;\n });\n const result = await fn(...parsedArgs);\n const parsedReturns = await this._def.returns._def.type\n .parseAsync(result, params)\n .catch((e) => {\n error.addIssue(makeReturnsIssue(result, e));\n throw error;\n });\n return parsedReturns;\n });\n }\n else {\n return OK((...args) => {\n const parsedArgs = this._def.args.safeParse(args, params);\n if (!parsedArgs.success) {\n throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);\n }\n const result = fn(...parsedArgs.data);\n const parsedReturns = this._def.returns.safeParse(result, params);\n if (!parsedReturns.success) {\n throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);\n }\n return parsedReturns.data;\n });\n }\n }\n parameters() {\n return this._def.args;\n }\n returnType() {\n return this._def.returns;\n }\n args(...items) {\n return new ZodFunction({\n ...this._def,\n args: ZodTuple.create(items).rest(ZodUnknown.create()),\n });\n }\n returns(returnType) {\n return new ZodFunction({\n ...this._def,\n returns: returnType,\n });\n }\n implement(func) {\n const validatedFunc = this.parse(func);\n return validatedFunc;\n }\n strictImplement(func) {\n const validatedFunc = this.parse(func);\n return validatedFunc;\n }\n static create(args, returns, params) {\n return new ZodFunction({\n args: (args\n ? args\n : ZodTuple.create([]).rest(ZodUnknown.create())),\n returns: returns || ZodUnknown.create(),\n typeName: ZodFirstPartyTypeKind.ZodFunction,\n ...processCreateParams(params),\n });\n }\n}\nclass ZodLazy extends ZodType {\n get schema() {\n return this._def.getter();\n }\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n const lazySchema = this._def.getter();\n return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx });\n }\n}\nZodLazy.create = (getter, params) => {\n return new ZodLazy({\n getter: getter,\n typeName: ZodFirstPartyTypeKind.ZodLazy,\n ...processCreateParams(params),\n });\n};\nclass ZodLiteral extends ZodType {\n _parse(input) {\n if (input.data !== this._def.value) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n received: ctx.data,\n code: ZodIssueCode.invalid_literal,\n expected: this._def.value,\n });\n return INVALID;\n }\n return { status: \"valid\", value: input.data };\n }\n get value() {\n return this._def.value;\n }\n}\nZodLiteral.create = (value, params) => {\n return new ZodLiteral({\n value: value,\n typeName: ZodFirstPartyTypeKind.ZodLiteral,\n ...processCreateParams(params),\n });\n};\nfunction createZodEnum(values, params) {\n return new ZodEnum({\n values: values,\n typeName: ZodFirstPartyTypeKind.ZodEnum,\n ...processCreateParams(params),\n });\n}\nclass ZodEnum extends ZodType {\n _parse(input) {\n if (typeof input.data !== \"string\") {\n const ctx = this._getOrReturnCtx(input);\n const expectedValues = this._def.values;\n addIssueToContext(ctx, {\n expected: util.joinValues(expectedValues),\n received: ctx.parsedType,\n code: ZodIssueCode.invalid_type,\n });\n return INVALID;\n }\n if (this._def.values.indexOf(input.data) === -1) {\n const ctx = this._getOrReturnCtx(input);\n const expectedValues = this._def.values;\n addIssueToContext(ctx, {\n received: ctx.data,\n code: ZodIssueCode.invalid_enum_value,\n options: expectedValues,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n get options() {\n return this._def.values;\n }\n get enum() {\n const enumValues = {};\n for (const val of this._def.values) {\n enumValues[val] = val;\n }\n return enumValues;\n }\n get Values() {\n const enumValues = {};\n for (const val of this._def.values) {\n enumValues[val] = val;\n }\n return enumValues;\n }\n get Enum() {\n const enumValues = {};\n for (const val of this._def.values) {\n enumValues[val] = val;\n }\n return enumValues;\n }\n extract(values) {\n return ZodEnum.create(values);\n }\n exclude(values) {\n return ZodEnum.create(this.options.filter((opt) => !values.includes(opt)));\n }\n}\nZodEnum.create = createZodEnum;\nclass ZodNativeEnum extends ZodType {\n _parse(input) {\n const nativeEnumValues = util.getValidEnumValues(this._def.values);\n const ctx = this._getOrReturnCtx(input);\n if (ctx.parsedType !== ZodParsedType.string &&\n ctx.parsedType !== ZodParsedType.number) {\n const expectedValues = util.objectValues(nativeEnumValues);\n addIssueToContext(ctx, {\n expected: util.joinValues(expectedValues),\n received: ctx.parsedType,\n code: ZodIssueCode.invalid_type,\n });\n return INVALID;\n }\n if (nativeEnumValues.indexOf(input.data) === -1) {\n const expectedValues = util.objectValues(nativeEnumValues);\n addIssueToContext(ctx, {\n received: ctx.data,\n code: ZodIssueCode.invalid_enum_value,\n options: expectedValues,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n get enum() {\n return this._def.values;\n }\n}\nZodNativeEnum.create = (values, params) => {\n return new ZodNativeEnum({\n values: values,\n typeName: ZodFirstPartyTypeKind.ZodNativeEnum,\n ...processCreateParams(params),\n });\n};\nclass ZodPromise extends ZodType {\n unwrap() {\n return this._def.type;\n }\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.promise &&\n ctx.common.async === false) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.promise,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const promisified = ctx.parsedType === ZodParsedType.promise\n ? ctx.data\n : Promise.resolve(ctx.data);\n return OK(promisified.then((data) => {\n return this._def.type.parseAsync(data, {\n path: ctx.path,\n errorMap: ctx.common.contextualErrorMap,\n });\n }));\n }\n}\nZodPromise.create = (schema, params) => {\n return new ZodPromise({\n type: schema,\n typeName: ZodFirstPartyTypeKind.ZodPromise,\n ...processCreateParams(params),\n });\n};\nclass ZodEffects extends ZodType {\n innerType() {\n return this._def.schema;\n }\n sourceType() {\n return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects\n ? this._def.schema.sourceType()\n : this._def.schema;\n }\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n const effect = this._def.effect || null;\n if (effect.type === \"preprocess\") {\n const processed = effect.transform(ctx.data);\n if (ctx.common.async) {\n return Promise.resolve(processed).then((processed) => {\n return this._def.schema._parseAsync({\n data: processed,\n path: ctx.path,\n parent: ctx,\n });\n });\n }\n else {\n return this._def.schema._parseSync({\n data: processed,\n path: ctx.path,\n parent: ctx,\n });\n }\n }\n const checkCtx = {\n addIssue: (arg) => {\n addIssueToContext(ctx, arg);\n if (arg.fatal) {\n status.abort();\n }\n else {\n status.dirty();\n }\n },\n get path() {\n return ctx.path;\n },\n };\n checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx);\n if (effect.type === \"refinement\") {\n const executeRefinement = (acc\n // effect: RefinementEffect\n ) => {\n const result = effect.refinement(acc, checkCtx);\n if (ctx.common.async) {\n return Promise.resolve(result);\n }\n if (result instanceof Promise) {\n throw new Error(\"Async refinement encountered during synchronous parse operation. Use .parseAsync instead.\");\n }\n return acc;\n };\n if (ctx.common.async === false) {\n const inner = this._def.schema._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n if (inner.status === \"aborted\")\n return INVALID;\n if (inner.status === \"dirty\")\n status.dirty();\n // return value is ignored\n executeRefinement(inner.value);\n return { status: status.value, value: inner.value };\n }\n else {\n return this._def.schema\n ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx })\n .then((inner) => {\n if (inner.status === \"aborted\")\n return INVALID;\n if (inner.status === \"dirty\")\n status.dirty();\n return executeRefinement(inner.value).then(() => {\n return { status: status.value, value: inner.value };\n });\n });\n }\n }\n if (effect.type === \"transform\") {\n if (ctx.common.async === false) {\n const base = this._def.schema._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n if (!isValid(base))\n return base;\n const result = effect.transform(base.value, checkCtx);\n if (result instanceof Promise) {\n throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);\n }\n return { status: status.value, value: result };\n }\n else {\n return this._def.schema\n ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx })\n .then((base) => {\n if (!isValid(base))\n return base;\n return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ status: status.value, value: result }));\n });\n }\n }\n util.assertNever(effect);\n }\n}\nZodEffects.create = (schema, effect, params) => {\n return new ZodEffects({\n schema,\n typeName: ZodFirstPartyTypeKind.ZodEffects,\n effect,\n ...processCreateParams(params),\n });\n};\nZodEffects.createWithPreprocess = (preprocess, schema, params) => {\n return new ZodEffects({\n schema,\n effect: { type: \"preprocess\", transform: preprocess },\n typeName: ZodFirstPartyTypeKind.ZodEffects,\n ...processCreateParams(params),\n });\n};\nclass ZodOptional extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType === ZodParsedType.undefined) {\n return OK(undefined);\n }\n return this._def.innerType._parse(input);\n }\n unwrap() {\n return this._def.innerType;\n }\n}\nZodOptional.create = (type, params) => {\n return new ZodOptional({\n innerType: type,\n typeName: ZodFirstPartyTypeKind.ZodOptional,\n ...processCreateParams(params),\n });\n};\nclass ZodNullable extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType === ZodParsedType.null) {\n return OK(null);\n }\n return this._def.innerType._parse(input);\n }\n unwrap() {\n return this._def.innerType;\n }\n}\nZodNullable.create = (type, params) => {\n return new ZodNullable({\n innerType: type,\n typeName: ZodFirstPartyTypeKind.ZodNullable,\n ...processCreateParams(params),\n });\n};\nclass ZodDefault extends ZodType {\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n let data = ctx.data;\n if (ctx.parsedType === ZodParsedType.undefined) {\n data = this._def.defaultValue();\n }\n return this._def.innerType._parse({\n data,\n path: ctx.path,\n parent: ctx,\n });\n }\n removeDefault() {\n return this._def.innerType;\n }\n}\nZodDefault.create = (type, params) => {\n return new ZodDefault({\n innerType: type,\n typeName: ZodFirstPartyTypeKind.ZodDefault,\n defaultValue: typeof params.default === \"function\"\n ? params.default\n : () => params.default,\n ...processCreateParams(params),\n });\n};\nclass ZodCatch extends ZodType {\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n // newCtx is used to not collect issues from inner types in ctx\n const newCtx = {\n ...ctx,\n common: {\n ...ctx.common,\n issues: [],\n },\n };\n const result = this._def.innerType._parse({\n data: newCtx.data,\n path: newCtx.path,\n parent: {\n ...newCtx,\n },\n });\n if (isAsync(result)) {\n return result.then((result) => {\n return {\n status: \"valid\",\n value: result.status === \"valid\"\n ? result.value\n : this._def.catchValue({\n get error() {\n return new ZodError(newCtx.common.issues);\n },\n input: newCtx.data,\n }),\n };\n });\n }\n else {\n return {\n status: \"valid\",\n value: result.status === \"valid\"\n ? result.value\n : this._def.catchValue({\n get error() {\n return new ZodError(newCtx.common.issues);\n },\n input: newCtx.data,\n }),\n };\n }\n }\n removeCatch() {\n return this._def.innerType;\n }\n}\nZodCatch.create = (type, params) => {\n return new ZodCatch({\n innerType: type,\n typeName: ZodFirstPartyTypeKind.ZodCatch,\n catchValue: typeof params.catch === \"function\" ? params.catch : () => params.catch,\n ...processCreateParams(params),\n });\n};\nclass ZodNaN extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.nan) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.nan,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return { status: \"valid\", value: input.data };\n }\n}\nZodNaN.create = (params) => {\n return new ZodNaN({\n typeName: ZodFirstPartyTypeKind.ZodNaN,\n ...processCreateParams(params),\n });\n};\nconst BRAND = Symbol(\"zod_brand\");\nclass ZodBranded extends ZodType {\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n const data = ctx.data;\n return this._def.type._parse({\n data,\n path: ctx.path,\n parent: ctx,\n });\n }\n unwrap() {\n return this._def.type;\n }\n}\nclass ZodPipeline extends ZodType {\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n if (ctx.common.async) {\n const handleAsync = async () => {\n const inResult = await this._def.in._parseAsync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n if (inResult.status === \"aborted\")\n return INVALID;\n if (inResult.status === \"dirty\") {\n status.dirty();\n return DIRTY(inResult.value);\n }\n else {\n return this._def.out._parseAsync({\n data: inResult.value,\n path: ctx.path,\n parent: ctx,\n });\n }\n };\n return handleAsync();\n }\n else {\n const inResult = this._def.in._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n if (inResult.status === \"aborted\")\n return INVALID;\n if (inResult.status === \"dirty\") {\n status.dirty();\n return {\n status: \"dirty\",\n value: inResult.value,\n };\n }\n else {\n return this._def.out._parseSync({\n data: inResult.value,\n path: ctx.path,\n parent: ctx,\n });\n }\n }\n }\n static create(a, b) {\n return new ZodPipeline({\n in: a,\n out: b,\n typeName: ZodFirstPartyTypeKind.ZodPipeline,\n });\n }\n}\nconst custom = (check, params = {}, \n/*\n * @deprecated\n *\n * Pass `fatal` into the params object instead:\n *\n * ```ts\n * z.string().custom((val) => val.length > 5, { fatal: false })\n * ```\n *\n */\nfatal) => {\n if (check)\n return ZodAny.create().superRefine((data, ctx) => {\n var _a, _b;\n if (!check(data)) {\n const p = typeof params === \"function\"\n ? params(data)\n : typeof params === \"string\"\n ? { message: params }\n : params;\n const _fatal = (_b = (_a = p.fatal) !== null && _a !== void 0 ? _a : fatal) !== null && _b !== void 0 ? _b : true;\n const p2 = typeof p === \"string\" ? { message: p } : p;\n ctx.addIssue({ code: \"custom\", ...p2, fatal: _fatal });\n }\n });\n return ZodAny.create();\n};\nconst late = {\n object: ZodObject.lazycreate,\n};\nvar ZodFirstPartyTypeKind;\n(function (ZodFirstPartyTypeKind) {\n ZodFirstPartyTypeKind[\"ZodString\"] = \"ZodString\";\n ZodFirstPartyTypeKind[\"ZodNumber\"] = \"ZodNumber\";\n ZodFirstPartyTypeKind[\"ZodNaN\"] = \"ZodNaN\";\n ZodFirstPartyTypeKind[\"ZodBigInt\"] = \"ZodBigInt\";\n ZodFirstPartyTypeKind[\"ZodBoolean\"] = \"ZodBoolean\";\n ZodFirstPartyTypeKind[\"ZodDate\"] = \"ZodDate\";\n ZodFirstPartyTypeKind[\"ZodSymbol\"] = \"ZodSymbol\";\n ZodFirstPartyTypeKind[\"ZodUndefined\"] = \"ZodUndefined\";\n ZodFirstPartyTypeKind[\"ZodNull\"] = \"ZodNull\";\n ZodFirstPartyTypeKind[\"ZodAny\"] = \"ZodAny\";\n ZodFirstPartyTypeKind[\"ZodUnknown\"] = \"ZodUnknown\";\n ZodFirstPartyTypeKind[\"ZodNever\"] = \"ZodNever\";\n ZodFirstPartyTypeKind[\"ZodVoid\"] = \"ZodVoid\";\n ZodFirstPartyTypeKind[\"ZodArray\"] = \"ZodArray\";\n ZodFirstPartyTypeKind[\"ZodObject\"] = \"ZodObject\";\n ZodFirstPartyTypeKind[\"ZodUnion\"] = \"ZodUnion\";\n ZodFirstPartyTypeKind[\"ZodDiscriminatedUnion\"] = \"ZodDiscriminatedUnion\";\n ZodFirstPartyTypeKind[\"ZodIntersection\"] = \"ZodIntersection\";\n ZodFirstPartyTypeKind[\"ZodTuple\"] = \"ZodTuple\";\n ZodFirstPartyTypeKind[\"ZodRecord\"] = \"ZodRecord\";\n ZodFirstPartyTypeKind[\"ZodMap\"] = \"ZodMap\";\n ZodFirstPartyTypeKind[\"ZodSet\"] = \"ZodSet\";\n ZodFirstPartyTypeKind[\"ZodFunction\"] = \"ZodFunction\";\n ZodFirstPartyTypeKind[\"ZodLazy\"] = \"ZodLazy\";\n ZodFirstPartyTypeKind[\"ZodLiteral\"] = \"ZodLiteral\";\n ZodFirstPartyTypeKind[\"ZodEnum\"] = \"ZodEnum\";\n ZodFirstPartyTypeKind[\"ZodEffects\"] = \"ZodEffects\";\n ZodFirstPartyTypeKind[\"ZodNativeEnum\"] = \"ZodNativeEnum\";\n ZodFirstPartyTypeKind[\"ZodOptional\"] = \"ZodOptional\";\n ZodFirstPartyTypeKind[\"ZodNullable\"] = \"ZodNullable\";\n ZodFirstPartyTypeKind[\"ZodDefault\"] = \"ZodDefault\";\n ZodFirstPartyTypeKind[\"ZodCatch\"] = \"ZodCatch\";\n ZodFirstPartyTypeKind[\"ZodPromise\"] = \"ZodPromise\";\n ZodFirstPartyTypeKind[\"ZodBranded\"] = \"ZodBranded\";\n ZodFirstPartyTypeKind[\"ZodPipeline\"] = \"ZodPipeline\";\n})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));\nconst instanceOfType = (\n// const instanceOfType = any>(\ncls, params = {\n message: `Input not instance of ${cls.name}`,\n}) => custom((data) => data instanceof cls, params);\nconst stringType = ZodString.create;\nconst numberType = ZodNumber.create;\nconst nanType = ZodNaN.create;\nconst bigIntType = ZodBigInt.create;\nconst booleanType = ZodBoolean.create;\nconst dateType = ZodDate.create;\nconst symbolType = ZodSymbol.create;\nconst undefinedType = ZodUndefined.create;\nconst nullType = ZodNull.create;\nconst anyType = ZodAny.create;\nconst unknownType = ZodUnknown.create;\nconst neverType = ZodNever.create;\nconst voidType = ZodVoid.create;\nconst arrayType = ZodArray.create;\nconst objectType = ZodObject.create;\nconst strictObjectType = ZodObject.strictCreate;\nconst unionType = ZodUnion.create;\nconst discriminatedUnionType = ZodDiscriminatedUnion.create;\nconst intersectionType = ZodIntersection.create;\nconst tupleType = ZodTuple.create;\nconst recordType = ZodRecord.create;\nconst mapType = ZodMap.create;\nconst setType = ZodSet.create;\nconst functionType = ZodFunction.create;\nconst lazyType = ZodLazy.create;\nconst literalType = ZodLiteral.create;\nconst enumType = ZodEnum.create;\nconst nativeEnumType = ZodNativeEnum.create;\nconst promiseType = ZodPromise.create;\nconst effectsType = ZodEffects.create;\nconst optionalType = ZodOptional.create;\nconst nullableType = ZodNullable.create;\nconst preprocessType = ZodEffects.createWithPreprocess;\nconst pipelineType = ZodPipeline.create;\nconst ostring = () => stringType().optional();\nconst onumber = () => numberType().optional();\nconst oboolean = () => booleanType().optional();\nconst coerce = {\n string: ((arg) => ZodString.create({ ...arg, coerce: true })),\n number: ((arg) => ZodNumber.create({ ...arg, coerce: true })),\n boolean: ((arg) => ZodBoolean.create({\n ...arg,\n coerce: true,\n })),\n bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })),\n date: ((arg) => ZodDate.create({ ...arg, coerce: true })),\n};\nconst NEVER = INVALID;\n\nvar z = /*#__PURE__*/Object.freeze({\n __proto__: null,\n defaultErrorMap: errorMap,\n setErrorMap: setErrorMap,\n getErrorMap: getErrorMap,\n makeIssue: makeIssue,\n EMPTY_PATH: EMPTY_PATH,\n addIssueToContext: addIssueToContext,\n ParseStatus: ParseStatus,\n INVALID: INVALID,\n DIRTY: DIRTY,\n OK: OK,\n isAborted: isAborted,\n isDirty: isDirty,\n isValid: isValid,\n isAsync: isAsync,\n get util () { return util; },\n get objectUtil () { return objectUtil; },\n ZodParsedType: ZodParsedType,\n getParsedType: getParsedType,\n ZodType: ZodType,\n ZodString: ZodString,\n ZodNumber: ZodNumber,\n ZodBigInt: ZodBigInt,\n ZodBoolean: ZodBoolean,\n ZodDate: ZodDate,\n ZodSymbol: ZodSymbol,\n ZodUndefined: ZodUndefined,\n ZodNull: ZodNull,\n ZodAny: ZodAny,\n ZodUnknown: ZodUnknown,\n ZodNever: ZodNever,\n ZodVoid: ZodVoid,\n ZodArray: ZodArray,\n ZodObject: ZodObject,\n ZodUnion: ZodUnion,\n ZodDiscriminatedUnion: ZodDiscriminatedUnion,\n ZodIntersection: ZodIntersection,\n ZodTuple: ZodTuple,\n ZodRecord: ZodRecord,\n ZodMap: ZodMap,\n ZodSet: ZodSet,\n ZodFunction: ZodFunction,\n ZodLazy: ZodLazy,\n ZodLiteral: ZodLiteral,\n ZodEnum: ZodEnum,\n ZodNativeEnum: ZodNativeEnum,\n ZodPromise: ZodPromise,\n ZodEffects: ZodEffects,\n ZodTransformer: ZodEffects,\n ZodOptional: ZodOptional,\n ZodNullable: ZodNullable,\n ZodDefault: ZodDefault,\n ZodCatch: ZodCatch,\n ZodNaN: ZodNaN,\n BRAND: BRAND,\n ZodBranded: ZodBranded,\n ZodPipeline: ZodPipeline,\n custom: custom,\n Schema: ZodType,\n ZodSchema: ZodType,\n late: late,\n get ZodFirstPartyTypeKind () { return ZodFirstPartyTypeKind; },\n coerce: coerce,\n any: anyType,\n array: arrayType,\n bigint: bigIntType,\n boolean: booleanType,\n date: dateType,\n discriminatedUnion: discriminatedUnionType,\n effect: effectsType,\n 'enum': enumType,\n 'function': functionType,\n 'instanceof': instanceOfType,\n intersection: intersectionType,\n lazy: lazyType,\n literal: literalType,\n map: mapType,\n nan: nanType,\n nativeEnum: nativeEnumType,\n never: neverType,\n 'null': nullType,\n nullable: nullableType,\n number: numberType,\n object: objectType,\n oboolean: oboolean,\n onumber: onumber,\n optional: optionalType,\n ostring: ostring,\n pipeline: pipelineType,\n preprocess: preprocessType,\n promise: promiseType,\n record: recordType,\n set: setType,\n strictObject: strictObjectType,\n string: stringType,\n symbol: symbolType,\n transformer: effectsType,\n tuple: tupleType,\n 'undefined': undefinedType,\n union: unionType,\n unknown: unknownType,\n 'void': voidType,\n NEVER: NEVER,\n ZodIssueCode: ZodIssueCode,\n quotelessJson: quotelessJson,\n ZodError: ZodError\n});\n\nexport { BRAND, DIRTY, EMPTY_PATH, INVALID, NEVER, OK, ParseStatus, ZodType as Schema, ZodAny, ZodArray, ZodBigInt, ZodBoolean, ZodBranded, ZodCatch, ZodDate, ZodDefault, ZodDiscriminatedUnion, ZodEffects, ZodEnum, ZodError, ZodFirstPartyTypeKind, ZodFunction, ZodIntersection, ZodIssueCode, ZodLazy, ZodLiteral, ZodMap, ZodNaN, ZodNativeEnum, ZodNever, ZodNull, ZodNullable, ZodNumber, ZodObject, ZodOptional, ZodParsedType, ZodPipeline, ZodPromise, ZodRecord, ZodType as ZodSchema, ZodSet, ZodString, ZodSymbol, ZodEffects as ZodTransformer, ZodTuple, ZodType, ZodUndefined, ZodUnion, ZodUnknown, ZodVoid, addIssueToContext, anyType as any, arrayType as array, bigIntType as bigint, booleanType as boolean, coerce, custom, dateType as date, z as default, errorMap as defaultErrorMap, discriminatedUnionType as discriminatedUnion, effectsType as effect, enumType as enum, functionType as function, getErrorMap, getParsedType, instanceOfType as instanceof, intersectionType as intersection, isAborted, isAsync, isDirty, isValid, late, lazyType as lazy, literalType as literal, makeIssue, mapType as map, nanType as nan, nativeEnumType as nativeEnum, neverType as never, nullType as null, nullableType as nullable, numberType as number, objectType as object, objectUtil, oboolean, onumber, optionalType as optional, ostring, pipelineType as pipeline, preprocessType as preprocess, promiseType as promise, quotelessJson, recordType as record, setType as set, setErrorMap, strictObjectType as strictObject, stringType as string, symbolType as symbol, effectsType as transformer, tupleType as tuple, undefinedType as undefined, unionType as union, unknownType as unknown, util, voidType as void, z };\n","'use strict';\n\n/// \n/// \n/// \n\nimport {AmeCustomizable, AmeCustomizableViewModel} from '../../pro-customizables/assets/customizable.js';\nimport {registerBaseComponents} from '../../pro-customizables/ko-components/ame-components.js';\nimport AmeAcStructure from './ko-components/ame-ac-structure.js';\nimport AmeAcSection from './ko-components/ame-ac-section.js';\nimport AmeAcSectionLink from './ko-components/ame-ac-section-link.js';\nimport AmeAcControl from './ko-components/ame-ac-control.js';\nimport AmeAcControlGroup from './ko-components/ame-ac-control-group.js';\nimport AmeAcContentSection from './ko-components/ame-ac-content-section.js';\nimport {AmeAdminCustomizerBase} from './admin-customizer-base.js';\nimport AmeAcSeparator from './ko-components/ame-ac-separator.js';\nimport AmeAcValidationErrors from './ko-components/ame-ac-validation-errors.js';\nimport z, {ZodError, ZodType} from '../../zod/lib/index.js';\n\ndeclare var wsAmeLodash: _.LoDashStatic;\ndeclare const wsAmeAdminCustomizerData: AmeAdminCustomizer.ScriptData;\n\nexport namespace AmeAdminCustomizer {\n\timport Setting = AmeCustomizable.Setting;\n\timport SettingCollection = AmeCustomizable.SettingCollection;\n\timport InterfaceStructureData = AmeCustomizable.InterfaceStructureData;\n\timport InterfaceStructure = AmeCustomizable.InterfaceStructure;\n\timport unserializeUiElement = AmeCustomizable.unserializeUiElement;\n\timport unserializeSetting = AmeCustomizable.unserializeSetting;\n\timport AnySpecificElementData = AmeCustomizable.AnySpecificElementData;\n\timport CustomizableVmInterface = AmeCustomizableViewModel.CustomizableVmInterface;\n\n\tconst $ = jQuery;\n\tconst _ = wsAmeLodash;\n\n\tregisterBaseComponents();\n\tko.components.register('ame-ac-structure', AmeAcStructure);\n\tko.components.register('ame-ac-section', AmeAcSection);\n\tko.components.register('ame-ac-section-link', AmeAcSectionLink);\n\tko.components.register('ame-ac-content-section', AmeAcContentSection);\n\tko.components.register('ame-ac-control-group', AmeAcControlGroup);\n\tko.components.register('ame-ac-control', AmeAcControl);\n\tko.components.register('ame-ac-separator', AmeAcSeparator);\n\tko.components.register('ame-ac-validation-errors', AmeAcValidationErrors);\n\n\texport interface ScriptData extends AmeAdminCustomizerBase.ScriptData {\n\t\tajaxUrl: string;\n\t\tsaveChangesetNonce: string;\n\t\ttrashChangesetNonce: string;\n\t\tchangesetItemCount: number;\n\t\tchangesetStatus: string;\n\t\trefreshPreviewNonce: string;\n\t\tinitialPreviewUrl: string;\n\t\tinterfaceStructure: InterfaceStructureData;\n\t}\n\n\tconst reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n\tlet prefersReducedMotion = reducedMotionQuery && reducedMotionQuery.matches;\n\treducedMotionQuery.addEventListener('change', () => {\n\t\tprefersReducedMotion = reducedMotionQuery.matches;\n\t});\n\n\tclass CustomizerSettingsCollection extends SettingCollection {\n\t\t/**\n\t\t * Settings that have changed since the last save attempt.\n\t\t */\n\t\tprivate pendingSettings: Record = {};\n\t\t/**\n\t\t * Settings that in the process of being sent to the server to be saved.\n\t\t * They might not be saved yet.\n\t\t */\n\t\tprivate sentSettings: Record = {};\n\t\tprivate currentChangesetRequest: JQueryXHR | null = null;\n\t\tprivate saveTriggerTimeoutId: null | ReturnType = null;\n\n\t\tprivate readonly currentChangeset: KnockoutObservable;\n\n\t\tpublic readonly changesetName: KnockoutComputed;\n\n\t\tpublic readonly isExclusiveOperationInProgress: KnockoutComputed;\n\t\tprivate readonly exclusiveOperation: KnockoutObservable = ko.observable(false);\n\n\t\tconstructor(\n\t\t\tpublic readonly ajaxUrl: string,\n\t\t\tpublic readonly saveChangesetNonce: string,\n\t\t\tpublic readonly trashChangesetNonce: string,\n\t\t\tchangesetName: string,\n\t\t\tchangesetItemCount: number = 0,\n\t\t\tchangesetStatus: string | null = null\n\t\t) {\n\t\t\tsuper();\n\t\t\tconst self = this;\n\n\t\t\tthis.currentChangeset = ko.observable(\n\t\t\t\tnew Changeset(changesetName, changesetItemCount, changesetStatus)\n\t\t\t);\n\t\t\tthis.changesetName = ko.pureComputed(() => {\n\t\t\t\treturn (self.currentChangeset()?.name()) || '';\n\t\t\t});\n\n\t\t\t//Automatically save the changeset when any settings change.\n\t\t\tconst totalChangeCount = ko.pureComputed(() => {\n\t\t\t\tconst changeset = self.currentChangeset();\n\t\t\t\treturn (changeset ? changeset.currentSessionChanges() : 0);\n\t\t\t});\n\t\t\ttotalChangeCount.subscribe(_.debounce(\n\t\t\t\t(counter) => {\n\t\t\t\t\tif (counter > 0) {\n\t\t\t\t\t\tself.queueChangesetUpdate()\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t3000,\n\t\t\t\t{leading: true, trailing: true}\n\t\t\t));\n\n\t\t\tthis.isExclusiveOperationInProgress = ko.pureComputed(() => {\n\t\t\t\treturn self.exclusiveOperation();\n\t\t\t});\n\n\t\t\t//Keep track of unsaved changes and changesets.\n\t\t\tthis.addChangeListener((setting: Setting) => {\n\t\t\t\tthis.pendingSettings[setting.id] = setting;\n\n\t\t\t\tlet changeset = this.currentChangeset();\n\t\t\t\t//If the current changeset cannot be modified, create a new one\n\t\t\t\t//for the changed setting(s).\n\t\t\t\tif (!changeset?.canBeModified()) {\n\t\t\t\t\tchangeset = new Changeset();\n\t\t\t\t\tthis.currentChangeset(changeset);\n\t\t\t\t}\n\t\t\t\t//Track the number of changes in the current session.\n\t\t\t\tchangeset.currentSessionChanges(changeset.currentSessionChanges() + 1);\n\t\t\t});\n\t\t}\n\n\t\tqueueChangesetUpdate(delay: number = 0) {\n\t\t\tif (delay > 0) {\n\t\t\t\tif (this.saveTriggerTimeoutId !== null) {\n\t\t\t\t\t//Replace the existing timeout with a new one.\n\t\t\t\t\tclearTimeout(this.saveTriggerTimeoutId);\n\t\t\t\t}\n\t\t\t\tthis.saveTriggerTimeoutId = setTimeout(() => {\n\t\t\t\t\tthis.saveTriggerTimeoutId = null;\n\t\t\t\t\tthis.queueChangesetUpdate(0);\n\t\t\t\t}, delay);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this.saveTriggerTimeoutId !== null) {\n\t\t\t\treturn; //Another timeout is already waiting.\n\t\t\t}\n\n\t\t\tif (this.currentChangesetRequest !== null) {\n\t\t\t\t//There's an in-progress request, so wait until it's done.\n\t\t\t\tthis.currentChangesetRequest.always(() => {\n\t\t\t\t\t//Wait a bit to avoid hammering the server.\n\t\t\t\t\tthis.queueChangesetUpdate(1000);\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.saveChangeset();\n\t\t}\n\n\t\tprivate saveChangeset(status: string | null = null): JQueryPromise {\n\t\t\t//Do nothing if there are no changes.\n\t\t\tif (_.isEmpty(this.pendingSettings) && (status === null)) {\n\t\t\t\treturn $.Deferred().reject(new Error('There are no changes to save.')).promise();\n\t\t\t}\n\n\t\t\tif (this.isExclusiveOperationInProgress()) {\n\t\t\t\treturn $.Deferred().reject(\n\t\t\t\t\tnew Error('Another exclusive changeset operation is in progress.')\n\t\t\t\t).promise();\n\t\t\t}\n\n\t\t\tlet isExclusiveRequest = (status === 'publish') || (status === 'trash');\n\t\t\tif (isExclusiveRequest) {\n\t\t\t\tthis.exclusiveOperation(true);\n\t\t\t}\n\n\t\t\tconst savedChangeset = this.currentChangeset();\n\n\t\t\t//Keep a local copy of the settings in case something changes instance\n\t\t\t//properties while the request is in progress (should never happen).\n\t\t\tconst settingsToSend = this.pendingSettings;\n\t\t\tthis.sentSettings = settingsToSend;\n\t\t\tthis.pendingSettings = {};\n\n\t\t\tconst modifiedSettings = _.mapValues(settingsToSend, setting => setting.value());\n\t\t\tconst requestData: Record = {\n\t\t\t\taction: 'ws_ame_ac_save_changeset',\n\t\t\t\t_ajax_nonce: this.saveChangesetNonce,\n\t\t\t\tchangeset: savedChangeset?.name ?? '',\n\t\t\t\tmodified: JSON.stringify(modifiedSettings),\n\t\t\t};\n\t\t\tif (status !== null) {\n\t\t\t\trequestData['status'] = status;\n\t\t\t}\n\t\t\t//If the changeset doesn't have a name, it is new.\n\t\t\tif (!savedChangeset?.hasName()) {\n\t\t\t\trequestData['createNew'] = 1;\n\t\t\t}\n\n\t\t\tconst request = $.ajax({\n\t\t\t\turl: this.ajaxUrl,\n\t\t\t\tmethod: 'POST',\n\t\t\t\tdata: requestData,\n\t\t\t\tdataType: 'json',\n\t\t\t\ttimeout: 20000,\n\t\t\t});\n\t\t\tthis.currentChangesetRequest = request;\n\n\t\t\tinterface ServerValidationResults {\n\t\t\t\t[settingId: string]: {\n\t\t\t\t\tisValid: boolean;\n\t\t\t\t\terrors: Array<{ code: string; message: string; }>;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst self = this;\n\n\t\t\tfunction storeValidationResultsFrom(serverResponse: any) {\n\t\t\t\tconst results: ServerValidationResults = _.get(\n\t\t\t\t\tserverResponse,\n\t\t\t\t\t['data', 'validationResults']\n\t\t\t\t);\n\t\t\t\tif (typeof results !== 'object') {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tfor (const settingId in results) {\n\t\t\t\t\tconst setting = self.get(settingId);\n\t\t\t\t\tif (!setting.isDefined()) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!modifiedSettings.hasOwnProperty(settingId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst sentValue = modifiedSettings[settingId];\n\n\t\t\t\t\tconst state = results[settingId];\n\t\t\t\t\tif (state.isValid) {\n\t\t\t\t\t\tsetting.get().clearValidationErrorsForValue(sentValue);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t//Since the server response is not fully validated, some typeof checks\n\t\t\t\t\t\t//are still useful.\n\t\t\t\t\t\t// noinspection SuspiciousTypeOfGuard\n\t\t\t\t\t\tsetting.get().addValidationErrorsForValue(\n\t\t\t\t\t\t\tsentValue,\n\t\t\t\t\t\t\t_.filter(state.errors, error => (typeof error.message === 'string'))\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction storeChangesetDetailsFrom(serverResponse: any) {\n\t\t\t\tif (!savedChangeset) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t//Store the returned changeset name in case a new changeset was created.\n\t\t\t\tif (!savedChangeset.hasName()) {\n\t\t\t\t\tconst newName = _.get(serverResponse, ['data', 'changeset']);\n\t\t\t\t\tif (typeof newName === 'string') {\n\t\t\t\t\t\tsavedChangeset.name(newName);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//Store the changeset status.\n\t\t\t\tconst newStatus = _.get(serverResponse, ['data', 'changesetStatus']);\n\t\t\t\tif (typeof newStatus === 'string') {\n\t\t\t\t\tsavedChangeset.status(newStatus);\n\t\t\t\t}\n\n\t\t\t\t//Store the number of changes in the changeset.\n\t\t\t\tconst newChangeCount = _.get(serverResponse, ['data', 'changesetItemCount']);\n\t\t\t\tif (typeof newChangeCount === 'number') {\n\t\t\t\t\tsavedChangeset.knownItemCount(newChangeCount);\n\t\t\t\t}\n\n\t\t\t\t//Was the changeset published? Because changesets are typically moved\n\t\t\t\t//to trash after publishing, \"status\" might be \"trash\" instead of \"publish\",\n\t\t\t\t//but we still want to know if it was successfully published.\n\t\t\t\tconst wasPublished = _.get(serverResponse, ['data', 'changesetWasPublished'], null);\n\t\t\t\tif (wasPublished) {\n\t\t\t\t\tsavedChangeset.wasPublished(wasPublished);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trequest.done(function (response) {\n\t\t\t\tstoreChangesetDetailsFrom(response);\n\t\t\t\tstoreValidationResultsFrom(response);\n\n\t\t\t\t//After successfully publishing a changeset, it has no more\n\t\t\t\t//unsaved changes.\n\t\t\t\tconst isPublished =\n\t\t\t\t\t(savedChangeset.status() === 'publish')\n\t\t\t\t\t|| (savedChangeset.status() === 'future')\n\t\t\t\t\t|| (savedChangeset.wasPublished());\n\t\t\t\tif (isPublished) {\n\t\t\t\t\tsavedChangeset.currentSessionChanges(0);\n\t\t\t\t}\n\n\t\t\t\t//After a changeset is published or trashed, it can no longer\n\t\t\t\t//be edited. We may be able to replace it with a new changeset\n\t\t\t\t//that was created on the server.\n\t\t\t\tif (!self.currentChangeset().canBeModified()) {\n\t\t\t\t\tconst nextChangeset = _.get(response, ['data', 'nextChangeset']);\n\t\t\t\t\tif ((typeof nextChangeset === 'string') && (nextChangeset !== '')) {\n\t\t\t\t\t\tself.currentChangeset(new Changeset(nextChangeset));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\trequest.fail((requestObject: JQueryXHR) => {\n\t\t\t\tif (typeof requestObject.responseJSON === 'object') {\n\t\t\t\t\tstoreValidationResultsFrom(requestObject.responseJSON);\n\t\t\t\t\tstoreChangesetDetailsFrom(requestObject.responseJSON);\n\t\t\t\t}\n\n\t\t\t\t//Add the unsaved settings back to the pending list.\n\t\t\t\tfor (const id in settingsToSend) {\n\t\t\t\t\t//Keep only settings that still exist.\n\t\t\t\t\tif (this.get(id).isDefined()) {\n\t\t\t\t\t\tthis.pendingSettings[id] = settingsToSend[id];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t//We don't automatically retry because the problem might be something\n\t\t\t\t//that doesn't get better on its own, like missing permissions.\n\t\t\t});\n\n\t\t\trequest.always(() => {\n\t\t\t\tthis.currentChangesetRequest = null;\n\t\t\t\tthis.sentSettings = {};\n\t\t\t\tif (isExclusiveRequest) {\n\t\t\t\t\tthis.exclusiveOperation(false);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn request;\n\t\t}\n\n\t\tpublic savePendingSettings(timeout: number = 20): JQueryPromise {\n\t\t\tif (this.isExclusiveOperationInProgress()) {\n\t\t\t\t//Wait for the exclusive operation to finish.\n\t\t\t\tconst deferred = $.Deferred();\n\t\t\t\tconst result = deferred.then(() => this.doSavePendingSettings());\n\n\t\t\t\tconst startTime = Date.now();\n\t\t\t\tconst timer = setInterval(() => {\n\t\t\t\t\tif (!this.isExclusiveOperationInProgress()) {\n\t\t\t\t\t\tclearInterval(timer);\n\t\t\t\t\t\tdeferred.resolve();\n\t\t\t\t\t} else if ((Date.now() - startTime) > timeout) {\n\t\t\t\t\t\tclearInterval(timer);\n\t\t\t\t\t\tdeferred.reject(new Error('Exclusive operation timed out.'));\n\t\t\t\t\t}\n\t\t\t\t}, 200);\n\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\treturn this.doSavePendingSettings();\n\t\t}\n\n\t\tprivate doSavePendingSettings(): JQueryPromise {\n\t\t\t//If there are no changes, we don't need to do anything.\n\t\t\tif (_.isEmpty(this.pendingSettings)) {\n\t\t\t\treturn $.Deferred().resolve().promise();\n\t\t\t}\n\t\t\treturn this.saveChangeset();\n\t\t}\n\n\t\tpublic getCurrentChangeset(): Changeset {\n\t\t\treturn this.currentChangeset();\n\t\t}\n\n\t\t/**\n\t\t * Get any unsaved setting changes.\n\t\t *\n\t\t * @returns An object mapping setting IDs to their modified values.\n\t\t */\n\t\tpublic get unsavedChanges(): Record {\n\t\t\t//Include both pending settings and sent settings. Sent settings\n\t\t\t//might not be saved yet.\n\t\t\tlet unsavedSettings: Record = {};\n\t\t\t_.defaults(unsavedSettings, this.pendingSettings, this.sentSettings);\n\n\t\t\treturn _.mapValues(unsavedSettings, setting => setting.value());\n\t\t}\n\n\t\tpublic publishChangeset(): JQueryPromise {\n\t\t\tif (this.isExclusiveOperationInProgress()) {\n\t\t\t\treturn $.Deferred()\n\t\t\t\t\t.reject(new Error('Another exclusive changeset operation is already in progress.'))\n\t\t\t\t\t.promise();\n\t\t\t}\n\t\t\treturn this.saveChangeset('publish');\n\t\t}\n\n\t\tpublic trashChangeset(): JQueryPromise {\n\t\t\tif (this.isExclusiveOperationInProgress()) {\n\t\t\t\treturn $.Deferred()\n\t\t\t\t\t.reject(new Error('Another exclusive changeset operation is already in progress.'))\n\t\t\t\t\t.promise();\n\t\t\t}\n\n\t\t\tconst changeset = this.currentChangeset();\n\t\t\tif (!changeset.hasName()) {\n\t\t\t\t//The changeset hasn't been saved yet, so we can just mark it as trashed.\n\t\t\t\tchangeset.status('trash');\n\t\t\t\tchangeset.currentSessionChanges(0);\n\n\t\t\t\t//It's a success of sorts.\n\t\t\t\treturn $.Deferred().resolve(true).promise();\n\t\t\t}\n\n\t\t\tthis.exclusiveOperation(true);\n\n\t\t\tconst requestData: Record = {\n\t\t\t\taction: 'ws_ame_ac_trash_changeset',\n\t\t\t\t_ajax_nonce: this.trashChangesetNonce,\n\t\t\t\tchangeset: changeset.name\n\t\t\t};\n\n\t\t\tconst request = $.ajax({\n\t\t\t\turl: this.ajaxUrl,\n\t\t\t\tmethod: 'POST',\n\t\t\t\tdata: requestData,\n\t\t\t\tdataType: 'json',\n\t\t\t\ttimeout: 20000,\n\t\t\t});\n\t\t\tthis.currentChangesetRequest = request;\n\n\t\t\trequest.done(function () {\n\t\t\t\tchangeset.status('trash');\n\t\t\t\tchangeset.currentSessionChanges(0);\n\t\t\t});\n\n\t\t\t//Unfortunately, jQuery doesn't seem to allow us to create a custom\n\t\t\t//error object and pass it to other handlers, so code that uses this\n\t\t\t//method will have to parse the error response itself.\n\n\t\t\trequest.always(() => {\n\t\t\t\tthis.currentChangesetRequest = null;\n\t\t\t\tthis.exclusiveOperation(false);\n\t\t\t});\n\n\t\t\treturn request;\n\t\t}\n\t}\n\n\tclass Changeset {\n\t\tpublic readonly name: KnockoutObservable;\n\t\tpublic readonly knownItemCount: KnockoutObservable;\n\t\tpublic readonly status: KnockoutObservable;\n\n\t\t/**\n\t\t * The number of times settings have been changed in this changeset\n\t\t * during the current customizer session.\n\t\t *\n\t\t * Note that this is not the same as the number settings in the changeset:\n\t\t * if the same setting is changed X times, this counter will increase by X,\n\t\t * but the changeset will still only have one entry for that setting.\n\t\t */\n\t\tpublic readonly currentSessionChanges: KnockoutObservable = ko.observable(0);\n\n\t\t/**\n\t\t * Once a changeset has been published or deleted, its contents can't be modified any more.\n\t\t * @private\n\t\t */\n\t\tprivate readonly fixedContentStatuses: Record =\n\t\t\t{'publish': true, 'trash': true, 'future': true};\n\n\t\tpublic readonly wasPublished: KnockoutObservable = ko.observable(false);\n\n\t\tconstructor(name: string = '', knownItemCount: number = 0, initialStatus: string | null = '') {\n\t\t\tthis.name = ko.observable(name);\n\t\t\tthis.knownItemCount = ko.observable(knownItemCount);\n\t\t\tthis.status = ko.observable(initialStatus ?? '');\n\t\t}\n\n\t\tpublic hasName(): boolean {\n\t\t\tconst name = this.name();\n\t\t\treturn (name !== '');\n\t\t}\n\n\t\tpublic canBeModified(): boolean {\n\t\t\treturn !this.fixedContentStatuses.hasOwnProperty(this.status());\n\t\t}\n\n\t\tpublic isNonEmpty(): boolean {\n\t\t\treturn (this.currentSessionChanges() > 0) || (this.knownItemCount() > 0)\n\t\t}\n\t}\n\n\t//region Admin theme\n\tconst UrlOrEmpty = z.union([\n\t\tz.string().url().max(1000),\n\t\tz.literal('')\n\t]);\n\n\tconst AdminThemeMetadata = z.object({\n\t\tpluginName: z.string().max(100),\n\t\tshortDescription: z.string().max(500),\n\n\t\tpluginSlug: z.string().max(64).toLowerCase().default('')\n\t\t\t.refine(\n\t\t\t\tfunction (input: string) {\n\t\t\t\t\t//Only allow alphanumeric characters, underscores, and dashes.\n\t\t\t\t\t//Empty string is allowed.\n\t\t\t\t\treturn /^[a-z0-9_-]*$/.test(input);\n\t\t\t\t},\n\t\t\t\t{message: 'The slug can only contain letters (a-z), numbers, underscores, and dashes.'}\n\t\t\t),\n\t\tidentifierPrefix: z.string().max(20).optional(),\n\n\t\tpluginVersion: z.string().default('1.0').optional(),\n\t\tpluginUrl: UrlOrEmpty.optional(),\n\t\tauthorName: z.string().max(100).optional(),\n\t\tauthorUrl: UrlOrEmpty.optional(),\n\t\trequiredWpVersion: z.string().max(30).default('4.7').optional(),\n\t\ttestedWpVersion: z.string().max(30).optional(),\n\t});\n\n\ttype AdminThemeMetadata = z.infer;\n\n\tconst AdminThemeSettings = z.record(\n\t\t//Key type\n\t\tz.string().min(1),\n\t\t//Value type\n\t\tz.any()\n\t);\n\n\tclass AdminThemeImportReport {\n\t\tpublic totalSettings: number = 0;\n\t\tpublic importedSettings: number = 0;\n\t\tpublic invalidSettings: number = 0;\n\t\tpublic skippedSettings: number = 0;\n\t\tpublic differentImportedSettings: number = 0;\n\n\t\tpublic readonly pluginName: string;\n\n\t\tconstructor(\n\t\t\tpublic readonly fileName: string,\n\t\t\tpublic readonly metadata: AdminThemeMetadata\n\t\t) {\n\t\t\tthis.pluginName = metadata.pluginName || '(Unnamed)';\n\t\t}\n\t}\n\n\tinterface WithZodValidationResults extends ObservableValidationFields {\n\t\tameZodValidationError: KnockoutObservable;\n\t}\n\n\ttype ZodValidatedObservable = KnockoutComputed & WithZodValidationResults;\n\n\tfunction observableWithZodValidation>(\n\t\tvalue: z.output,\n\t\tschema: S\n\t): ZodValidatedObservable> {\n\t\tconst underlyingObservable = ko.observable(value);\n\n\t\tconst observable: ZodValidatedObservable = ko.pureComputed({\n\t\t\tread: underlyingObservable,\n\t\t\twrite: (newValue: T) => {\n\t\t\t\tconst validationResult = schema.safeParse(newValue);\n\t\t\t\tif (validationResult.success) {\n\t\t\t\t\tunderlyingObservable(validationResult.data);\n\t\t\t\t\tobservable.ameZodValidationError(null);\n\t\t\t\t\tobservable.ameValidationErrors([]);\n\t\t\t\t} else {\n\t\t\t\t\tobservable.ameZodValidationError(validationResult.error);\n\t\t\t\t\t//Convert Zod issues to ObservableValidationErrors.\n\t\t\t\t\tobservable.ameValidationErrors(validationResult.error.issues.map(issue => {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcode: issue.code,\n\t\t\t\t\t\t\tmessage: issue.message\n\t\t\t\t\t\t} satisfies ObservableValidationError;\n\t\t\t\t\t}));\n\t\t\t\t}\n\t\t\t}\n\t\t}) as ZodValidatedObservable;\n\n\t\tobservable.ameZodValidationError = ko.observable(null);\n\t\tobservable.ameValidationErrors = ko.observable([] as ObservableValidationError[]);\n\t\tobservable.ameIsValid = ko.pureComputed(() => {\n\t\t\tconst errors = observable.ameValidationErrors();\n\t\t\treturn !errors || errors.length === 0;\n\t\t});\n\n\t\treturn observable;\n\t}\n\n\tclass ObservableThemeMetadata {\n\t\tpublic readonly pluginName: ZodValidatedObservable;\n\t\tpublic readonly shortDescription: ZodValidatedObservable;\n\t\tpublic readonly pluginSlug: ZodValidatedObservable;\n\t\tpublic readonly identifierPrefix: ZodValidatedObservable;\n\t\tpublic readonly pluginVersion: ZodValidatedObservable;\n\t\tpublic readonly pluginUrl: ZodValidatedObservable;\n\t\tpublic readonly authorName: ZodValidatedObservable;\n\t\tpublic readonly authorUrl: ZodValidatedObservable;\n\t\tpublic readonly requiredWpVersion: ZodValidatedObservable;\n\t\tpublic readonly testedWpVersion: ZodValidatedObservable;\n\n\t\tconstructor(metadata: AdminThemeMetadata) {\n\t\t\tthis.pluginName = observableWithZodValidation(\n\t\t\t\tmetadata.pluginName,\n\t\t\t\tAdminThemeMetadata.shape.pluginName\n\t\t\t);\n\t\t\tthis.shortDescription = observableWithZodValidation(\n\t\t\t\tmetadata.shortDescription,\n\t\t\t\tAdminThemeMetadata.shape.shortDescription\n\t\t\t);\n\n\t\t\tthis.pluginSlug = observableWithZodValidation(\n\t\t\t\tmetadata.pluginSlug ?? '',\n\t\t\t\tAdminThemeMetadata.shape.pluginSlug\n\t\t\t);\n\t\t\tthis.identifierPrefix = observableWithZodValidation(\n\t\t\t\tmetadata.identifierPrefix ?? '',\n\t\t\t\tAdminThemeMetadata.shape.identifierPrefix\n\t\t\t);\n\n\t\t\tthis.pluginVersion = observableWithZodValidation(\n\t\t\t\tmetadata.pluginVersion ?? '',\n\t\t\t\tAdminThemeMetadata.shape.pluginVersion\n\t\t\t);\n\t\t\tthis.pluginUrl = observableWithZodValidation(\n\t\t\t\tmetadata.pluginUrl ?? '',\n\t\t\t\tAdminThemeMetadata.shape.pluginUrl\n\t\t\t);\n\t\t\tthis.authorName = observableWithZodValidation(\n\t\t\t\tmetadata.authorName ?? '',\n\t\t\t\tAdminThemeMetadata.shape.authorName\n\t\t\t);\n\t\t\tthis.authorUrl = observableWithZodValidation(\n\t\t\t\tmetadata.authorUrl ?? '',\n\t\t\t\tAdminThemeMetadata.shape.authorUrl\n\t\t\t);\n\t\t\tthis.requiredWpVersion = observableWithZodValidation(\n\t\t\t\tmetadata.requiredWpVersion ?? '',\n\t\t\t\tAdminThemeMetadata.shape.requiredWpVersion\n\t\t\t);\n\t\t\tthis.testedWpVersion = observableWithZodValidation(\n\t\t\t\tmetadata.testedWpVersion ?? '',\n\t\t\t\tAdminThemeMetadata.shape.testedWpVersion\n\t\t\t);\n\t\t}\n\n\t\tpublic toObject(): AdminThemeMetadata {\n\t\t\treturn {\n\t\t\t\tpluginName: this.pluginName(),\n\t\t\t\tshortDescription: this.shortDescription(),\n\t\t\t\tpluginSlug: this.pluginSlug(),\n\t\t\t\tidentifierPrefix: this.identifierPrefix(),\n\t\t\t\tpluginVersion: this.pluginVersion(),\n\t\t\t\tpluginUrl: this.pluginUrl(),\n\t\t\t\tauthorName: this.authorName(),\n\t\t\t\tauthorUrl: this.authorUrl(),\n\t\t\t\trequiredWpVersion: this.requiredWpVersion(),\n\t\t\t\ttestedWpVersion: this.testedWpVersion(),\n\t\t\t};\n\t\t}\n\n\t\tisValid(): boolean {\n\t\t\t//This seems really inelegant, but I can't think of a better way to do it.\n\t\t\treturn this.pluginName.ameIsValid()\n\t\t\t\t&& this.shortDescription.ameIsValid()\n\t\t\t\t&& this.pluginSlug.ameIsValid()\n\t\t\t\t&& this.identifierPrefix.ameIsValid()\n\t\t\t\t&& this.pluginVersion.ameIsValid()\n\t\t\t\t&& this.pluginUrl.ameIsValid()\n\t\t\t\t&& this.authorName.ameIsValid()\n\t\t\t\t&& this.authorUrl.ameIsValid()\n\t\t\t\t&& this.requiredWpVersion.ameIsValid()\n\t\t\t\t&& this.testedWpVersion.ameIsValid();\n\t\t}\n\t}\n\n\tclass DownloadThemeDialog extends AmeBaseKnockoutDialog {\n\t\tpublic readonly meta: KnockoutObservable;\n\t\tpublic readonly areFieldsEditable: KnockoutComputed;\n\t\tpublic readonly isOperationInProgress: KnockoutObservable = ko.observable(false);\n\n\t\tautoCancelButton: boolean = true;\n\t\tisConfirmButtonEnabled: KnockoutObservable;\n\n\t\tadvancedOptionsVisible: KnockoutObservable = ko.observable(false);\n\t\tadvancedOptionsToggleLabel: KnockoutComputed;\n\n\t\thelpVisible: KnockoutObservable = ko.observable(false);\n\t\thelpToggleLabel: KnockoutComputed;\n\n\t\tchangesetName: KnockoutObservable = ko.observable('');\n\t\tmetadataJson: KnockoutObservable = ko.observable('');\n\t\tdownloadCookieName: KnockoutObservable = ko.observable('');\n\n\t\tprivate cleanupCurrentDownload: () => void = () => {\n\t\t};\n\n\t\tconstructor(\n\t\t\tprivate readonly getChangesetName: () => string,\n\t\t\tprivate readonly savePendingChangesetData: () => JQueryPromise,\n\t\t) {\n\t\t\tsuper();\n\t\t\tthis.options.minWidth = 400;\n\n\t\t\tthis.meta = ko.observable(new ObservableThemeMetadata(AdminThemeMetadata.parse({\n\t\t\t\tpluginName: 'Custom Admin Theme',\n\t\t\t\tshortDescription: 'A custom admin theme generated using the Admin Menu Editor Pro plugin.',\n\t\t\t\tpluginVersion: '1.0',\n\t\t\t})));\n\n\t\t\tthis.isConfirmButtonEnabled = ko.computed(() => {\n\t\t\t\tif (this.isOperationInProgress()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (getChangesetName() === '') {\n\t\t\t\t\t//To generate an admin theme, the changeset must have already been saved.\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn this.meta().isValid();\n\t\t\t});\n\n\t\t\tthis.areFieldsEditable = ko.computed(() => {\n\t\t\t\treturn !this.isOperationInProgress();\n\t\t\t});\n\n\t\t\tthis.advancedOptionsToggleLabel = ko.pureComputed((): string => {\n\t\t\t\treturn this.advancedOptionsVisible() ? 'Fewer options' : 'More options';\n\t\t\t});\n\t\t\tthis.helpToggleLabel = ko.pureComputed((): string => {\n\t\t\t\treturn this.helpVisible() ? 'Hide info' : 'How it works';\n\t\t\t});\n\t\t}\n\n\t\tprotected getConfirmButtonLabel(): string | null {\n\t\t\treturn 'Download Admin Theme';\n\t\t}\n\n\t\ttoggleAdvancedOptions(): void {\n\t\t\tthis.advancedOptionsVisible(!this.advancedOptionsVisible());\n\t\t}\n\n\t\ttoggleHelp(): void {\n\t\t\tthis.helpVisible(!this.helpVisible());\n\t\t}\n\n\t\tonConfirm(event: JQueryEventObject) {\n\t\t\t//Sanity checks.\n\t\t\tconst changesetName = this.getChangesetName();\n\t\t\tif (changesetName === '') {\n\t\t\t\talert('Error: The changeset has not been saved yet (name is empty).');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!this.meta().isValid()) {\n\t\t\t\t//This should never happen because the confirm button is disabled\n\t\t\t\t//when the metadata is invalid.\n\t\t\t\talert('Error: The admin theme details are not valid.');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst metadata = this.meta().toObject();\n\n\t\t\tthis.isOperationInProgress(true);\n\n\t\t\tconst $form = $('#ame-ac-theme-download-request-form');\n\t\t\tconst $frame = $('#ame-ac-theme-download-frame');\n\n\t\t\t//Cancel the operation and re-enable buttons if the request takes too long.\n\t\t\tlet isCancelledOrDone: boolean = false;\n\t\t\tconst requestTimeoutMs = 30000;\n\t\t\tconst requestStartTime = (new Date()).getTime();\n\t\t\tlet statusCheckInterval: ReturnType | null = null;\n\n\t\t\tconst cleanup = this.cleanupCurrentDownload = () => {\n\t\t\t\tisCancelledOrDone = true;\n\n\t\t\t\t$frame.off('load.ameAcDownloadAdminTheme');\n\t\t\t\tif (timeoutTimer) {\n\t\t\t\t\tclearTimeout(timeoutTimer);\n\t\t\t\t}\n\t\t\t\tif (statusCheckInterval) {\n\t\t\t\t\tclearInterval(statusCheckInterval);\n\t\t\t\t}\n\t\t\t\t$frame.attr('src', 'about:blank');\n\n\t\t\t\tthis.isOperationInProgress(false);\n\n\t\t\t\tif (this.cleanupCurrentDownload === cleanup) {\n\t\t\t\t\tthis.cleanupCurrentDownload = () => {\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst timeoutTimer = setTimeout(() => {\n\t\t\t\tcleanup();\n\t\t\t\talert('Error: The download operation timed out.');\n\t\t\t}, requestTimeoutMs);\n\n\t\t\tthis.savePendingChangesetData().then(\n\t\t\t\t() => {\n\t\t\t\t\tif (isCancelledOrDone) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.changesetName(changesetName);\n\t\t\t\t\tthis.metadataJson(JSON.stringify(metadata));\n\n\t\t\t\t\t//The server will set a cookie with a unique name that can be used\n\t\t\t\t\t//to check if the download has been initiated. Note that the user\n\t\t\t\t\t//can still cancel the download.\n\t\t\t\t\tconst cookieName = ('ameAcFileDownload_'\n\t\t\t\t\t\t+ new Date().getTime()\n\t\t\t\t\t\t+ '_'\n\t\t\t\t\t\t+ Math.round(Math.random() * 10000) //No dots allowed these cookie names.\n\t\t\t\t\t);\n\t\t\t\t\tthis.downloadCookieName(cookieName);\n\n\t\t\t\t\t//Clear the frame to prevent the old response from being read.\n\t\t\t\t\t$frame.attr('src', 'about:blank');\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$frame.contents().find('body').html('');\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t//Ignore but log cross-origin errors. These should not happen in practice.\n\t\t\t\t\t\tif (console && console.error) {\n\t\t\t\t\t\t\tconsole.error(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tstatusCheckInterval = setInterval(() => {\n\t\t\t\t\t\tconst cookieValue = $.cookie(cookieName);\n\t\t\t\t\t\tif (cookieValue) {\n\t\t\t\t\t\t\tcleanup();\n\t\t\t\t\t\t\t$.removeCookie(cookieName);\n\n\t\t\t\t\t\t\t//Close the dialog when the download starts.\n\t\t\t\t\t\t\tthis.isOpen(false);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((new Date()).getTime() - requestStartTime > requestTimeoutMs) {\n\t\t\t\t\t\t\tcleanup();\n\t\t\t\t\t\t}\n\t\t\t\t\t}, 1000);\n\n\t\t\t\t\t$frame.on('load.ameAcDownloadAdminTheme', () => {\n\t\t\t\t\t\t//Get the response from the frame. It should be JSON displayed as text.\n\t\t\t\t\t\tconst responseText = String($frame.contents().text()).trim();\n\t\t\t\t\t\tconst response = JSON.parse(responseText);\n\n\t\t\t\t\t\tcleanup();\n\n\t\t\t\t\t\tif ((response === null) || (typeof response !== 'object')) {\n\t\t\t\t\t\t\talert('Error: Received an invalid response from the server.');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (!response.success) {\n\t\t\t\t\t\t\t\tlet errorMessage;\n\t\t\t\t\t\t\t\tif (response.data.message) {\n\t\t\t\t\t\t\t\t\terrorMessage = response.data.message;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\terrorMessage = 'An unknown error occurred on the server.';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\talert(errorMessage);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t//This should never happen in practice.\n\t\t\t\t\t\t\t\talert('Error: The server did not start the download correctly.');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\t$form.trigger('submit');\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tif (isCancelledOrDone) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tcleanup();\n\t\t\t\t\talert('Error: Could not save the changeset data before generating an admin theme.')\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\n\t\tonClose(event: JQueryEventObject, ui: any) {\n\t\t\tthis.cleanupCurrentDownload();\n\t\t}\n\t}\n\n\t//endregion\n\n\tclass SectionNavigation {\n\t\tprivate sectionNavStack: KnockoutObservableArray = ko.observableArray([] as string[]);\n\t\tprivate $sectionList: JQuery;\n\n\t\tpublic readonly breadcrumbs: KnockoutObservable;\n\n\t\tconstructor() {\n\t\t\tthis.$sectionList = $('#ame-ac-container-collection');\n\n\t\t\tthis.$sectionList.on('click', '.ame-ac-section-link', (event) => {\n\t\t\t\tevent.preventDefault()\n\n\t\t\t\tif (event.currentTarget === null) {\n\t\t\t\t\treturn; //Shouldn't happen in practice, but let's satisfy the type checker.\n\t\t\t\t}\n\n\t\t\t\tconst targetId = $(event.currentTarget).data('target-id');\n\t\t\t\tif (targetId) {\n\t\t\t\t\tthis.navigateToSection(targetId);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tthis.$sectionList.on('click', '.ame-ac-section-back-button', (event) => {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.navigateBack();\n\t\t\t});\n\n\t\t\tthis.breadcrumbs = ko.pureComputed(() => {\n\t\t\t\treturn this.sectionNavStack()\n\t\t\t\t\t.map((sectionId) => $('#' + sectionId))\n\t\t\t\t\t.filter(($section) => $section.length > 0)\n\t\t\t\t\t.map(($section) => {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: $section.find('.ame-ac-section-title .ame-ac-section-own-title')\n\t\t\t\t\t\t\t\t.first().text()\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tnavigateToSection(sectionElementId: string) {\n\t\t\tconst $section = $('#' + sectionElementId);\n\t\t\tif ($section.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ($section.hasClass('ame-ac-current-section')) {\n\t\t\t\treturn; //Already on this section.\n\t\t\t}\n\n\t\t\t//If the requested section is in the navigation stack, navigate back\n\t\t\t//to it instead of putting more sections on the stack.\n\t\t\tconst stackIndex = this.sectionNavStack.indexOf(sectionElementId);\n\t\t\tif (stackIndex !== -1) {\n\t\t\t\twhile (this.sectionNavStack().length > stackIndex) {\n\t\t\t\t\tthis.navigateBack();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst $previousSection = this.$sectionList.find('.ame-ac-current-section');\n\t\t\tif ($previousSection.length > 0) {\n\t\t\t\tthis.expectTransition($previousSection, '.ame-ac-section');\n\t\t\t\t$previousSection\n\t\t\t\t\t.removeClass('ame-ac-current-section')\n\t\t\t\t\t.addClass('ame-ac-previous-section');\n\t\t\t\tthis.sectionNavStack.push($previousSection.attr('id'));\n\n\t\t\t\t$previousSection.trigger('adminMenuEditor:leaveSection');\n\t\t\t}\n\n\t\t\tthis.expectTransition($section, '.ame-ac-section');\n\t\t\t$section.addClass('ame-ac-current-section');\n\n\t\t\t$section.trigger('adminMenuEditor:enterSection');\n\t\t}\n\n\t\tnavigateBack() {\n\t\t\tif (this.sectionNavStack().length < 1) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst $newCurrentSection = $('#' + this.sectionNavStack.pop());\n\t\t\tif ($newCurrentSection.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst $oldCurrentSection = this.$sectionList.find('.ame-ac-current-section');\n\t\t\tthis.expectTransition($oldCurrentSection, '.ame-ac-section');\n\t\t\t$oldCurrentSection.removeClass('ame-ac-current-section ame-ac-previous-section');\n\t\t\t$oldCurrentSection.trigger('adminMenuEditor:leaveSection');\n\n\t\t\tconst $oldPreviousSection = this.$sectionList.find('.ame-ac-previous-section');\n\t\t\t$oldPreviousSection.removeClass('ame-ac-previous-section');\n\n\t\t\t//Show the new current section.\n\t\t\tthis.expectTransition($newCurrentSection, '.ame-ac-section');\n\t\t\t$newCurrentSection.addClass('ame-ac-current-section');\n\t\t\t$newCurrentSection.trigger('adminMenuEditor:enterSection');\n\n\t\t\t//The next section in the stack becomes the previous section.\n\t\t\tif (this.sectionNavStack().length > 0) {\n\t\t\t\tthis.$sectionList.find('#' + this.sectionNavStack()[this.sectionNavStack().length - 1])\n\t\t\t\t\t.addClass('ame-ac-previous-section');\n\t\t\t}\n\t\t}\n\n\t\t//Add a special class to sections when they have an active CSS transition.\n\t\t//This is used to keep both sections visible while the previous section\n\t\t//slides out and the next section slides in.\n\t\texpectTransition($element: JQuery, requiredSelector: string) {\n\t\t\tif (prefersReducedMotion) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ($element.data('ameHasTransitionEvents')) {\n\t\t\t\treturn; //Event handler(s) already added.\n\t\t\t}\n\n\t\t\tconst transitionEvents = 'transitionend transitioncancel';\n\n\t\t\t$element.addClass('ame-ac-transitioning');\n\n\t\t\tfunction transitionEndCallback(event: JQueryEventObject) {\n\t\t\t\t//Ignore events that bubble from child elements.\n\t\t\t\tif (!$(event.target).is(requiredSelector)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t$element\n\t\t\t\t\t.off(transitionEvents, transitionEndCallback)\n\t\t\t\t\t.data('ameHasTransitionEvents', null)\n\t\t\t\t\t.removeClass('ame-ac-transitioning');\n\t\t\t}\n\n\t\t\t$element.data('ameHasTransitionEvents', true);\n\t\t\t$element.on(transitionEvents, transitionEndCallback);\n\t\t}\n\t}\n\n\texport interface NavigationBreadcrumb {\n\t\ttitle: string;\n\t}\n\n\texport class AdminCustomizer extends AmeAdminCustomizerBase.AdminCustomizerBase implements CustomizableVmInterface {\n\t\tprivate readonly exitPromptMessage = 'Unsaved changes will be lost if you navigate away from this page.';\n\t\t//Admin themes generated by this plugin should be fairly small.\n\t\tprivate readonly maxImportFileSize = 500 * 1024;\n\n\t\tsectionNavigation: SectionNavigation;\n\t\tsettings: CustomizerSettingsCollection;\n\t\tpublic readonly interfaceStructure: InterfaceStructure;\n\n\t\tprivate readonly $previewFrame: JQuery;\n\n\t\t/**\n\t\t * Preview frame URL.\n\t\t */\n\t\tprivate currentPreviewUrl: string | null = null;\n\t\t/**\n\t\t * The default preview URL that can be used when the current frame URL cannot be detected.\n\t\t */\n\t\tprivate readonly initialPreviewUrl: string;\n\t\tprivate previewConnection: ReturnType | null = null;\n\t\tprivate readonly refreshPreviewNonce: string;\n\n\t\tprivate readonly $saveButton: JQuery;\n\n\t\tpublic readonly downloadThemeDialog: DownloadThemeDialog;\n\t\tprivate $extraActionMenu: JQuery | null = null;\n\t\tprivate $extraActionButton: JQuery | null = null;\n\n\t\tprivate $importFileInput: JQuery | null = null;\n\t\tprivate isImporting: KnockoutObservable = ko.observable(false);\n\t\tprivate lastImportReport: KnockoutObservable = ko.observable(null);\n\t\tprivate isImportReportVisible: KnockoutObservable = ko.observable(true);\n\n\t\tprivate isDiscardingChanges: KnockoutObservable = ko.observable(false);\n\n\t\tpublic readonly isGeneralOverlayVisible: KnockoutObservable;\n\n\t\tprivate readonly importActionEnabled: KnockoutComputed;\n\t\tprivate readonly discardChangesActionEnabled: KnockoutComputed;\n\t\tprivate readonly downloadThemeActionEnabled: KnockoutComputed;\n\n\t\tconstructor(scriptData: ScriptData) {\n\t\t\tsuper(scriptData);\n\n\t\t\tthis.settings = new CustomizerSettingsCollection(\n\t\t\t\tscriptData.ajaxUrl,\n\t\t\t\tscriptData.saveChangesetNonce,\n\t\t\t\tscriptData.trashChangesetNonce,\n\t\t\t\tscriptData.changesetName,\n\t\t\t\tscriptData.changesetItemCount,\n\t\t\t\tscriptData.changesetStatus\n\t\t\t);\n\t\t\t_.forOwn(scriptData.settings, (data, id) => {\n\t\t\t\tif (typeof id === 'string') {\n\t\t\t\t\tthis.settings.add(unserializeSetting(id, data));\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tlet sectionIdCounter = 0;\n\n\t\t\tthis.interfaceStructure = unserializeUiElement(\n\t\t\t\tscriptData.interfaceStructure,\n\t\t\t\tthis.settings.get.bind(this.settings),\n\t\t\t\t(data: AnySpecificElementData) => {\n\t\t\t\t\tswitch (data.t) {\n\t\t\t\t\t\tcase 'section':\n\t\t\t\t\t\t\tdata.component = 'ame-ac-section';\n\t\t\t\t\t\t\t//All sections must have unique IDs for navigation to work.\n\t\t\t\t\t\t\tif (!data.id) {\n\t\t\t\t\t\t\t\tdata.id = 'autoID-' + (++sectionIdCounter);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'control-group':\n\t\t\t\t\t\t\tdata.component = 'ame-ac-control-group';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'control':\n\t\t\t\t\t\t\t//Tell controls that use number inputs to position the popup\n\t\t\t\t\t\t\t//slider within the customizer sidebar.\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t(data.component === 'ame-number-input')\n\t\t\t\t\t\t\t\t|| (data.component === 'ame-box-dimensions')\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tdata.params = data.params || {};\n\t\t\t\t\t\t\t\tdata.params.popupSliderWithin = '#ame-ac-sidebar-content';\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t//Replace regular separators with AC-specific ones.\n\t\t\t\t\t\t\tif (data.component === 'ame-horizontal-separator') {\n\t\t\t\t\t\t\t\tdata.component = 'ame-ac-separator';\n\t\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\n\t\t\t//Add the changeset name to the URL (if not already present).\n\t\t\tconst currentUrl = new URL(window.location.href);\n\t\t\tif (currentUrl.searchParams.get('ame-ac-changeset') !== this.settings.changesetName()) {\n\t\t\t\tcurrentUrl.searchParams.set('ame-ac-changeset', this.settings.changesetName());\n\t\t\t\twindow.history.replaceState({}, '', currentUrl.href);\n\t\t\t}\n\n\t\t\t//When the changeset name changes, also change the URL. Discourage navigating\n\t\t\t//to the old URL (no pushState()) because the name is only expected to change\n\t\t\t//when the old changeset becomes invalid (e.g. it's deleted or published).\n\t\t\tthis.settings.changesetName.subscribe((changesetName) => {\n\t\t\t\tconst url = new URL(window.location.href);\n\t\t\t\turl.searchParams.set('ame-ac-changeset', changesetName);\n\t\t\t\twindow.history.replaceState({}, '', url.href);\n\t\t\t});\n\n\t\t\tthis.$saveButton = $('#ame-ac-apply-changes');\n\n\t\t\t//The save button should be enabled when:\n\t\t\t// - There are non-zero changes in the current changeset.\n\t\t\t// - All settings are valid.\n\t\t\t// - The changeset is not in the process of being published, deleted, etc.\n\t\t\t// - The contents of the changeset can be modified (e.g. not already published).\n\t\t\tconst isSaveButtonEnabled = ko.pureComputed(() => {\n\t\t\t\tconst changeset = this.settings.getCurrentChangeset();\n\t\t\t\treturn (\n\t\t\t\t\tchangeset.isNonEmpty()\n\t\t\t\t\t&& changeset.canBeModified()\n\t\t\t\t\t&& !this.settings.isExclusiveOperationInProgress()\n\t\t\t\t\t&& !this.settings.hasValidationErrors()\n\t\t\t\t);\n\t\t\t});\n\t\t\t//Update button state when the customizer loads.\n\t\t\tthis.$saveButton.prop('disabled', !isSaveButtonEnabled());\n\t\t\t//And also on changes.\n\t\t\tisSaveButtonEnabled.subscribe((isEnabled) => {\n\t\t\t\tthis.$saveButton.prop('disabled', !isEnabled);\n\t\t\t\t//Change the text back to the default when the button is enabled.\n\t\t\t\tif (isEnabled) {\n\t\t\t\t\tthis.$saveButton.val(this.$saveButton.data('default-text') ?? 'Save Changes');\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t//Handle the \"Save Changes\" button.\n\t\t\tthis.$saveButton.on('click', () => {\n\t\t\t\t//Show the spinner.\n\t\t\t\tconst $spinner = $('#ame-ac-primary-actions .spinner');\n\t\t\t\t$spinner.css('visibility', 'visible').show();\n\n\t\t\t\tconst publishFailNoticeId = 'ame-ac-publish-failed-notice';\n\t\t\t\t//Remove the previous error notification, if any.\n\t\t\t\t$('#' + publishFailNoticeId).remove();\n\n\t\t\t\tconst promise = this.settings.publishChangeset();\n\n\t\t\t\tpromise.fail((error) => {\n\t\t\t\t\t//Show a dismissible error notification.\n\t\t\t\t\tlet message = 'An unexpected error occurred while saving changes.';\n\t\t\t\t\tif (typeof error === 'string') {\n\t\t\t\t\t\tmessage = error;\n\t\t\t\t\t} else if (error instanceof Error) {\n\t\t\t\t\t\tmessage = error.message;\n\t\t\t\t\t} else if (typeof error.responseJSON === 'object') {\n\t\t\t\t\t\tmessage = _.get(error.responseJSON, ['data', 'message'], message);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst $notice = $(''; + ?> + + + +'; \ No newline at end of file diff --git a/extras/modules/admin-customizer/admin-customizer.css b/extras/modules/admin-customizer/admin-customizer.css new file mode 100644 index 0000000..d5c435a --- /dev/null +++ b/extras/modules/admin-customizer/admin-customizer.css @@ -0,0 +1,677 @@ +.ame-input-group { + display: flex; + flex-wrap: wrap; +} +.ame-input-group .ame-input-group-secondary, .ame-input-group > :not(:first-child) { + margin-left: -1px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.ame-input-group > :not(:last-child) { + margin-right: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +#ame-ac-admin-customizer { + display: flex; + flex-direction: row; + align-items: stretch; + box-sizing: border-box; + width: 100%; + height: 100%; + max-height: 100%; +} + +#ame-ac-sidebar { + flex-basis: 18%; + min-width: 320px; + max-width: 600px; + flex-grow: 1; + display: flex; + flex-direction: column; + background: #f4f4f5; + border-right: 1px solid #dcdcde; +} + +#ame-ac-container-collection { + position: relative; + width: 100%; +} + +#ame-ac-preview-container { + flex-basis: 82%; + flex-grow: 1; + background: #ddd; + position: relative; +} + +#ame-ac-primary-actions { + min-height: 30px; + border-bottom: 1px solid #dcdcde; + display: flex; + flex-direction: row; + align-items: center; + padding-right: 15px; +} + +#ame-ac-sidebar-content { + flex-grow: 1; + overflow-y: auto; + overflow-x: hidden; + position: relative; +} + +#ame-ac-sidebar-blocker-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.4); + display: none; +} + +#ame-ac-exit-admin-customizer { + box-sizing: border-box; + display: block; + height: 45px; + width: 48px; + margin-right: auto; + border-right: 1px solid #dcdcde; + border-top: 4px solid transparent; + color: #3c434a; + text-decoration: none; + position: relative; + text-align: center; +} +#ame-ac-exit-admin-customizer:before { + font: normal 22px/45px dashicons; + content: "\f335"; + position: relative; + top: -3px; +} +#ame-ac-exit-admin-customizer:hover { + background: #fff; + color: #2271b1; + border-top-color: #2271b1; + cursor: pointer; +} + +#ame-ac-primary-actions .spinner { + margin-top: 0; + float: none; +} + +#ame-ac-save-button-wrapper #ame-ac-apply-changes { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +#ame-ac-save-button-wrapper #ame-ac-extra-actions-trigger { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + min-width: 30px; + padding-left: 0; + padding-right: 0; +} + +#ame-ac-sidebar-info .notice { + margin: 0; + padding-top: 9px; + padding-left: 12px; + padding-bottom: 9px; +} +#ame-ac-sidebar-info #ame-ac-global-notification-area { + border-bottom: 1px solid #dcdcde; + overflow-x: hidden; +} +#ame-ac-sidebar-info #ame-ac-global-notification-area:empty { + display: none; +} + +.ame-ac-control-label { + display: block; + box-sizing: border-box; + font-size: 14px; + line-height: 1.75; + font-weight: 600; + margin-bottom: 4px; +} + +.ame-ac-control { + display: block; + box-sizing: border-box; + width: 100%; + margin-bottom: 12px; + padding-left: 12px; + padding-right: 12px; +} +.ame-ac-control .description { + color: #50575e; + display: block; + font-style: italic; + line-height: 1.5; + margin-top: 0; + margin-bottom: 5px; +} +.ame-ac-control p.description { + margin-top: 0.5em; +} + +.ame-ac-control-group .ame-ac-control { + padding-left: 0; + padding-right: 0; +} + +.ame-text-input-control { + width: 100%; +} + +.ame-ac-separator { + margin: 15px 0 0 0; + border: none; +} + +#ame-ac-sidebar-content .ame-ac-validation-errors > ul, #ame-ac-sidebar-content ame-ac-validation-errors > ul { + margin: 0; + padding: 0; +} +#ame-ac-sidebar-content .ame-ac-validation-errors li, #ame-ac-sidebar-content ame-ac-validation-errors li { + list-style: none; +} +#ame-ac-sidebar-content .ame-ac-validation-error { + margin: 0 0 6px 0; + padding: 9px 12px; +} + +.ame-ac-control .ame-ac-has-error { + outline: 2px solid #d63638; +} + +.ame-ac-section { + list-style: none; + box-sizing: border-box; + margin: 0; + width: 100%; +} + +.ame-ac-section-link { + display: block; + box-sizing: border-box; + width: 100%; + margin: 0; + cursor: pointer; +} +.ame-ac-section-link .ame-ac-section-title { + display: block; + position: relative; + padding: 10px 10px 11px 14px; + margin: 0; + color: #50575e; + background-color: white; + border-bottom: 1px solid #dcdcde; + border-left: 4px solid #fff; + line-height: 1.55; + font-size: 14px; + transition: 0.14s color ease-in-out, 0.14s background-color ease-in-out, 0.14s border-color ease-in-out; +} +.ame-ac-section-link .ame-ac-section-title:after { + font: normal 20px/1 dashicons; + display: block; + content: "\f345"; + color: #a7aaad; + position: absolute; + right: 10px; + top: calc(50% - 10px); +} +.ame-ac-section-link .ame-ac-section-title:hover { + color: #2271b1; + background: #f6f7f7; + border-left-color: #2271b1; +} +.ame-ac-section-link .ame-ac-section-title:hover:after { + color: #2271b1; +} + +.ame-ac-section .ame-ac-section-meta + .ame-ac-section-link, +li:not(.ame-ac-section-link) + .ame-ac-section-link { + border-top: 1px solid #dcdcde; +} + +.ame-ac-section-meta { + display: block; + box-sizing: border-box; + width: 100%; + background: white; +} + +.ame-ac-section-header { + display: flex; + border-bottom: 1px solid #dcdcde; + margin-bottom: 15px; + color: #50575e; +} +.ame-ac-section-header .ame-ac-section-title { + flex-grow: 1; + padding-left: 14px; + padding-right: 10px; + font-size: 20px; + font-weight: 200; + line-height: 27px; + margin-top: 1.15em; + margin-bottom: 1.2em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.ame-ac-section-header .ame-ac-section-breadcrumbs { + display: block; + font-size: 13px; + line-height: 26px; + font-weight: 400; + color: #50575e; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.ame-ac-section-header .ame-ac-section-title.ame-ac-has-breadcrumbs { + margin-top: 0.45em; + margin-bottom: 0.55em; +} + +.ame-ac-section-back-button { + display: block; + width: 48px; + flex-shrink: 0; + margin: 0; + padding: 0 3px 0 0; + background: #fff; + color: #50575e; + border: none; + border-right: 1px solid #dcdcde; + border-left: 4px solid #fff; + box-shadow: none; + cursor: pointer; + text-align: center; +} +.ame-ac-section-back-button:before { + display: block; + font: normal 20px/1 dashicons; + content: "\f341"; + margin-right: 1px; +} +.ame-ac-section-back-button:hover { + color: #2271b1; + border-left-color: #2271b1; + background: #f6f7f7; +} + +#ame-ac-section-structure-root .ame-ac-section-back-button { + display: none; +} + +.ame-ac-section { + transform: translateX(100%); + visibility: hidden; + height: 0; + overflow: hidden; + position: absolute; + top: 0; + left: 0; + transition-property: transform, visibility; + transition-duration: 0.182s; + transition-timing-function: cubic-bezier(0.65, 0.05, 0.36, 1); +} +.ame-ac-section.ame-ac-transitioning { + visibility: visible; + height: auto; + overflow: auto; +} + +.ame-ac-current-section { + transform: none; + visibility: visible; + height: auto; + overflow: auto; +} + +.ame-ac-previous-section { + transform: translateX(-100%); +} + +@media (prefers-reduced-motion: reduce) { + .ame-ac-section { + transition: none; + } +} +.ame-ac-content-section { + background: #fff; + border-top: 1px solid #dcdcde; + border-bottom: 1px solid #dcdcde; + padding-top: 4px; + padding-bottom: 5px; +} + +.ame-ac-content-section-title { + margin: 0; + font-size: 15px; +} + +.ame-ac-control + .ame-ac-content-section { + margin-top: 18px; +} + +.ame-ac-section-link + .ame-ac-content-section { + margin-top: 0.5em; +} + +.ame-ac-content-section-2 { + border-top-style: none; + background-color: transparent; + margin-left: 8px; + padding-left: 4px; + width: calc(100% - 8px); + padding-top: 0; + padding-bottom: 0; +} +#ame-ac-preview { + display: block; + box-sizing: border-box; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + border: none; +} + +#ame-ac-preview-refresh-indicator { + display: block; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + z-index: 2; + background: rgba(255, 255, 255, 0.5); + visibility: hidden; + opacity: 0; + transition: opacity 250ms ease-in, visibility 0ms ease-in 250ms; +} +#ame-ac-preview-refresh-indicator.ame-ac-show-indicator { + visibility: visible; + opacity: 1; + transition-delay: 0ms; +} +#ame-ac-preview-refresh-indicator #ame-ac-refresh-spinner { + display: none; + /*box-sizing: border-box; + width: 10em; + height: 10em; + border-radius: 50%; + + $backColor: rgba(100, 100, 100, 0.4); + border: 1.1em solid $backColor; + border-left: 1.1em solid #ffffff; + + animation: ame-ac-basic-spin 1.1s infinite linear;*/ +} + +@keyframes ame-ac-basic-spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} +.ui-menu { + background-color: #ffffff; + border-radius: 5px; + border: 1px solid #444a4a; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + z-index: 9999; +} +.ui-menu .ui-menu-item { + color: #32373c; + padding: 8px 16px; + margin: 0; + cursor: pointer; + font-size: 14px; + line-height: 1.4; + white-space: nowrap; +} +.ui-menu .ui-menu-item:hover, .ui-menu .ui-menu-item.ui-state-focus { + background-color: #0073aa; + color: #ffffff; + outline: none; +} +.ui-menu .ui-menu-item .dashicons { + transition: none; +} +.ui-menu .ui-menu-divider { + border-top: 1px solid #e5e5e5; + margin: 5px 0; +} +.ui-menu .ui-menu-item.ui-state-disabled { + color: #8c8f94; + cursor: default; +} +.ui-menu .ui-menu-item.ui-state-disabled:hover, .ui-menu .ui-menu-item.ui-state-disabled.ui-state-focus { + background-color: transparent; +} +.ui-menu .ui-menu-icon { + float: right; + margin-top: 2px; +} +.ui-menu .ui-menu .ui-menu { + position: absolute; + top: 0; + left: 100%; + border: none; + margin-top: -2px; + margin-left: -1px; +} +.ui-menu .ui-menu-item-delete, .ui-menu .ame-ac-menu-item-delete { + color: #dc3232; +} +.ui-menu .ui-menu-item-delete:hover, .ui-menu .ui-menu-item-delete.ui-state-focus, .ui-menu .ame-ac-menu-item-delete:hover, .ui-menu .ame-ac-menu-item-delete.ui-state-focus { + background-color: #e77373; + color: #ffffff; +} +.ui-menu .ui-menu-item-delete.ui-state-disabled:hover, .ui-menu .ui-menu-item-delete.ui-state-disabled.ui-state-focus, .ui-menu .ame-ac-menu-item-delete.ui-state-disabled:hover, .ui-menu .ame-ac-menu-item-delete.ui-state-disabled.ui-state-focus { + color: #8c8f94; + background-color: transparent; +} + +#ame-ac-extra-actions-menu { + position: absolute; +} + +.ui-dialog { + background-color: white; + border: 1px solid #c0c0c0; + border-radius: 3px; + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.25); +} +.ui-dialog .ui-dialog-titlebar { + background-color: #fcfcfc; + border-radius: 3px 3px 0 0; + border-bottom: 1px solid #dfdfdf; + padding: 0 36px 0 8px; +} +.ui-dialog .ui-dialog-title { + font-size: 18px; + font-weight: 600; + line-height: 2; + margin: 0; +} +.ui-dialog .ui-dialog-titlebar-close { + background-color: transparent; + border: none; + color: #666; + cursor: pointer; + padding: 0; + margin: 0; + position: absolute; + top: 0; + right: 0; + width: 36px; + height: 36px; + text-align: center; +} +.ui-dialog .ui-dialog-titlebar-close:hover { + color: #000; +} +.ui-dialog .ui-dialog-titlebar-close:before { + font: normal 20px/36px "dashicons"; + content: "\f158"; + vertical-align: middle; +} +.ui-dialog .ui-dialog-content { + font-size: 14px; + line-height: 1.4285714286; + padding: 8px 8px; +} +.ui-dialog .ui-dialog-buttonpane { + background-color: #fcfcfc; + border-top: 1px solid #dcdcde; + padding: 8px; + text-align: right; +} +.ui-dialog .ui-dialog-buttonpane .button-primary { + float: left; +} +.ui-widget-overlay { + background-color: rgba(0, 0, 0, 0.7); + position: fixed; + left: 0; + top: 0; + right: 0; + bottom: 0; + width: 100%; + height: 100%; +} + +.ui-front { + z-index: 10000; +} + +.ame-ac-dialog-label { + display: block; + font-size: 14px; + font-weight: 600; + margin-bottom: 3px; +} + +.ame-ac-dialog-row { + margin-bottom: 0.7142857143em; +} +.ame-ac-dialog-row input, .ame-ac-dialog-row select, .ame-ac-dialog-row textarea { + width: 100%; +} + +.ame-ac-more-toggle { + text-decoration: none; + margin-bottom: 8px; + line-height: 20px; +} +.ame-ac-more-toggle:before { + font: normal 20px/1 "dashicons"; + content: "\f140"; + vertical-align: top; + margin-left: -3px; +} +.ame-ac-more-toggle.ame-ac-more-toggle-active:before { + content: "\f142"; +} + +.ame-ac-dialog-help { + margin: -8px -8px 8px; + padding: 3px 8px; + background-color: #f5f5f5; + border-bottom: 1px solid #dcdcdc; +} +.ame-ac-dialog-help p:first-of-type { + margin-top: 0; +} +.ame-ac-dialog-help ul { + margin-top: 0.5em; +} +.ame-ac-dialog-help ul li { + list-style: disc inside; + margin-left: 0.5em; +} +.ame-ac-dialog-help .ame-ac-more-toggle.ame-ac-more-toggle-inactive:before { + content: "\f348"; +} + +.ame-ac-dialog select:invalid, .ame-ac-dialog select.ame-has-validation-errors, .ame-ac-dialog input:invalid, .ame-ac-dialog input.ame-has-validation-errors { + border-color: #d63638; +} +.ame-ac-dialog select:invalid:focus, .ame-ac-dialog select.ame-has-validation-errors:focus, .ame-ac-dialog input:invalid:focus, .ame-ac-dialog input.ame-has-validation-errors:focus { + box-shadow: 0 0 0 1px #d63638; +} + +#ame-ac-download-theme-dialog { + position: relative; +} + +#ame-ac-download-progress-overlay { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + z-index: 2; + cursor: wait; +} + +#ame-ac-latest-import-report table th { + text-align: left; +} +#ame-ac-latest-import-report table td { + text-align: right; + padding-left: 1em; +} + +.ame-ac-visually-hidden { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; +} + +.ame-ac-general-progress-spinner { + width: 50px; + height: 50px; + background-color: rgba(255, 255, 255, 0.8); + border: 7px solid #f3f3f3; + border-top: 7px solid #3498db; + border-radius: 50%; + animation: ac-progress-spin 2s linear infinite; +} + +@keyframes ac-progress-spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} +.ame-ac-spinner-container { + width: 100%; + height: 100%; + /* Flexbox properties for centering the spinner */ + display: flex; + justify-content: center; + align-items: center; +} + +/*# sourceMappingURL=admin-customizer.css.map */ diff --git a/extras/modules/admin-customizer/admin-customizer.css.map b/extras/modules/admin-customizer/admin-customizer.css.map new file mode 100644 index 0000000..d1eac22 --- /dev/null +++ b/extras/modules/admin-customizer/admin-customizer.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["../../../css/_input-group.scss","admin-customizer.scss","../../../css/_forms.scss"],"names":[],"mappings":"AAAA;EACC;EACA;;AAEA;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;;ACMF;EACC;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA,cA/BW;;;AAkCZ;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA,eAhDW;EAkDX;EACA;EACA;EACA;;;AAGD;EAEC;EAGA;EACA;EAGA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;;AAMD;EACC;EAMA;EACA,QALa;EAOb;EAEA;EAEA;EACA;EACA;EACA;EAEA;EACA;;AAEA;EACC;EACA;EACA;EACA;;AAGD;EACC;EACA,OA1BY;EA2BZ,kBA3BY;EA4BZ;;;AAKD;EACC;EACA;;;AAKD;EACC;EACA;;AAGD;EACC;EACA;EAEA;EACA;EACA;;;AAQD;EACC;EACA;EACA,cA/ImB;EAgJnB;;AAID;EACC,eA1JU;EA2JV;;AAEA;EACC;;;AAQH;EACC;EACA;EAEA,WAtKe;EAuKf;EACA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA,cAjLoB;EAkLpB,eAjLqB;;AAmLrB;EACC,OA5La;EA8Lb;EACA;EACA;EAEA;EACA;;AAGD;EACC;;;AAIF;EACC;EACA;;;AAGD;EACC;;;AAGD;EAEC;EACA;;;AASC;EACC;EACA;;AAGD;EACC;;AAIF;EACC;EACA;;;AAKD;EACC;;;AAOF;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;;AAGA;EACC;EACA;EACA;EACA;EAEA,OA/Qa;EAgRb;EACA,eA/QU;EAgRV;EAEA;EACA,WA/Qc;EAiRd;;AAGA;EACC;EACA;EACA;EACA;EAEA;EACA;EACA;;AAGD;EACC,OAxSa;EAySb,YAxSuB;EAySvB,mBA1Sa;;AA4Sb;EACC,OA7SY;;;AAqThB;AAAA;EAEC,YAnTW;;;AAsTZ;EACC;EACA;EACA;EAEA;;;AAGD;EAIC;EACA,eAnUW;EAoUX;EAEA,OAxUc;;AA0Ud;EACC;EACA;EACA;EAEA;EACA;EACA;EAGA;EACA;EAEA;EACA;EACA;;AAGD;EACC;EAEA;EACA;EACA;EACA;EAEA;EACA;EACA;;AAKD;EACC;EACA;;;AAIF;EACC;EACA,OA/WiB;EAgXjB;EAEA;EACA;EAEA;EACA,OA1Xc;EA4Xd;EACA,cA3XW;EA4XX;EACA;EAEA;EACA;;AAEA;EACC;EACA;EACA;EACA;;AAGD;EACC,OA9Yc;EA+Yd,mBA/Yc;EAgZd,YA/YwB;;;AAoZ1B;EACC;;;AAID;EAEC;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACC;EACA;EACA;;;AAIF;EAEC;EACA;EACA;EACA;;;AAGD;EACC;;;AAID;EACC;IACC;;;AAQF;EACC;EACA;EACA;EAEA;EACA,gBAT6B;;;AAY9B;EACC;EACA;;;AAID;EACC;;;AAID;EACC;;;AAID;EACC;EACA;EAGA,aADe;EAEf;EACA;EAEA;EACA;;AAYD;EACC;EACA;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;EAGA;EACA;EAGA;;AAEA;EACC;EACA;EAIA;;AAGD;EACC;AACA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;;;AAaF;EACC;IACC;;EAED;IACC;;;AAQF;EAWC,kBAVU;EAWV;EACA;EACA;EACA;;AAEA;EACC,OAZW;EAaX;EACA;EAEA;EACA;EACA;EACA;;AAEA;EAEC,kBA1BU;EA2BV,OAvBM;EAwBN;;AAKD;EACC;;AAIF;EACC;EACA;;AAGD;EACC,OA1Ca;EA2Cb;;AAEA;EAEC;;AAIF;EACC;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;;AAID;EACC,OA/DQ;;AAiER;EAEC;EACA,OAtEM;;AA0EN;EAEC,OA9EW;EA+EX;;;AAMJ;EAEC;;;AAUD;EAKC;EACA;EACA,eANe;EAOf;;AAEA;EACC;EACA;EACA;EAEA;;AAGD;EACC;EACA;EACA;EACA;;AAGD;EACC;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA,OAnCiB;EAoCjB,QApCiB;EAqCjB;;AAEA;EAEC;;AAGD;EACC;EACA;EACA;;AAIF;EACC,WAnDc;EAoDd;EACA;;AAGD;EACC;EACA;EACA;EACA;;AAGA;EACC;;AASH;EACC;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;;AAEA;EACC;;;AAIF;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;EAIA;;AAGD;EACC;;;AAIF;EACC;EACA;EAGA;EACA;;AAEA;EACC;;AAGD;EACC;;AAEA;EACC;EACA;;AAIF;EACC;;;AC1yBA;EACC,cALY;;AAQZ;EACC;;;ADgzBJ;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAOC;EACC;;AAED;EACC;EACA;;;AAQH;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EAGC;EACA;EACA;EACA;EACA;EACA;EAEA;;;AAGD;EACC;IAAK;;EACL;IAAO;;;AAGR;EACC;EACA;AAEA;EACA;EACA;EACA","file":"admin-customizer.css"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/admin-customizer.js b/extras/modules/admin-customizer/admin-customizer.js new file mode 100644 index 0000000..e6a1555 --- /dev/null +++ b/extras/modules/admin-customizer/admin-customizer.js @@ -0,0 +1,1512 @@ +'use strict'; +/// +/// +/// +import { AmeCustomizable } from '../../pro-customizables/assets/customizable.js'; +import { registerBaseComponents } from '../../pro-customizables/ko-components/ame-components.js'; +import AmeAcStructure from './ko-components/ame-ac-structure.js'; +import AmeAcSection from './ko-components/ame-ac-section.js'; +import AmeAcSectionLink from './ko-components/ame-ac-section-link.js'; +import AmeAcControl from './ko-components/ame-ac-control.js'; +import AmeAcControlGroup from './ko-components/ame-ac-control-group.js'; +import AmeAcContentSection from './ko-components/ame-ac-content-section.js'; +import { AmeAdminCustomizerBase } from './admin-customizer-base.js'; +import AmeAcSeparator from './ko-components/ame-ac-separator.js'; +import AmeAcValidationErrors from './ko-components/ame-ac-validation-errors.js'; +import z, { ZodError } from '../../zod/lib/index.js'; +export var AmeAdminCustomizer; +(function (AmeAdminCustomizer) { + var SettingCollection = AmeCustomizable.SettingCollection; + var unserializeUiElement = AmeCustomizable.unserializeUiElement; + var unserializeSetting = AmeCustomizable.unserializeSetting; + const $ = jQuery; + const _ = wsAmeLodash; + registerBaseComponents(); + ko.components.register('ame-ac-structure', AmeAcStructure); + ko.components.register('ame-ac-section', AmeAcSection); + ko.components.register('ame-ac-section-link', AmeAcSectionLink); + ko.components.register('ame-ac-content-section', AmeAcContentSection); + ko.components.register('ame-ac-control-group', AmeAcControlGroup); + ko.components.register('ame-ac-control', AmeAcControl); + ko.components.register('ame-ac-separator', AmeAcSeparator); + ko.components.register('ame-ac-validation-errors', AmeAcValidationErrors); + const reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); + let prefersReducedMotion = reducedMotionQuery && reducedMotionQuery.matches; + reducedMotionQuery.addEventListener('change', () => { + prefersReducedMotion = reducedMotionQuery.matches; + }); + class CustomizerSettingsCollection extends SettingCollection { + constructor(ajaxUrl, saveChangesetNonce, trashChangesetNonce, changesetName, changesetItemCount = 0, changesetStatus = null) { + super(); + this.ajaxUrl = ajaxUrl; + this.saveChangesetNonce = saveChangesetNonce; + this.trashChangesetNonce = trashChangesetNonce; + /** + * Settings that have changed since the last save attempt. + */ + this.pendingSettings = {}; + /** + * Settings that in the process of being sent to the server to be saved. + * They might not be saved yet. + */ + this.sentSettings = {}; + this.currentChangesetRequest = null; + this.saveTriggerTimeoutId = null; + this.exclusiveOperation = ko.observable(false); + const self = this; + this.currentChangeset = ko.observable(new Changeset(changesetName, changesetItemCount, changesetStatus)); + this.changesetName = ko.pureComputed(() => { + var _a; + return ((_a = self.currentChangeset()) === null || _a === void 0 ? void 0 : _a.name()) || ''; + }); + //Automatically save the changeset when any settings change. + const totalChangeCount = ko.pureComputed(() => { + const changeset = self.currentChangeset(); + return (changeset ? changeset.currentSessionChanges() : 0); + }); + totalChangeCount.subscribe(_.debounce((counter) => { + if (counter > 0) { + self.queueChangesetUpdate(); + } + }, 3000, { leading: true, trailing: true })); + this.isExclusiveOperationInProgress = ko.pureComputed(() => { + return self.exclusiveOperation(); + }); + //Keep track of unsaved changes and changesets. + this.addChangeListener((setting) => { + this.pendingSettings[setting.id] = setting; + let changeset = this.currentChangeset(); + //If the current changeset cannot be modified, create a new one + //for the changed setting(s). + if (!(changeset === null || changeset === void 0 ? void 0 : changeset.canBeModified())) { + changeset = new Changeset(); + this.currentChangeset(changeset); + } + //Track the number of changes in the current session. + changeset.currentSessionChanges(changeset.currentSessionChanges() + 1); + }); + } + queueChangesetUpdate(delay = 0) { + if (delay > 0) { + if (this.saveTriggerTimeoutId !== null) { + //Replace the existing timeout with a new one. + clearTimeout(this.saveTriggerTimeoutId); + } + this.saveTriggerTimeoutId = setTimeout(() => { + this.saveTriggerTimeoutId = null; + this.queueChangesetUpdate(0); + }, delay); + return; + } + if (this.saveTriggerTimeoutId !== null) { + return; //Another timeout is already waiting. + } + if (this.currentChangesetRequest !== null) { + //There's an in-progress request, so wait until it's done. + this.currentChangesetRequest.always(() => { + //Wait a bit to avoid hammering the server. + this.queueChangesetUpdate(1000); + }); + return; + } + this.saveChangeset(); + } + saveChangeset(status = null) { + var _a; + //Do nothing if there are no changes. + if (_.isEmpty(this.pendingSettings) && (status === null)) { + return $.Deferred().reject(new Error('There are no changes to save.')).promise(); + } + if (this.isExclusiveOperationInProgress()) { + return $.Deferred().reject(new Error('Another exclusive changeset operation is in progress.')).promise(); + } + let isExclusiveRequest = (status === 'publish') || (status === 'trash'); + if (isExclusiveRequest) { + this.exclusiveOperation(true); + } + const savedChangeset = this.currentChangeset(); + //Keep a local copy of the settings in case something changes instance + //properties while the request is in progress (should never happen). + const settingsToSend = this.pendingSettings; + this.sentSettings = settingsToSend; + this.pendingSettings = {}; + const modifiedSettings = _.mapValues(settingsToSend, setting => setting.value()); + const requestData = { + action: 'ws_ame_ac_save_changeset', + _ajax_nonce: this.saveChangesetNonce, + changeset: (_a = savedChangeset === null || savedChangeset === void 0 ? void 0 : savedChangeset.name) !== null && _a !== void 0 ? _a : '', + modified: JSON.stringify(modifiedSettings), + }; + if (status !== null) { + requestData['status'] = status; + } + //If the changeset doesn't have a name, it is new. + if (!(savedChangeset === null || savedChangeset === void 0 ? void 0 : savedChangeset.hasName())) { + requestData['createNew'] = 1; + } + const request = $.ajax({ + url: this.ajaxUrl, + method: 'POST', + data: requestData, + dataType: 'json', + timeout: 20000, + }); + this.currentChangesetRequest = request; + const self = this; + function storeValidationResultsFrom(serverResponse) { + const results = _.get(serverResponse, ['data', 'validationResults']); + if (typeof results !== 'object') { + return; + } + for (const settingId in results) { + const setting = self.get(settingId); + if (!setting.isDefined()) { + continue; + } + if (!modifiedSettings.hasOwnProperty(settingId)) { + continue; + } + const sentValue = modifiedSettings[settingId]; + const state = results[settingId]; + if (state.isValid) { + setting.get().clearValidationErrorsForValue(sentValue); + } + else { + //Since the server response is not fully validated, some typeof checks + //are still useful. + // noinspection SuspiciousTypeOfGuard + setting.get().addValidationErrorsForValue(sentValue, _.filter(state.errors, error => (typeof error.message === 'string'))); + } + } + } + function storeChangesetDetailsFrom(serverResponse) { + if (!savedChangeset) { + return; + } + //Store the returned changeset name in case a new changeset was created. + if (!savedChangeset.hasName()) { + const newName = _.get(serverResponse, ['data', 'changeset']); + if (typeof newName === 'string') { + savedChangeset.name(newName); + } + } + //Store the changeset status. + const newStatus = _.get(serverResponse, ['data', 'changesetStatus']); + if (typeof newStatus === 'string') { + savedChangeset.status(newStatus); + } + //Store the number of changes in the changeset. + const newChangeCount = _.get(serverResponse, ['data', 'changesetItemCount']); + if (typeof newChangeCount === 'number') { + savedChangeset.knownItemCount(newChangeCount); + } + //Was the changeset published? Because changesets are typically moved + //to trash after publishing, "status" might be "trash" instead of "publish", + //but we still want to know if it was successfully published. + const wasPublished = _.get(serverResponse, ['data', 'changesetWasPublished'], null); + if (wasPublished) { + savedChangeset.wasPublished(wasPublished); + } + } + request.done(function (response) { + storeChangesetDetailsFrom(response); + storeValidationResultsFrom(response); + //After successfully publishing a changeset, it has no more + //unsaved changes. + const isPublished = (savedChangeset.status() === 'publish') + || (savedChangeset.status() === 'future') + || (savedChangeset.wasPublished()); + if (isPublished) { + savedChangeset.currentSessionChanges(0); + } + //After a changeset is published or trashed, it can no longer + //be edited. We may be able to replace it with a new changeset + //that was created on the server. + if (!self.currentChangeset().canBeModified()) { + const nextChangeset = _.get(response, ['data', 'nextChangeset']); + if ((typeof nextChangeset === 'string') && (nextChangeset !== '')) { + self.currentChangeset(new Changeset(nextChangeset)); + } + } + }); + request.fail((requestObject) => { + if (typeof requestObject.responseJSON === 'object') { + storeValidationResultsFrom(requestObject.responseJSON); + storeChangesetDetailsFrom(requestObject.responseJSON); + } + //Add the unsaved settings back to the pending list. + for (const id in settingsToSend) { + //Keep only settings that still exist. + if (this.get(id).isDefined()) { + this.pendingSettings[id] = settingsToSend[id]; + } + } + //We don't automatically retry because the problem might be something + //that doesn't get better on its own, like missing permissions. + }); + request.always(() => { + this.currentChangesetRequest = null; + this.sentSettings = {}; + if (isExclusiveRequest) { + this.exclusiveOperation(false); + } + }); + return request; + } + savePendingSettings(timeout = 20) { + if (this.isExclusiveOperationInProgress()) { + //Wait for the exclusive operation to finish. + const deferred = $.Deferred(); + const result = deferred.then(() => this.doSavePendingSettings()); + const startTime = Date.now(); + const timer = setInterval(() => { + if (!this.isExclusiveOperationInProgress()) { + clearInterval(timer); + deferred.resolve(); + } + else if ((Date.now() - startTime) > timeout) { + clearInterval(timer); + deferred.reject(new Error('Exclusive operation timed out.')); + } + }, 200); + return result; + } + return this.doSavePendingSettings(); + } + doSavePendingSettings() { + //If there are no changes, we don't need to do anything. + if (_.isEmpty(this.pendingSettings)) { + return $.Deferred().resolve().promise(); + } + return this.saveChangeset(); + } + getCurrentChangeset() { + return this.currentChangeset(); + } + /** + * Get any unsaved setting changes. + * + * @returns An object mapping setting IDs to their modified values. + */ + get unsavedChanges() { + //Include both pending settings and sent settings. Sent settings + //might not be saved yet. + let unsavedSettings = {}; + _.defaults(unsavedSettings, this.pendingSettings, this.sentSettings); + return _.mapValues(unsavedSettings, setting => setting.value()); + } + publishChangeset() { + if (this.isExclusiveOperationInProgress()) { + return $.Deferred() + .reject(new Error('Another exclusive changeset operation is already in progress.')) + .promise(); + } + return this.saveChangeset('publish'); + } + trashChangeset() { + if (this.isExclusiveOperationInProgress()) { + return $.Deferred() + .reject(new Error('Another exclusive changeset operation is already in progress.')) + .promise(); + } + const changeset = this.currentChangeset(); + if (!changeset.hasName()) { + //The changeset hasn't been saved yet, so we can just mark it as trashed. + changeset.status('trash'); + changeset.currentSessionChanges(0); + //It's a success of sorts. + return $.Deferred().resolve(true).promise(); + } + this.exclusiveOperation(true); + const requestData = { + action: 'ws_ame_ac_trash_changeset', + _ajax_nonce: this.trashChangesetNonce, + changeset: changeset.name + }; + const request = $.ajax({ + url: this.ajaxUrl, + method: 'POST', + data: requestData, + dataType: 'json', + timeout: 20000, + }); + this.currentChangesetRequest = request; + request.done(function () { + changeset.status('trash'); + changeset.currentSessionChanges(0); + }); + //Unfortunately, jQuery doesn't seem to allow us to create a custom + //error object and pass it to other handlers, so code that uses this + //method will have to parse the error response itself. + request.always(() => { + this.currentChangesetRequest = null; + this.exclusiveOperation(false); + }); + return request; + } + } + class Changeset { + constructor(name = '', knownItemCount = 0, initialStatus = '') { + /** + * The number of times settings have been changed in this changeset + * during the current customizer session. + * + * Note that this is not the same as the number settings in the changeset: + * if the same setting is changed X times, this counter will increase by X, + * but the changeset will still only have one entry for that setting. + */ + this.currentSessionChanges = ko.observable(0); + /** + * Once a changeset has been published or deleted, its contents can't be modified any more. + * @private + */ + this.fixedContentStatuses = { 'publish': true, 'trash': true, 'future': true }; + this.wasPublished = ko.observable(false); + this.name = ko.observable(name); + this.knownItemCount = ko.observable(knownItemCount); + this.status = ko.observable(initialStatus !== null && initialStatus !== void 0 ? initialStatus : ''); + } + hasName() { + const name = this.name(); + return (name !== ''); + } + canBeModified() { + return !this.fixedContentStatuses.hasOwnProperty(this.status()); + } + isNonEmpty() { + return (this.currentSessionChanges() > 0) || (this.knownItemCount() > 0); + } + } + //region Admin theme + const UrlOrEmpty = z.union([ + z.string().url().max(1000), + z.literal('') + ]); + const AdminThemeMetadata = z.object({ + pluginName: z.string().max(100), + shortDescription: z.string().max(500), + pluginSlug: z.string().max(64).toLowerCase().default('') + .refine(function (input) { + //Only allow alphanumeric characters, underscores, and dashes. + //Empty string is allowed. + return /^[a-z0-9_-]*$/.test(input); + }, { message: 'The slug can only contain letters (a-z), numbers, underscores, and dashes.' }), + identifierPrefix: z.string().max(20).optional(), + pluginVersion: z.string().default('1.0').optional(), + pluginUrl: UrlOrEmpty.optional(), + authorName: z.string().max(100).optional(), + authorUrl: UrlOrEmpty.optional(), + requiredWpVersion: z.string().max(30).default('4.7').optional(), + testedWpVersion: z.string().max(30).optional(), + }); + const AdminThemeSettings = z.record( + //Key type + z.string().min(1), + //Value type + z.any()); + class AdminThemeImportReport { + constructor(fileName, metadata) { + this.fileName = fileName; + this.metadata = metadata; + this.totalSettings = 0; + this.importedSettings = 0; + this.invalidSettings = 0; + this.skippedSettings = 0; + this.differentImportedSettings = 0; + this.pluginName = metadata.pluginName || '(Unnamed)'; + } + } + function observableWithZodValidation(value, schema) { + const underlyingObservable = ko.observable(value); + const observable = ko.pureComputed({ + read: underlyingObservable, + write: (newValue) => { + const validationResult = schema.safeParse(newValue); + if (validationResult.success) { + underlyingObservable(validationResult.data); + observable.ameZodValidationError(null); + observable.ameValidationErrors([]); + } + else { + observable.ameZodValidationError(validationResult.error); + //Convert Zod issues to ObservableValidationErrors. + observable.ameValidationErrors(validationResult.error.issues.map(issue => { + return { + code: issue.code, + message: issue.message + }; + })); + } + } + }); + observable.ameZodValidationError = ko.observable(null); + observable.ameValidationErrors = ko.observable([]); + observable.ameIsValid = ko.pureComputed(() => { + const errors = observable.ameValidationErrors(); + return !errors || errors.length === 0; + }); + return observable; + } + class ObservableThemeMetadata { + constructor(metadata) { + var _a, _b, _c, _d, _e, _f, _g, _h; + this.pluginName = observableWithZodValidation(metadata.pluginName, AdminThemeMetadata.shape.pluginName); + this.shortDescription = observableWithZodValidation(metadata.shortDescription, AdminThemeMetadata.shape.shortDescription); + this.pluginSlug = observableWithZodValidation((_a = metadata.pluginSlug) !== null && _a !== void 0 ? _a : '', AdminThemeMetadata.shape.pluginSlug); + this.identifierPrefix = observableWithZodValidation((_b = metadata.identifierPrefix) !== null && _b !== void 0 ? _b : '', AdminThemeMetadata.shape.identifierPrefix); + this.pluginVersion = observableWithZodValidation((_c = metadata.pluginVersion) !== null && _c !== void 0 ? _c : '', AdminThemeMetadata.shape.pluginVersion); + this.pluginUrl = observableWithZodValidation((_d = metadata.pluginUrl) !== null && _d !== void 0 ? _d : '', AdminThemeMetadata.shape.pluginUrl); + this.authorName = observableWithZodValidation((_e = metadata.authorName) !== null && _e !== void 0 ? _e : '', AdminThemeMetadata.shape.authorName); + this.authorUrl = observableWithZodValidation((_f = metadata.authorUrl) !== null && _f !== void 0 ? _f : '', AdminThemeMetadata.shape.authorUrl); + this.requiredWpVersion = observableWithZodValidation((_g = metadata.requiredWpVersion) !== null && _g !== void 0 ? _g : '', AdminThemeMetadata.shape.requiredWpVersion); + this.testedWpVersion = observableWithZodValidation((_h = metadata.testedWpVersion) !== null && _h !== void 0 ? _h : '', AdminThemeMetadata.shape.testedWpVersion); + } + toObject() { + return { + pluginName: this.pluginName(), + shortDescription: this.shortDescription(), + pluginSlug: this.pluginSlug(), + identifierPrefix: this.identifierPrefix(), + pluginVersion: this.pluginVersion(), + pluginUrl: this.pluginUrl(), + authorName: this.authorName(), + authorUrl: this.authorUrl(), + requiredWpVersion: this.requiredWpVersion(), + testedWpVersion: this.testedWpVersion(), + }; + } + isValid() { + //This seems really inelegant, but I can't think of a better way to do it. + return this.pluginName.ameIsValid() + && this.shortDescription.ameIsValid() + && this.pluginSlug.ameIsValid() + && this.identifierPrefix.ameIsValid() + && this.pluginVersion.ameIsValid() + && this.pluginUrl.ameIsValid() + && this.authorName.ameIsValid() + && this.authorUrl.ameIsValid() + && this.requiredWpVersion.ameIsValid() + && this.testedWpVersion.ameIsValid(); + } + } + class DownloadThemeDialog extends AmeBaseKnockoutDialog { + constructor(getChangesetName, savePendingChangesetData) { + super(); + this.getChangesetName = getChangesetName; + this.savePendingChangesetData = savePendingChangesetData; + this.isOperationInProgress = ko.observable(false); + this.autoCancelButton = true; + this.advancedOptionsVisible = ko.observable(false); + this.helpVisible = ko.observable(false); + this.changesetName = ko.observable(''); + this.metadataJson = ko.observable(''); + this.downloadCookieName = ko.observable(''); + this.cleanupCurrentDownload = () => { + }; + this.options.minWidth = 400; + this.meta = ko.observable(new ObservableThemeMetadata(AdminThemeMetadata.parse({ + pluginName: 'Custom Admin Theme', + shortDescription: 'A custom admin theme generated using the Admin Menu Editor Pro plugin.', + pluginVersion: '1.0', + }))); + this.isConfirmButtonEnabled = ko.computed(() => { + if (this.isOperationInProgress()) { + return false; + } + if (getChangesetName() === '') { + //To generate an admin theme, the changeset must have already been saved. + return false; + } + return this.meta().isValid(); + }); + this.areFieldsEditable = ko.computed(() => { + return !this.isOperationInProgress(); + }); + this.advancedOptionsToggleLabel = ko.pureComputed(() => { + return this.advancedOptionsVisible() ? 'Fewer options' : 'More options'; + }); + this.helpToggleLabel = ko.pureComputed(() => { + return this.helpVisible() ? 'Hide info' : 'How it works'; + }); + } + getConfirmButtonLabel() { + return 'Download Admin Theme'; + } + toggleAdvancedOptions() { + this.advancedOptionsVisible(!this.advancedOptionsVisible()); + } + toggleHelp() { + this.helpVisible(!this.helpVisible()); + } + onConfirm(event) { + //Sanity checks. + const changesetName = this.getChangesetName(); + if (changesetName === '') { + alert('Error: The changeset has not been saved yet (name is empty).'); + return; + } + if (!this.meta().isValid()) { + //This should never happen because the confirm button is disabled + //when the metadata is invalid. + alert('Error: The admin theme details are not valid.'); + return; + } + const metadata = this.meta().toObject(); + this.isOperationInProgress(true); + const $form = $('#ame-ac-theme-download-request-form'); + const $frame = $('#ame-ac-theme-download-frame'); + //Cancel the operation and re-enable buttons if the request takes too long. + let isCancelledOrDone = false; + const requestTimeoutMs = 30000; + const requestStartTime = (new Date()).getTime(); + let statusCheckInterval = null; + const cleanup = this.cleanupCurrentDownload = () => { + isCancelledOrDone = true; + $frame.off('load.ameAcDownloadAdminTheme'); + if (timeoutTimer) { + clearTimeout(timeoutTimer); + } + if (statusCheckInterval) { + clearInterval(statusCheckInterval); + } + $frame.attr('src', 'about:blank'); + this.isOperationInProgress(false); + if (this.cleanupCurrentDownload === cleanup) { + this.cleanupCurrentDownload = () => { + }; + } + }; + const timeoutTimer = setTimeout(() => { + cleanup(); + alert('Error: The download operation timed out.'); + }, requestTimeoutMs); + this.savePendingChangesetData().then(() => { + if (isCancelledOrDone) { + return; + } + this.changesetName(changesetName); + this.metadataJson(JSON.stringify(metadata)); + //The server will set a cookie with a unique name that can be used + //to check if the download has been initiated. Note that the user + //can still cancel the download. + const cookieName = ('ameAcFileDownload_' + + new Date().getTime() + + '_' + + Math.round(Math.random() * 10000) //No dots allowed these cookie names. + ); + this.downloadCookieName(cookieName); + //Clear the frame to prevent the old response from being read. + $frame.attr('src', 'about:blank'); + try { + $frame.contents().find('body').html(''); + } + catch (e) { + //Ignore but log cross-origin errors. These should not happen in practice. + if (console && console.error) { + console.error(e); + } + } + statusCheckInterval = setInterval(() => { + const cookieValue = $.cookie(cookieName); + if (cookieValue) { + cleanup(); + $.removeCookie(cookieName); + //Close the dialog when the download starts. + this.isOpen(false); + return; + } + if ((new Date()).getTime() - requestStartTime > requestTimeoutMs) { + cleanup(); + } + }, 1000); + $frame.on('load.ameAcDownloadAdminTheme', () => { + //Get the response from the frame. It should be JSON displayed as text. + const responseText = String($frame.contents().text()).trim(); + const response = JSON.parse(responseText); + cleanup(); + if ((response === null) || (typeof response !== 'object')) { + alert('Error: Received an invalid response from the server.'); + } + else { + if (!response.success) { + let errorMessage; + if (response.data.message) { + errorMessage = response.data.message; + } + else { + errorMessage = 'An unknown error occurred on the server.'; + } + alert(errorMessage); + } + else { + //This should never happen in practice. + alert('Error: The server did not start the download correctly.'); + } + } + }); + $form.trigger('submit'); + }, () => { + if (isCancelledOrDone) { + return; + } + cleanup(); + alert('Error: Could not save the changeset data before generating an admin theme.'); + }); + } + onClose(event, ui) { + this.cleanupCurrentDownload(); + } + } + //endregion + class SectionNavigation { + constructor() { + this.sectionNavStack = ko.observableArray([]); + this.$sectionList = $('#ame-ac-container-collection'); + this.$sectionList.on('click', '.ame-ac-section-link', (event) => { + event.preventDefault(); + if (event.currentTarget === null) { + return; //Shouldn't happen in practice, but let's satisfy the type checker. + } + const targetId = $(event.currentTarget).data('target-id'); + if (targetId) { + this.navigateToSection(targetId); + } + }); + this.$sectionList.on('click', '.ame-ac-section-back-button', (event) => { + event.preventDefault(); + this.navigateBack(); + }); + this.breadcrumbs = ko.pureComputed(() => { + return this.sectionNavStack() + .map((sectionId) => $('#' + sectionId)) + .filter(($section) => $section.length > 0) + .map(($section) => { + return { + title: $section.find('.ame-ac-section-title .ame-ac-section-own-title') + .first().text() + }; + }); + }); + } + navigateToSection(sectionElementId) { + const $section = $('#' + sectionElementId); + if ($section.length === 0) { + return; + } + if ($section.hasClass('ame-ac-current-section')) { + return; //Already on this section. + } + //If the requested section is in the navigation stack, navigate back + //to it instead of putting more sections on the stack. + const stackIndex = this.sectionNavStack.indexOf(sectionElementId); + if (stackIndex !== -1) { + while (this.sectionNavStack().length > stackIndex) { + this.navigateBack(); + } + return; + } + const $previousSection = this.$sectionList.find('.ame-ac-current-section'); + if ($previousSection.length > 0) { + this.expectTransition($previousSection, '.ame-ac-section'); + $previousSection + .removeClass('ame-ac-current-section') + .addClass('ame-ac-previous-section'); + this.sectionNavStack.push($previousSection.attr('id')); + $previousSection.trigger('adminMenuEditor:leaveSection'); + } + this.expectTransition($section, '.ame-ac-section'); + $section.addClass('ame-ac-current-section'); + $section.trigger('adminMenuEditor:enterSection'); + } + navigateBack() { + if (this.sectionNavStack().length < 1) { + return; + } + const $newCurrentSection = $('#' + this.sectionNavStack.pop()); + if ($newCurrentSection.length === 0) { + return; + } + const $oldCurrentSection = this.$sectionList.find('.ame-ac-current-section'); + this.expectTransition($oldCurrentSection, '.ame-ac-section'); + $oldCurrentSection.removeClass('ame-ac-current-section ame-ac-previous-section'); + $oldCurrentSection.trigger('adminMenuEditor:leaveSection'); + const $oldPreviousSection = this.$sectionList.find('.ame-ac-previous-section'); + $oldPreviousSection.removeClass('ame-ac-previous-section'); + //Show the new current section. + this.expectTransition($newCurrentSection, '.ame-ac-section'); + $newCurrentSection.addClass('ame-ac-current-section'); + $newCurrentSection.trigger('adminMenuEditor:enterSection'); + //The next section in the stack becomes the previous section. + if (this.sectionNavStack().length > 0) { + this.$sectionList.find('#' + this.sectionNavStack()[this.sectionNavStack().length - 1]) + .addClass('ame-ac-previous-section'); + } + } + //Add a special class to sections when they have an active CSS transition. + //This is used to keep both sections visible while the previous section + //slides out and the next section slides in. + expectTransition($element, requiredSelector) { + if (prefersReducedMotion) { + return; + } + if ($element.data('ameHasTransitionEvents')) { + return; //Event handler(s) already added. + } + const transitionEvents = 'transitionend transitioncancel'; + $element.addClass('ame-ac-transitioning'); + function transitionEndCallback(event) { + //Ignore events that bubble from child elements. + if (!$(event.target).is(requiredSelector)) { + return; + } + $element + .off(transitionEvents, transitionEndCallback) + .data('ameHasTransitionEvents', null) + .removeClass('ame-ac-transitioning'); + } + $element.data('ameHasTransitionEvents', true); + $element.on(transitionEvents, transitionEndCallback); + } + } + class AdminCustomizer extends AmeAdminCustomizerBase.AdminCustomizerBase { + constructor(scriptData) { + super(scriptData); + this.exitPromptMessage = 'Unsaved changes will be lost if you navigate away from this page.'; + //Admin themes generated by this plugin should be fairly small. + this.maxImportFileSize = 500 * 1024; + /** + * Preview frame URL. + */ + this.currentPreviewUrl = null; + this.previewConnection = null; + this.$extraActionMenu = null; + this.$extraActionButton = null; + this.$importFileInput = null; + this.isImporting = ko.observable(false); + this.lastImportReport = ko.observable(null); + this.isImportReportVisible = ko.observable(true); + this.isDiscardingChanges = ko.observable(false); + this._isFrameLoading = false; + this.frameLoadingTimeoutId = null; + this.lastPreviewLoadTimestamp = new Date(0); + this.reloadWaitTimeoutId = null; + this.hasPendingPreviewReload = false; + this.settings = new CustomizerSettingsCollection(scriptData.ajaxUrl, scriptData.saveChangesetNonce, scriptData.trashChangesetNonce, scriptData.changesetName, scriptData.changesetItemCount, scriptData.changesetStatus); + _.forOwn(scriptData.settings, (data, id) => { + if (typeof id === 'string') { + this.settings.add(unserializeSetting(id, data)); + } + }); + let sectionIdCounter = 0; + this.interfaceStructure = unserializeUiElement(scriptData.interfaceStructure, this.settings.get.bind(this.settings), (data) => { + switch (data.t) { + case 'section': + data.component = 'ame-ac-section'; + //All sections must have unique IDs for navigation to work. + if (!data.id) { + data.id = 'autoID-' + (++sectionIdCounter); + } + break; + case 'control-group': + data.component = 'ame-ac-control-group'; + break; + case 'control': + //Tell controls that use number inputs to position the popup + //slider within the customizer sidebar. + if ((data.component === 'ame-number-input') + || (data.component === 'ame-box-dimensions')) { + data.params = data.params || {}; + data.params.popupSliderWithin = '#ame-ac-sidebar-content'; + } + //Replace regular separators with AC-specific ones. + if (data.component === 'ame-horizontal-separator') { + data.component = 'ame-ac-separator'; + } + } + }); + //Add the changeset name to the URL (if not already present). + const currentUrl = new URL(window.location.href); + if (currentUrl.searchParams.get('ame-ac-changeset') !== this.settings.changesetName()) { + currentUrl.searchParams.set('ame-ac-changeset', this.settings.changesetName()); + window.history.replaceState({}, '', currentUrl.href); + } + //When the changeset name changes, also change the URL. Discourage navigating + //to the old URL (no pushState()) because the name is only expected to change + //when the old changeset becomes invalid (e.g. it's deleted or published). + this.settings.changesetName.subscribe((changesetName) => { + const url = new URL(window.location.href); + url.searchParams.set('ame-ac-changeset', changesetName); + window.history.replaceState({}, '', url.href); + }); + this.$saveButton = $('#ame-ac-apply-changes'); + //The save button should be enabled when: + // - There are non-zero changes in the current changeset. + // - All settings are valid. + // - The changeset is not in the process of being published, deleted, etc. + // - The contents of the changeset can be modified (e.g. not already published). + const isSaveButtonEnabled = ko.pureComputed(() => { + const changeset = this.settings.getCurrentChangeset(); + return (changeset.isNonEmpty() + && changeset.canBeModified() + && !this.settings.isExclusiveOperationInProgress() + && !this.settings.hasValidationErrors()); + }); + //Update button state when the customizer loads. + this.$saveButton.prop('disabled', !isSaveButtonEnabled()); + //And also on changes. + isSaveButtonEnabled.subscribe((isEnabled) => { + var _a; + this.$saveButton.prop('disabled', !isEnabled); + //Change the text back to the default when the button is enabled. + if (isEnabled) { + this.$saveButton.val((_a = this.$saveButton.data('default-text')) !== null && _a !== void 0 ? _a : 'Save Changes'); + } + }); + //Handle the "Save Changes" button. + this.$saveButton.on('click', () => { + //Show the spinner. + const $spinner = $('#ame-ac-primary-actions .spinner'); + $spinner.css('visibility', 'visible').show(); + const publishFailNoticeId = 'ame-ac-publish-failed-notice'; + //Remove the previous error notification, if any. + $('#' + publishFailNoticeId).remove(); + const promise = this.settings.publishChangeset(); + promise.fail((error) => { + //Show a dismissible error notification. + let message = 'An unexpected error occurred while saving changes.'; + if (typeof error === 'string') { + message = error; + } + else if (error instanceof Error) { + message = error.message; + } + else if (typeof error.responseJSON === 'object') { + message = _.get(error.responseJSON, ['data', 'message'], message); + } + const $notice = $('
    ') + .attr('id', publishFailNoticeId) + .addClass('notice notice-error is-dismissible') + .text(message); + //WordPress won't automatically add the dismiss button to a dynamically + //generated notice like this, so we have to do it. + $notice.append($('') + .append('Dismiss this notice') + .on('click', (event) => { + event.preventDefault(); + $notice.remove(); //Not as fancy as WP does it. + })); + const $container = $('#ame-ac-global-notification-area'); + $container.append($notice); + }); + promise.done(() => { + var _a; + this.$saveButton.val((_a = this.$saveButton.data('published-text')) !== null && _a !== void 0 ? _a : 'Saved'); + //The preview could be stale. For example, the color scheme module + //switches between "actual" and "preview" color schemes dynamically, + //but the "actual" scheme could change after applying new settings. + //Let's reload the preview frame to make sure it's up-to-date. + this.queuePreviewFrameReload(); + }); + promise.always(() => { + $spinner.css('visibility', 'hidden'); + }); + }); + //Prevent the user from interacting with settings while the changeset is being modified. + this.settings.isExclusiveOperationInProgress.subscribe((isInProgress) => { + $('#ame-ac-sidebar-blocker-overlay').toggle(isInProgress); + }); + //Show a general overlay with a progress spinner while something is happening. + this.isGeneralOverlayVisible = ko.pureComputed(() => { + return this.isImporting() || this.isDiscardingChanges(); + }); + //Initialize the "download admin theme" dialog. + this.downloadThemeDialog = new DownloadThemeDialog(() => this.settings.getCurrentChangeset().name(), () => this.settings.savePendingSettings()); + //Toggle available extra actions based on changeset status. + this.importActionEnabled = ko.pureComputed(() => { + const changeset = this.settings.getCurrentChangeset(); + return changeset && changeset.canBeModified() + && !this.settings.isExclusiveOperationInProgress(); + }); + this.importActionEnabled.subscribe((isEnabled) => { + if (this.$extraActionMenu) { + this.$extraActionMenu.find('.ame-ac-import-theme-action') + .toggleClass('ui-state-disabled', !isEnabled); + } + }); + this.discardChangesActionEnabled = ko.pureComputed(() => { + const changeset = this.settings.getCurrentChangeset(); + return changeset && changeset.isNonEmpty() && changeset.canBeModified() + && !this.settings.isExclusiveOperationInProgress(); + }); + this.discardChangesActionEnabled.subscribe((isEnabled) => { + if (this.$extraActionMenu) { + this.$extraActionMenu.find('.ame-ac-discard-changes-action') + .toggleClass('ui-state-disabled', !isEnabled); + } + }); + this.downloadThemeActionEnabled = ko.pureComputed(() => { + return !this.settings.isExclusiveOperationInProgress(); + }); + this.downloadThemeActionEnabled.subscribe((isEnabled) => { + if (this.$extraActionMenu) { + this.$extraActionMenu.find('.ame-ac-download-theme-action') + .toggleClass('ui-state-disabled', !isEnabled); + } + }); + this.sectionNavigation = new SectionNavigation(); + //Set up the preview frame. + this.$previewFrame = $('iframe#ame-ac-preview'); + this.initialPreviewUrl = scriptData.initialPreviewUrl; + this.refreshPreviewNonce = scriptData.refreshPreviewNonce; + this.$previewFrame.on('load', () => { + var _a; + this.isFrameLoading = false; + //The URL that was actually loaded might not match the one that + //was requested (e.g. because there was a redirect). + this.currentPreviewUrl = null; + //Close the previous postMessage connection. + if (this.previewConnection) { + this.previewConnection.disconnect(); + this.previewConnection = null; + } + const frame = this.$previewFrame.get(0); + if (!frame || !(frame instanceof HTMLIFrameElement)) { + return; + } + //Try to get the preview URL from the iframe. + try { + const url = (_a = frame.contentWindow) === null || _a === void 0 ? void 0 : _a.location.href; + if (url) { + this.currentPreviewUrl = url; + } + } + catch (e) { + //We can't get the URL directly, probably because it's a cross-origin iframe. + } + this.previewConnection = AmeAcCommunicator.connectToChild(frame, { + 'setPreviewUrl': (url) => { + if (this.isPreviewableUrl(url)) { + this.previewUrl = url; + } + }, + 'notifyPreviewUrlChanged': (url) => { + this.currentPreviewUrl = url; + } + }, this.allowedCommOrigins, scriptData.isWpDebugEnabled); + this.previewConnection.promise.then((connection) => { + if (typeof connection === 'undefined') { + //This should never happen, but the type checker doesn't know that. + throw new Error('Unexpected error: Connection apparently succeeded, but the connection object is undefined'); + } + connection.execute('getCurrentUrl').then((url) => { + if (url && (typeof url === 'string')) { + this.currentPreviewUrl = url; + } + }); + //Notify other scripts that the preview frame is loaded and + //the postMessage connection is ready for use. + $('body').trigger('adminMenuEditor:acPreviewConnectionReady'); + }); + }); + this.previewUrl = this.initialPreviewUrl; + //Notify other scripts. This lets them register custom controls and so on. + $('#ame-ac-admin-customizer').trigger('adminMenuEditor:acRegister', [this]); + const throttledReloadPreview = _.throttle(() => { + this.queuePreviewFrameReload(); + }, 1000, //The reload method does its own throttling, so we use a low wait time here. + { leading: true, trailing: true }); + //Refresh the preview when any setting changes. + this.settings.addChangeListener((setting, newValue) => { + if (setting.supportsPostMessage + && this.previewConnection + && this.previewConnection.isConnected) { + this.previewConnection.execute('previewSetting', setting.id, newValue); + } + else { + throttledReloadPreview(); + } + }); + const registerUnloadPrompt = () => { + //Ask for confirmation when the user tries to leave the page and the changeset + //has unpublished changes. + $(window).on('beforeunload.ame-ac-exit-confirm', (event) => { + if (this.hasUnpublishedChanges()) { + event.preventDefault(); + //Note: The confirmation prompt will only be displayed if the user + //has interacted with the page (e.g. clicked something). + //As of this writing, MDN says that some browsers still don't support triggering + //an "unsaved changes" prompt with event.preventDefault(). You need to set + //event.returnValue to a string or return a string from the event handler. + //Modern browsers will ignore the content and display their own generic message. + return this.exitPromptMessage; + } + }); + }; + /* + Allegedly, registering a beforeunload handler can cause the browser to + disable some optimizations, so let's only do it when the user changes + something or the changeset already contains some changes. + */ + if (this.settings.getCurrentChangeset().isNonEmpty()) { + registerUnloadPrompt(); + } + else { + const listenerId = this.settings.addChangeListener(() => { + //Remove the listener after it has been triggered once. + this.settings.removeChangeListener(listenerId); + registerUnloadPrompt(); + }); + } + } + getSettingObservable(settingId, defaultValue) { + //Let's just implement this temporarily while working on refactoring this + //stuff to use KO components. + return this.settings + .get(settingId) + .map(setting => setting.value) + .getOrElse(ko.observable(defaultValue)); + } + getAllSettingValues() { + throw new Error('Method not implemented.'); + } + get previewUrl() { + return this.currentPreviewUrl; + } + set previewUrl(url) { + if (url === this.currentPreviewUrl) { + return; + } + //The URL starts out as null, but it cannot be set to NULL again after + //the preview frame has been loaded. + if (url === null) { + throw new Error('Cannot directly set preview URL to null'); + } + if (this.isPreviewableUrl(url)) { + this.navigatePreviewFrame(url); + } + } + navigatePreviewFrame(url = null, forceReload = false) { + const oldUrl = this.previewUrl; + if (url === null) { + url = oldUrl !== null && oldUrl !== void 0 ? oldUrl : this.initialPreviewUrl; + } + const isSameUrl = (oldUrl === url); + if (isSameUrl && !forceReload) { + return; + } + //If there are any unsaved changes, let's include them in the preview by simulating + //a form submission and sending the changes as form data. The server-side component + //will merge these changes with existing changeset data. + const unsavedChanges = this.settings.unsavedChanges; + const simulateFormSubmission = !_.isEmpty(unsavedChanges); + const parsedUrl = new URL(url); + //If we're not using form submission, add a special parameter + //to the URL to force a refresh. + const refreshParam = '_ame-ac-refresh-trigger'; + if (isSameUrl && !simulateFormSubmission) { + parsedUrl.searchParams.set(refreshParam, Date.now() + '_' + Math.random()); + } + else { + //Otherwise, remove the parameter just to be safe. + parsedUrl.searchParams.delete(refreshParam); + } + //Ensure that the changeset used in the preview matches the current + //changeset and preview is enabled. This is just a precaution. Normally, + //the preview script automatically changes link URLs. + parsedUrl.searchParams.set('ame-ac-changeset', this.settings.changesetName()); + parsedUrl.searchParams.set('ame-ac-preview', '1'); + this.hasPendingPreviewReload = false; //Reloading now, so no longer pending. + this.isFrameLoading = true; + //console.info('navigatePreviewFrame: Navigating to ' + parsedUrl.href); + if (simulateFormSubmission) { + const formData = { + action: 'ws_ame_ac_refresh_preview_frame', + "ame-ac-changeset": this.settings.changesetName(), + modified: JSON.stringify(unsavedChanges), + nonce: this.refreshPreviewNonce + }; + const $form = $('
    ') + .attr('method', 'post') + .attr('action', parsedUrl.href) + .attr('target', 'ame-ac-preview-frame') + .appendTo('body'); + let key; + for (key in formData) { + const value = formData[key]; + $('') + .attr('type', 'hidden') + .attr('name', key) + .val(value) + .appendTo($form); + } + this.currentPreviewUrl = parsedUrl.href; + $form.trigger('submit'); + $form.remove(); + } + else { + this.currentPreviewUrl = parsedUrl.href; + this.$previewFrame.attr('src', this.currentPreviewUrl); + } + } + set isFrameLoading(isLoading) { + const wasLoadingBefore = this._isFrameLoading; + if (!isLoading && (isLoading === wasLoadingBefore)) { + return; + } + //In some circumstances, we may start to load a new URL before + //the previous one has finished loading. This is valid and should + //reset the load timeout. + $('#ame-ac-preview-refresh-indicator').toggleClass('ame-ac-show-indicator', isLoading); + if (this.frameLoadingTimeoutId) { + clearTimeout(this.frameLoadingTimeoutId); + this.frameLoadingTimeoutId = null; + } + if (isLoading) { + //As a precaution, we'll assume that if the frame doesn't load in a reasonable + //time, it will never finish loading. + this.frameLoadingTimeoutId = window.setTimeout(() => { + if (this.isFrameLoading) { + this.isFrameLoading = false; + } + }, 20000); + } + this._isFrameLoading = isLoading; + if (wasLoadingBefore && !isLoading) { + this.lastPreviewLoadTimestamp = new Date(); + } + //Once the frame is loaded, trigger any pending reload. + if (!isLoading && this.hasPendingPreviewReload) { + this.hasPendingPreviewReload = false; + this.queuePreviewFrameReload(); + } + } + get isFrameLoading() { + return this._isFrameLoading; + } + queuePreviewFrameReload() { + if (this.reloadWaitTimeoutId) { + return; //The frame will reload soon. + } + if (this.isFrameLoading) { + this.hasPendingPreviewReload = true; + return; + } + //To avoid stressing the server, wait at least X ms after the last + //load completes before reloading the frame. + const reloadWaitTime = 2000; + const now = new Date(); + const timeSinceLastLoad = now.getTime() - this.lastPreviewLoadTimestamp.getTime(); + if (timeSinceLastLoad < reloadWaitTime) { + this.reloadWaitTimeoutId = window.setTimeout(() => { + this.reloadWaitTimeoutId = null; + this.queuePreviewFrameReload(); + }, reloadWaitTime - timeSinceLastLoad); + return; + } + //Actually reload the frame. + this.navigatePreviewFrame(null, true); + } + onBindingsApplied(rootElement) { + //Navigate to the root section. In the current implementation this can't happen + //until bindings have been applied, so it's not part of the constructor. + this.navigateToRootSection(); + //Initialize the action menu. + this.$extraActionButton = jQuery('#ame-ac-extra-actions-trigger', rootElement); + this.$extraActionMenu = jQuery('#ame-ac-extra-actions-menu', rootElement).menu(); + //Update menu states. + this.importActionEnabled.notifySubscribers(this.importActionEnabled()); + this.discardChangesActionEnabled.notifySubscribers(this.discardChangesActionEnabled()); + //Get the file picker. + this.$importFileInput = jQuery('#ame-ac-import-admin-theme-file', rootElement); + } + navigateToRootSection() { + this.sectionNavigation.navigateToSection('ame-ac-section-structure-root'); + } + // noinspection JSUnusedGlobalSymbols -- Used in at least one add-on. + /** + * Execute an RPC method in the preview frame. + * + * @param {string} methodName + * @param {*} args + */ + executeRpcMethod(methodName, ...args) { + if (!this.previewConnection || !this.previewConnection.isConnected) { + return $.Deferred().reject('The preview frame is not connected.').promise(); + } + return this.previewConnection.execute(methodName, ...args); + } + confirmExit() { + if (this.hasUnpublishedChanges()) { + if (window.confirm(this.exitPromptMessage)) { + //Remove the confirmation prompt that appears when leaving the page. + //We don't want to show two prompts. + $(window).off('beforeunload.ame-ac-exit-confirm'); + return true; + } + return false; + } + return true; + } + hasUnpublishedChanges() { + const changeset = this.settings.getCurrentChangeset(); + return (changeset.isNonEmpty() + && !changeset.wasPublished() + && (changeset.status() !== 'trash') //Can't publish a trashed changeset. + ); + } + // noinspection JSUnusedGlobalSymbols -- Used in the Knockout template. + toggleExtraActionMenu() { + if (!this.$extraActionMenu) { + return; + } + this.$extraActionMenu.toggle(); + if (this.$extraActionMenu.is(':visible')) { + //Position the menu below the button. + const $button = $('#ame-ac-extra-actions-trigger'); + this.$extraActionMenu.position({ + my: 'right top', + at: 'right bottom', + of: $button, + collision: 'flipfit' + }); + //Hide the menu when the user clicks outside the menu or the button. + $(document).on('mousedown.ameAcExtraMenuHide', this.handleClickOutsideActionMenu.bind(this)); + } + else { + //Remove the click listener if it's still active. + $(document).off('mousedown.ameAcExtraMenuHide'); + } + } + handleClickOutsideActionMenu(event) { + if (!this.$extraActionMenu + || !this.$extraActionMenu.is(':visible') + || !this.$extraActionButton) { + //The event listener should not be active if the menu is not visible. + $(document).off('mousedown.ameAcExtraMenuHide'); + return; + } + const menuElement = this.$extraActionMenu.get(0); + const buttonElement = this.$extraActionButton.get(0); + const isClickOutsideMenu = !menuElement.contains(event.target); + const isClickOutsideButton = !buttonElement.contains(event.target); + if (isClickOutsideMenu && isClickOutsideButton) { + this.hideExtraActionMenu(); + } + } + hideExtraActionMenu() { + if (!this.$extraActionMenu) { + return; + } + this.$extraActionMenu.hide(); + //Stop listening for clicks outside the menu. + $(document).off('mousedown.ameAcExtraMenuHide'); + } + actionOpenDownloadDialog() { + if (!this.downloadThemeActionEnabled()) { + return; + } + this.downloadThemeDialog.isOpen(true); + this.isImportReportVisible(false); + this.hideExtraActionMenu(); + } + actionOpenImportDialog() { + if (!this.importActionEnabled()) { + //Can't import if there is no changeset or the changeset can't be edited. + //The menu item should be disabled in this case, but we'll check anyway. + return false; + } + this.hideExtraActionMenu(); + //Allow the default action to proceed, which will open the file picker. + return true; + } + actionDiscardChanges() { + if (!this.discardChangesActionEnabled()) { + return; + } + this.hideExtraActionMenu(); + if (this.settings.isExclusiveOperationInProgress()) { + alert('Another operation is in progress. Please wait for it to complete before discarding changes.'); + return; + } + if (!confirm('Are you sure you want to discard your unsaved changes?')) { + return; + } + this.isImportReportVisible(false); + this.isDiscardingChanges(true); + this.settings.trashChangeset() + .then(() => { + //Reload the customizer with a new changeset. + //First, to get the customizer's base URL, get the current URL + //and remove all query parameters except "page". + const url = new URL(window.location.href); + const page = url.searchParams.get('page'); + url.search = ''; + url.searchParams.set('page', page || 'ame-admin-customizer'); + //Don't need the hash either. + url.hash = ''; + //Add a random parameter to force a reload. + url.searchParams.set('_ame-ac-reload', Math.random().toString(36).substring(7)); + //Navigate to the new URL. + window.location.href = url.toString(); + //Note that the isDiscardingChanges flag is not reset here, + //so the progress overlay will stay visible until the page reloads. + }) + .fail((requestObject) => { + let message = requestObject.statusText || 'Unknown error.'; + if (typeof requestObject.responseJSON === 'object') { + const customMessage = _.get(requestObject.responseJSON, ['data', 'message']); + if (typeof customMessage === 'string') { + message = customMessage; + } + } + alert('Error: ' + message); + this.isDiscardingChanges(false); + }); + } + handleImportFileSelection() { + if (!this.$importFileInput) { + return; + } + const fileInput = this.$importFileInput.get(0); + if (!fileInput || !fileInput.files || (fileInput.files.length < 1)) { + return; + } + //Get the first file. Normally, there should only be one. + const selectedFile = fileInput.files.item(0); + if (!selectedFile) { + return; + } + //Limit the file size. + if (selectedFile.size > this.maxImportFileSize) { + alert('Error: The selected file is too large. The maximum file size is ' + + Math.round(this.maxImportFileSize / 1024) + ' KiB'); + //Clear the file input. + this.$importFileInput.val(''); + return; + } + this.isImporting(true); + this.lastImportReport(null); + JSZip.loadAsync(selectedFile).then((zip) => { + const metadataFileRegex = /^([\\/]?[a-zA-Z0-9_-]+[\\/])metadata\.json$/; + const foundMetadataFiles = zip.file(metadataFileRegex); + if (!foundMetadataFiles || (foundMetadataFiles.length < 1)) { + throw new Error('The selected file is not an admin theme generated by this tool.'); + } + const metadataFile = foundMetadataFiles[0]; + //Get the directory name and separator from the metadata file path. + //The prefix will usually be something like "admin-theme-slug/". + const matches = metadataFileRegex.exec(metadataFile.name); + let directoryPrefix; + if (!matches || (matches.length < 2)) { + throw new Error('The directory structure of this ZIP file is not recognized.'); + } + else { + directoryPrefix = matches[1]; + } + const settingsFile = zip.file(directoryPrefix + 'settings.json'); + if (!settingsFile) { + throw new Error('The selected ZIP file is missing a settings.json file.'); + } + //Read both files. + return Promise.all([ + metadataFile.async('string'), + settingsFile.async('string') + ]); + }, (error) => { + const errorMessage = error.message || error; + throw new Error('Error reading "' + selectedFile.name + '": ' + errorMessage); + }).then((fileContents) => { + if (!fileContents) { + throw new Error('Failed to read settings and metadata from the ZIP file.'); + } + const metadata = this.parseImportedAdminThemeFile(fileContents[0], 'metadata.json', AdminThemeMetadata); + const settings = this.parseImportedAdminThemeFile(fileContents[1], 'settings.json', AdminThemeSettings); + const report = new AdminThemeImportReport(selectedFile.name, metadata); + //Import metadata. + this.downloadThemeDialog.meta(new ObservableThemeMetadata(metadata)); + //Import settings. + for (const [settingId, value] of Object.entries(settings)) { + report.totalSettings++; + const foundSetting = this.settings.get(settingId); + foundSetting.forEach((setting) => { + const oldValue = setting.value(); + const errors = setting.tryUpdate(value); + if (errors && errors.length) { + report.invalidSettings++; + } + else { + report.importedSettings++; + if (oldValue != value) { + report.differentImportedSettings++; + } + } + }); + if (foundSetting.isEmpty()) { + report.skippedSettings++; + } + } + this.lastImportReport(report); + this.isImportReportVisible(true); + }).catch((error) => { + //Error handling: Show the error message to the user. + let errorMessage; + if (error instanceof Error) { + errorMessage = error.message; + } + else { + errorMessage = String(error); + } + alert('Error: ' + errorMessage); + }).finally(() => { + var _a; + this.isImporting(false); + (_a = this.$importFileInput) === null || _a === void 0 ? void 0 : _a.val(''); + }); + } + parseImportedAdminThemeFile(content, name, schema) { + try { + const parsedJson = JSON.parse(content); + return schema.parse(parsedJson); + } + catch (error) { + let errorMessage; + if (error instanceof ZodError) { + //Convert issues to a newline-separated string. + errorMessage = error.issues.map((issue) => { + return issue.path.join('.') + ': ' + issue.message; + }).join('\n'); + } + else if (error instanceof Error) { + errorMessage = error.message; + } + else { + errorMessage = String(error); + } + //Add the file name to the error message. + throw new Error('Error parsing ' + name + ':\n' + errorMessage); + } + } + dismissImportReport() { + this.isImportReportVisible(false); + } + } + AmeAdminCustomizer.AdminCustomizer = AdminCustomizer; +})(AmeAdminCustomizer || (AmeAdminCustomizer = {})); +jQuery(function () { + //Give other scripts a chance to load before we start. + //Some of them also use jQuery to run when the DOM is ready. + setTimeout(() => { + window.wsAdminCustomizer = new AmeAdminCustomizer.AdminCustomizer(wsAmeAdminCustomizerData); + const rootElement = document.getElementById('ame-ac-admin-customizer'); + if (rootElement === null) { + throw new Error('The root element for the admin customizer was not found.'); + } + ko.applyBindings(window.wsAdminCustomizer, rootElement); + //Notify the customizer that bindings have been applied. It needs to do some + //additional setup that can't be done until the DOM structure is ready. + setTimeout(() => { + window.wsAdminCustomizer.onBindingsApplied(rootElement); + }, 5); //Components are rendered asynchronously. + }, 20); +}); +//# sourceMappingURL=admin-customizer.js.map \ No newline at end of file diff --git a/extras/modules/admin-customizer/admin-customizer.js.map b/extras/modules/admin-customizer/admin-customizer.js.map new file mode 100644 index 0000000..98cae9e --- /dev/null +++ b/extras/modules/admin-customizer/admin-customizer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-customizer.js","sourceRoot":"","sources":["admin-customizer.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/admin-customizer.php b/extras/modules/admin-customizer/admin-customizer.php new file mode 100644 index 0000000..dd23911 --- /dev/null +++ b/extras/modules/admin-customizer/admin-customizer.php @@ -0,0 +1,2128 @@ + Settings that support preview via postMessage. + */ + protected $supportsPostMessage = []; + + protected $changesetName = null; + /** + * @var AcChangeset|null + */ + protected $currentChangeset = null; + + /** + * Setting preview values submitted to the current page in a POST request. + * + * This is used to preview setting changes that have not yet been saved + * to the changeset. The submitted values can override matching values + * in the current changeset. + * + * @see REFRESH_PREVIEW_ACTION + * @var array + */ + protected $submittedPreviewValues = []; + + protected $isPreviewFrameInitialized = false; + + /** + * @var \YahnisElsts\AdminMenuEditor\StyleGenerator\StyleGenerator[] + */ + protected $registeredStyleGenerators = []; + + public function __construct($menuEditor) { + parent::__construct($menuEditor); + + if ( $this->isInPreviewFrame() ) { + $this->initializePreviewFrame(); + } + + add_action('admin_menu', [$this, 'addAdminMenu']); + add_action('init', [$this, 'registerChangesetPostType']); + add_action('transition_post_status', [$this, 'applyChangesOnPublish'], 10, 3); + + add_action('wp_ajax_' . self::SAVE_CHANGESET_ACTION, [$this, 'ajaxSaveChangeset']); + add_action('wp_ajax_' . self::TRASH_CHANGESET_ACTION, [$this, 'ajaxTrashChangeset']); + add_action('wp_ajax_' . self::CREATE_THEME_ACTION, [$this, 'ajaxCreateAdminTheme']); + } + + public function addAdminMenu() { + if ( !$this->userCanAccessModule() ) { + return; + } + + $hook = add_options_page( + 'Admin Customizer', + 'Admin Customizer', + 'manage_options', + 'ame-admin-customizer', + [$this, 'outputAdminPage'] + ); + + add_action('load-' . $hook, [$this, 'initCustomizerPage']); + add_action('load-' . $hook, [$this, 'disableAdminHeader']); + } + + public function outputAdminPage() { + $this->outputMainTemplate(); + + //Stop execution to prevent the admin footer from being displayed. + exit; + } + + protected function getTemplateVariables($templateName) { + $variables = parent::getTemplateVariables($templateName); + $variables['settings'] = $this->registeredSettings; + $variables['structure'] = $this->structure; + $variables['currentChangeset'] = $this->currentChangeset; + $variables['returnUrl'] = $this->menuEditor->get_plugin_page_url(); + return $variables; + } + + public function initCustomizerPage() { + add_filter('admin_menu_editor-is_admin_customizer', '__return_true'); + + $this->registerCustomizableItems(); + $this->initializeCustomizerChangeset(); + $this->initializeSettingsPreview(); + + $this->disableAdminHeader(); + $this->enqueueDependencies(); + } + + public function disableAdminHeader() { + $_GET['noheader'] = 1; + } + + protected function enqueueDependencies() { + $this->menuEditor->register_base_dependencies(); + $this->registerBaseScripts(); + + wp_enqueue_auto_versioned_style( + 'ame-admin-customizer', + plugins_url('admin-customizer.css', __FILE__), + ['wp-admin', 'colors'] + ); + + $this->structure->enqueueKoComponentDependencies(); + + $settingInfo = AbstractSetting::serializeSettingsForJs( + $this->registeredSettings, + AbstractSetting::SERIALIZE_INCLUDE_VALUE + | AbstractSetting::SERIALIZE_INCLUDE_POST_MESSAGE_SUPPORT + | AbstractSetting::SERIALIZE_INCLUDE_VALIDATION + | AbstractSetting::SERIALIZE_LEAVES_ONLY, + function ($data, AbstractSetting $setting) { + //Backwards compatibility with older parts of the code that didn't + //have access to the "supportsPostMessage" property on the setting. + if ( !empty($this->supportsPostMessage[$setting->getId()]) ) { + $data['supportsPostMessage'] = true; + } + return $data; + } + ); + + $scriptData = [ + 'ajaxUrl' => admin_url('admin-ajax.php'), + 'changesetName' => $this->currentChangeset->getName(), + 'changesetItemCount' => count($this->currentChangeset), + 'changesetStatus' => $this->currentChangeset->getStatus(), + 'saveChangesetNonce' => wp_create_nonce(self::SAVE_CHANGESET_ACTION), + 'trashChangesetNonce' => wp_create_nonce(self::TRASH_CHANGESET_ACTION), + 'refreshPreviewNonce' => wp_create_nonce(self::REFRESH_PREVIEW_ACTION), + 'createThemeNonce' => wp_create_nonce(self::CREATE_THEME_ACTION), + 'settings' => $settingInfo, + 'interfaceStructure' => $this->structure->serializeForJs(), + 'initialPreviewUrl' => add_query_arg( + [ + 'ame-ac-preview' => 1, + 'ame-ac-changeset' => $this->currentChangeset->getName(), + ], + self_admin_url('options-general.php') + ), + 'allowedPreviewUrls' => $this->getAllowedPreviewBaseUrls(), + 'allowedCommOrigins' => $this->getAllowedCommunicationOrigins(), + 'isWpDebugEnabled' => defined('WP_DEBUG') && WP_DEBUG, + ]; + + $useBundles = defined('WS_AME_USE_BUNDLES') && WS_AME_USE_BUNDLES + && file_exists(AME_ROOT_DIR . '/dist/admin-customizer.bundle.js'); + + $customizerDep = ScriptDependency::create( + $useBundles + ? plugins_url('dist/admin-customizer.bundle.js', AME_ROOT_DIR . '/stub') + : plugins_url('admin-customizer.js', __FILE__), + 'ame-admin-customizer-js' + ); + if ( $useBundles ) { + $customizerDep->addDependencies( + 'ame-webpack-runtime', + 'ame-customizable-settings-bundle', + //The bundled style generator depends on this. + 'jquery-color' + ); + } else { + $customizerDep->addDependencies('ame-admin-customizer-base', 'ame-customizable-settings'); + //It's an ES6 module, but only when not when compiled with Webpack. + $customizerDep->setTypeToModule(); + } + $customizerDep->addDependencies( + 'jquery', + 'jquery-ui-menu', + 'jquery-ui-dialog', + 'ame-jszip', + 'ame-lodash', + 'ame-knockout', + 'ame-ko-extensions', + 'ame-jquery-cookie', + 'ame-mini-functional-lib', + 'ame-admin-customizer-communicator' + ) + ->addJsVariable('wsAmeAdminCustomizerData', $scriptData) + ->enqueue(); + + do_action('admin_menu_editor-enqueue_ac_dependencies'); + } + + protected function registerCustomizableItems() { + if ( $this->isRegistrationDone ) { + return; + } + $this->isRegistrationDone = true; + + $this->structure = new Controls\InterfaceStructure('Admin Customizer'); + do_action('admin_menu_editor-register_ac_items', $this); + + //Register all settings used by controls. This way modules generally don't + //have to do it explicitly. + foreach ($this->structure->getAllDescendants() as $element) { + if ( $element instanceof Controls\Control ) { + $this->addSettings($element->getSettings()); + } + } + + //Add a separator that will be used to separate frequently used sections + //from other sections. + $this->structure->add(new HorizontalSeparator(['id' => 'ame-ac-frequent-sections-separator'])); + + //Sort top-level sections. The most frequently used (by assumption) sections + //should be at the top, and the rest are sorted alphabetically. + $this->structure->sortChildren(function (UiElement $a, UiElement $b) { + $popularSectionOrder = [ + 'ame-branding-color-scheme' => 1, + 'ame-admin-menu' => 2, + 'ame-ds-toolbar' => 3, + 'ame-ac-frequent-sections-separator' => 4, + ]; + $aId = $a->getId(); + $bId = $b->getId(); + + $aIsPopular = isset($popularSectionOrder[$aId]); + $bIsPopular = isset($popularSectionOrder[$bId]); + + if ( $aIsPopular && $bIsPopular ) { + return $popularSectionOrder[$aId] - $popularSectionOrder[$bId]; + } elseif ( $aIsPopular ) { + return -1; + } elseif ( $bIsPopular ) { + return 1; + } else { + $aTitle = ($a instanceof Controls\Container) ? $a->getTitle() : $aId; + $bTitle = ($b instanceof Controls\Container) ? $b->getTitle() : $bId; + return strnatcasecmp($aTitle, $bTitle); + } + }); + } + + /** + * @param \YahnisElsts\AdminMenuEditor\Customizable\Settings\AbstractSetting[] $settings + * @return void + */ + public function addSettings($settings) { + //Note that for structs, we register both the struct itself and its children. + //While the struct itself usually won't be updated or previewed, it's still + //useful to have it for finding settings by tag. We can find the struct by + //a tag, then find all its children even if they don't have the tag themselves. + + foreach (AbstractSetting::recursivelyIterateSettings($settings) as $instance) { + $this->registeredSettings[$instance->getId()] = $instance; + } + } + + /** + * @param \YahnisElsts\AdminMenuEditor\Customizable\Controls\Container $section + * @return void + */ + public function addSection($section) { + if ( $section === null ) { + return; + } + $this->structure->add($section); + } + + /** + * @param \YahnisElsts\AdminMenuEditor\StyleGenerator\StyleGenerator $styleGenerator + * @return void + */ + public function addPreviewStyleGenerator(StyleGenerator $styleGenerator) { + $this->registeredStyleGenerators[] = $styleGenerator; + } + + /** + * Enable postMessage support for a setting. + * + * @param string|AbstractSetting|null $settingOrId + * @return void + */ + public function enablePostMessage($settingOrId) { + if ( $settingOrId instanceof AbstractSetting ) { + $setting = $settingOrId; + } else if ( is_string($settingOrId) ) { + $setting = $this->getSettingById($settingOrId); + } else { + return; + } + if ( $setting === null ) { + return; + } + + $this->supportsPostMessage[$setting->getId()] = true; + } + + /** + * Find an existing section by ID. + * + * @param $sectionId + * @return Option<\YahnisElsts\AdminMenuEditor\Customizable\Controls\Section> + */ + public function findSection($sectionId) { + if ( !$this->structure ) { + //Structure only gets initialized when registering items. + return Option::none(); + } + + $child = $this->structure->findChildById($sectionId); + if ( $child instanceof Controls\Section ) { + return Option::some($child); + } + return Option::none(); + } + + /** + * @param string $settingId + * @return \YahnisElsts\AdminMenuEditor\Customizable\Settings\AbstractSetting|null + */ + private function getSettingById($settingId) { + if ( isset($this->registeredSettings[$settingId]) ) { + return $this->registeredSettings[$settingId]; + } + /** @noinspection PhpRedundantOptionalArgumentInspection */ + return \ameMultiDictionary::get($this->registeredSettings, $settingId, null); + } + + private function isInPreviewFrame() { + //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No action, just checking if param is set. + return !empty($_GET['ame-ac-preview']) + && function_exists('is_user_logged_in') + && is_user_logged_in(); + } + + private function initializePreviewFrame() { + add_filter('admin_menu_editor-is_preview_frame', '__return_true'); + + $this->requirePreviewChangeset(); + $this->parseSubmittedPreviewValues(); + + add_action('init', [$this, 'requirePreviewAccessPermissions'], 1); + + add_action('wp_loaded', [$this, 'registerAndPreviewSettings']); + + add_action('admin_enqueue_scripts', [$this, 'enqueuePreviewDependencies']); + add_action('wp_enqueue_scripts', [$this, 'enqueuePreviewDependencies']); + + add_filter('admin_menu_editor-ac_add_preview_params', [$this, 'addPreviewParamsToUrl']); + + $this->isPreviewFrameInitialized = true; + } + + /** + * @return void + * @internal + */ + public function enqueuePreviewDependencies() { + $this->menuEditor->register_safe_js_libraries(); + $this->registerBaseScripts(); + + do_action('admin_menu_editor-register_ac_preview_deps', $this); + + wp_enqueue_auto_versioned_style( + 'ame-admin-customizer-preview-styles', + plugins_url('preview.css', __FILE__) + ); + + ScriptDependency::create( + plugins_url('preview-handler.js', __FILE__), + 'ame-admin-customizer-preview' + ) + ->setTypeToModule() //Uses other modules. + ->setAsync() //Load sooner. + ->addDependencies( + 'jquery', + 'ame-admin-customizer-communicator', + 'ame-customizable-settings', + 'ame-style-generator' + ) + ->addJsVariable('wsAmeAcPreviewData', [ + 'changesetName' => $this->currentChangeset->getName(), + 'allowedPreviewUrls' => $this->getAllowedPreviewBaseUrls(), + 'allowedCommOrigins' => $this->getAllowedCommunicationOrigins(), + 'isWpDebugEnabled' => defined('WP_DEBUG') && WP_DEBUG, + 'settings' => AbstractSetting::serializeSettingsForJs( + $this->registeredSettings, + AbstractSetting::SERIALIZE_INCLUDE_VALUE | AbstractSetting::SERIALIZE_LEAVES_ONLY + ), + 'stylePreviewConfigs' => array_map( + function (StyleGenerator $generator) { + return $generator->getJsPreviewConfiguration(); + }, + $this->registeredStyleGenerators + ), + ]) + ->enqueue(); + + do_action('admin_menu_editor-enqueue_ac_preview'); + } + + private function registerBaseScripts() { + if ( function_exists('ws_ame_register_customizable_js_lib') ) { + ws_ame_register_customizable_js_lib(); + } + + $base = ScriptDependency::create( + plugins_url('admin-customizer-base.js', __FILE__), + 'ame-admin-customizer-base' + ) + ->addDependencies('jquery', 'ame-customizable-settings') + ->setTypeToModule() + ->register(); + + ScriptDependency::create( + plugins_url('communicator.js', __FILE__), + 'ame-admin-customizer-communicator' + ) + ->addDependencies('jquery', $base) + ->register(); + } + + private function getAllowedPreviewBaseUrls() { + $results = self::generateAllowedUrls(); + if ( is_ssl() ) { + $results = array_merge($results, self::generateAllowedUrls('https')); + } + return array_unique($results); + } + + private static function generateAllowedUrls($scheme = null) { + $params = ['/']; + if ( $scheme !== null ) { + $params[] = $scheme; + } + $results = [ + home_url(...$params), + admin_url(...$params), + ]; + if ( is_multisite() ) { + $results[] = network_admin_url(...$params); + $results[] = user_admin_url(...$params); + } + return $results; + } + + private function getAllowedCommunicationOrigins() { + $previewUrls = $this->getAllowedPreviewBaseUrls(); + $results = []; + foreach ($previewUrls as $url) { + $scheme = wp_parse_url($url, PHP_URL_SCHEME); + $host = wp_parse_url($url, PHP_URL_HOST); + //Skip invalid URLs. + if ( ($scheme === null) || ($host === null) ) { + continue; + } + $results[] = $scheme . '://' . $host; + } + return array_unique($results); + } + + /** + * Register customizable settings and enable preview. + * + * This needs to run after other modules have been loaded so that modules can + * register their settings. However, it also needs to run early enough so that + * we can set up the preview before those settings are actually *used*. + * + * @access private It's only public because it's a hook callback. + */ + public function registerAndPreviewSettings() { + $this->registerCustomizableItems(); + $this->initializeSettingsPreview(); + } + + /** + * Initialize the preview of registered settings using values from the current + * changeset and the current POST request, if any. + */ + private function initializeSettingsPreview() { + $previewValues = []; + $changeset = $this->getCurrentChangeset(); + if ( !empty($changeset) ) { + foreach ($changeset as $settingId => $value) { + $previewValues[$settingId] = $value; + } + } + $previewValues = array_merge($previewValues, $this->submittedPreviewValues); + + foreach ($previewValues as $settingId => $value) { + if ( isset($this->registeredSettings[$settingId]) ) { + $this->registeredSettings[$settingId]->preview($value); + } + } + } + + /** + * Load the current changeset while inside the preview frame. + * + * Will exit with an error if the changeset name is specified but invalid. + * + * @return void + */ + private function requirePreviewChangeset() { + //Let other modules retrieve the changeset once it's ready. + add_filter('admin_menu_editor-ac_preview_frame_changeset', function () { + return $this->currentChangeset; + }); + + //The changeset name can be passed as a query parameter. + if ( empty($this->changesetName) ) { + $this->changesetName = $this->getChangesetNameFromRequest()->getOrElse(null); + } + if ( empty($this->changesetName) ) { + //This can happen if the customizer has a created a new changeset, + //but hasn't saved it yet. Let's use a temporary empty changeset. + $this->currentChangeset = new AcChangeset(); + return; + } + + //Changeset name must be a UUID. + if ( !wp_is_uuid($this->changesetName) ) { + wp_die('Invalid changeset name.', 400); + } + + //Changeset must exist. + $changeset = $this->loadChangeset($this->changesetName); + if ( $changeset->isEmpty() ) { + wp_die('The specified changeset does not exist.', 404); + } + $this->currentChangeset = $changeset->get(); + } + + /** + * @return Option + */ + private function getChangesetNameFromRequest() { + //phpcs:disable WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput + if ( !empty($_POST['ame-ac-changeset']) ) { + return Option::fromValue(sanitize_key(strval($_POST['ame-ac-changeset']))); + } else if ( isset($_GET['ame-ac-changeset']) ) { + return Option::fromValue(sanitize_key(strval($_GET['ame-ac-changeset']))); + } + //phpcs:enable + return None::getInstance(); + } + + private function parseSubmittedPreviewValues() { + $this->submittedPreviewValues = []; + if ( + empty($_POST['action']) + || ($_POST['action'] !== self::REFRESH_PREVIEW_ACTION) + || empty($_POST['nonce']) + || empty($_POST['modified']) + ) { + return; + } + + //Note: This method is expected to run before WordPress adds magic quotes + //to $_POST. We don't need wp_unslash() here. + + //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $verified = wp_verify_nonce(strval($_POST['nonce']), self::REFRESH_PREVIEW_ACTION); + if ( $verified === false ) { + return; + } + + //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is JSON. + $modified = json_decode(strval($_POST['modified']), true); + if ( !is_array($modified) ) { + return; + } + $this->submittedPreviewValues = $modified; + } + + /** + * Check if the current user can access the preview and exit with an error if not. + * + * While I'm not aware of any specific security issues with previewing + * settings, let's be safe and allow only users who have access to this + * module to load the site in preview mode. + * + * Preview gets initialized before WordPress authenticates the user, + * so this is implemented as a separate check that runs later. + * + * @return void + * @internal + */ + public function requirePreviewAccessPermissions() { + if ( !$this->userCanAccessModule() ) { + wp_die('You do not have permission to preview settings.', 403); + } + } + + /** + * Add query parameters that will enable preview mode to the specified URL. + * + * Only works while inside a fully initialized preview frame. In other contexts, + * this method returns the original URL unchanged. + * + * This is used as a filter callback that lets other modules add preview parameters + * to their own URLs without having to somehow get a reference to the admin customizer. + * + * @param string $url + * @return string + */ + public function addPreviewParamsToUrl($url) { + if ( $this->isPreviewFrameInitialized && $this->currentChangeset ) { + return add_query_arg( + [ + 'ame-ac-preview' => '1', + 'ame-ac-changeset' => $this->currentChangeset->getName(), + ], + $url + ); + } + return $url; + } + + /** + * Load or create a changeset for the admin customizer. + * + * This is for the customizer UI, not for the preview frame. + * + * @return void + */ + private function initializeCustomizerChangeset() { + if ( $this->isInPreviewFrame() ) { + throw new \LogicException('This method should not be called in the preview frame.'); + } + if ( !empty($this->currentChangeset) ) { + return; //Already loaded. + } + + //Load the changeset specified in a query parameter, load the most recent + //draft changeset, or create a new changeset. + $strategies = [ + [$this, 'loadRequestedChangeset'], + [$this, 'getLatestUnfinishedChangeset'], + [$this, 'createNewChangeset'], + ]; + foreach ($strategies as $strategy) { + /** @var Option $changeset */ + $changeset = call_user_func($strategy); + if ( $changeset->isDefined() ) { + $this->currentChangeset = $changeset->get(); + $this->changesetName = $this->currentChangeset->getName(); + break; + } + } + + if ( $this->currentChangeset === null ) { + wp_die('Could not load or create a changeset.', 500); + } + } + + /** + * @return Option + */ + private function loadRequestedChangeset() { + return $this->getChangesetNameFromRequest()->flatMap(function ($name) { + return $this->loadChangeset($name); + }); + } + + /** + * @param $name + * @return Option + */ + private function loadChangeset($name) { + $results = $this->findChangesets([ + 'name' => $name, + 'posts_per_page' => 1, + ]); + if ( empty($results) ) { + return Option::fromValue(null); + } + return Option::fromValue(reset($results)); + } + + /** + * @param array $args + * @return AcChangeset[] + */ + private function findChangesets($args) { + $defaults = [ + 'post_type' => self::CHANGESET_POST_TYPE, + 'posts_per_page' => -1, + //Allow all statuses. get_posts() would usually default to "publish" only. + 'post_status' => get_post_stati(), + 'orderby' => 'date', + 'order' => 'DESC', + + 'no_found_rows' => true, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + ]; + + $args = array_merge($defaults, $args); + $posts = get_posts($args); + if ( empty($posts) ) { + return []; + } + + $results = []; + foreach ($posts as $post) { + $changeset = AcChangeset::fromPost($post); + if ( $changeset->isDefined() ) { + $results[] = $changeset->get(); + } + } + + return $results; + } + + /** + * Get the most recent valid changeset that is not an auto-draft and is not yet published. + * + * @return Option + */ + private function getLatestUnfinishedChangeset() { + $results = $this->findChangesets([ + 'post_status' => array_diff( + get_post_stati(), + ['auto-draft', 'publish', 'trash', 'inherit',] + ), + 'posts_per_page' => 1, + 'author' => get_current_user_id(), + ]); + + foreach ($results as $changeset) { + return Option::fromValue($changeset); + } + return None::getInstance(); + } + + private function createNewChangeset() { + $name = wp_generate_uuid4(); + $changeset = new AcChangeset($name); + + $postId = $this->saveChangeset($changeset); + if ( is_wp_error($postId) || empty($postId) ) { + return None::getInstance(); + } + + return Option::fromValue($changeset); + } + + /** + * @param AcChangeset $changeset + * @param string|null $status + * @return int|\WP_Error Post ID on success, or an error object on failure. + */ + private function saveChangeset(AcChangeset $changeset, $status = null) { + $postArray = [ + 'post_content' => wp_json_encode($changeset->jsonSerialize(), JSON_PRETTY_PRINT), + ]; + + $existingPostId = $changeset->getPostId(); + if ( $existingPostId !== null ) { + $postArray['ID'] = $existingPostId; + } else { + //The new changeset must already have a name. + $name = $changeset->getName(); + if ( empty($name) ) { + return new \WP_Error('ame_changeset_missing_name', 'Changeset is missing a name.'); + } + + $postArray['post_type'] = self::CHANGESET_POST_TYPE; + $postArray['post_status'] = 'auto-draft'; //Default. + $postArray['post_name'] = $name; + $postArray['post_title'] = $name; + $postArray['post_author'] = get_current_user_id(); + } + + //Optionally, change the status. + if ( $status !== null ) { + $postArray['post_status'] = $status; + } + + //Apparently, updating the post date extends the life of the auto-draft + //and prevents it from being automatically deleted. + if ( + //Is this post already an auto-draft? + $existingPostId && (get_post_status($existingPostId) === 'auto-draft') + //Will it remain an auto-draft? + && (empty($postArray['post_status']) || ($postArray['post_status'] === 'auto-draft')) + ) { + $postArray['post_date'] = current_time('mysql'); + $postArray['post_date_gmt'] = ''; + } + + //Prevent WordPress from corrupting JSON data by "sanitizing" it. + add_filter('wp_insert_post_data', [$this, 'preserveUnsanitizedPostContent'], 20, 3); + if ( $existingPostId ) { + $postId = wp_update_post(wp_slash($postArray), true); + } else { + $postId = wp_insert_post(wp_slash($postArray), true); + } + remove_filter('wp_insert_post_data', [$this, 'preserveUnsanitizedPostContent'], 20); + + if ( is_wp_error($postId) ) { + return $postId; + } else if ( $postId === 0 ) { + return new \WP_Error('ame_changeset_save_failed', 'Could not save the changeset.'); + } + + if ( !$existingPostId ) { + $changeset->setPostId($postId); + } + return $postId; + } + + /** + * + * @param array $data + * @param array $postArr + * @param array $unsanitizedPostArr + * @return array + * @internal This is a callback for the wp_insert_post_data filter. + * + * While the second argument is not used in this method, it is part of the filter + * signature and cannot be omitted because we need the third argument. + * @noinspection PhpUnusedParameterInspection + */ + public static function preserveUnsanitizedPostContent($data, $postArr = [], $unsanitizedPostArr = []) { + //This is basically a reimplementation of what WP_Customize_Manager does. + if ( + isset($unsanitizedPostArr['post_content']) + && isset($data['post_type']) + && ( + ($data['post_type'] === self::CHANGESET_POST_TYPE) + || ( + ($data['post_type'] === 'revision') + && !empty($data['post_parent']) + && (get_post_type($data['post_parent']) === self::CHANGESET_POST_TYPE) + ) + ) + ) { + $data['post_content'] = $unsanitizedPostArr['post_content']; + } + return $data; + } + + private function getCurrentChangeset() { + return $this->currentChangeset; + } + + /** + * @internal + */ + public function registerChangesetPostType() { + $capability = $this->menuEditor->current_user_can_edit_menu() + ? 'manage_options' + : 'do_not_allow'; + + register_post_type( + self::CHANGESET_POST_TYPE, + [ + 'public' => false, + 'hierarchical' => false, + 'rewrite' => false, + 'query_var' => false, + 'can_export' => false, + 'delete_with_user' => false, + 'supports' => ['title', 'author'], + + /* AFAICT, the "map_meta_cap" argument needs to be set to TRUE + * for WordPress to map meta capabilities like "edit_post" to + * context-specific capabilities like "edit_published_posts". + * + * Without it, checking for "edit_post" might just use the provided + * "edit_post" capability for all items regardless of their status. + */ + 'map_meta_cap' => true, + 'capability_type' => self::CHANGESET_POST_TYPE, + 'capabilities' => [ + 'create_posts' => $capability, + 'delete_others_posts' => $capability, + 'delete_posts' => $capability, + 'delete_private_posts' => $capability, + 'delete_published_posts' => $capability, + 'edit_others_posts' => $capability, + 'edit_posts' => $capability, + 'edit_private_posts' => $capability, + 'edit_published_posts' => 'do_not_allow', + 'publish_posts' => $capability, + 'read' => 'read', + 'read_private_posts' => $capability, + + /* + The "edit_post", "delete_post", and "read_post" keys are intentionally + omitted. Using these keys makes WordPress treat the specified capability + as a meta capability. + + For example, if we set "delete_post" to "manage_options", every time + something checks the "manage_options" capability, WordPress will try + to map it to "delete_post", which will fail without a post ID. This + would effectively make the "manage_options" capability useless. + */ + ], + ] + ); + } + + public function ajaxSaveChangeset() { + check_ajax_referer(self::SAVE_CHANGESET_ACTION); + + if ( !$this->userCanAccessModule() ) { + wp_send_json_error( + ['message' => 'You are not allowed to edit Admin Customizer changesets.'], + 403 + ); + //Technically, we don't need to call "exit" since wp_send_json_error() + //will die(), but this lets my IDE know that execution stops here. + exit; + } + + $post = $this->menuEditor->get_post_params(); + + /** Should we save the changeset even if some values are invalid? */ + $partialUpdatesAllowed = true; + + //The list of modified settings must be provided. + if ( !isset($post['modified']) ) { + wp_send_json_error(['message' => 'Missing "modified" parameter.'], 400); + exit; + } + + $modified = json_decode((string)($post['modified']), true); + if ( !is_array($modified) ) { + wp_send_json_error( + ['message' => 'Invalid "modified" parameter. It must be a JSON object.'], + 400 + ); + exit; + } + + //We'll need the post type object to check user permissions. + $postType = get_post_type_object(self::CHANGESET_POST_TYPE); + if ( !$postType ) { + //This should never happen unless there's a bug. + wp_send_json_error(['message' => 'The changeset post type is missing.'], 500); + exit; + } + + $response = new AcSaveResponse(); + + $createNewChangeset = empty($post['changeset']) && !empty($post['createNew']); + if ( $createNewChangeset ) { + if ( !current_user_can($postType->cap->create_posts) ) { + wp_send_json_error( + ['message' => 'You are not allowed to create new changesets.'], + 403 + ); + exit; + } + + $changeset = new AcChangeset(wp_generate_uuid4()); + } else { + /** + * @var Option $csOption + */ + $csOption = $this->loadChangeset((string)($post['changeset'])); + if ( $csOption->isEmpty() ) { + wp_send_json_error(['message' => 'Changeset not found.'], 400); + exit; + } + $changeset = $csOption->get(); + + if ( !current_user_can('edit_post', $changeset->getPostId()) ) { + wp_send_json_error( + $response->error('You do not have the required permissions to edit this changeset.'), + 403 + ); + exit; + } + } + /** @var AcChangeset $changeset */ + + //We need the setting objects to validate submitted values. + $this->registerCustomizableItems(); + + //Validate the submitted values. + $validationResults = []; + $successfulChanges = 0; + foreach ($modified as $settingId => $value) { + //Skip unmodified settings. + if ( + $changeset->hasValueFor($settingId) + && ($changeset->getSettingValue($settingId) === $value) + ) { + $response->addNote(sprintf('%s: %s', $settingId, 'New value is the same as old value.')); + continue; + } + + $validity = new AcValidationState(); + $validationResults[$settingId] = $validity; + + //The setting must exist. + $setting = $this->getSettingById($settingId); + if ( $setting === null ) { + $validity->addError(new \WP_Error( + 'ame_invalid_setting_id', + 'Setting not found: ' . $settingId + )); + $response->addNote(sprintf('%s: %s', $settingId, 'Setting not found.')); + continue; + } + + //Check user permissions. + if ( !$setting->isEditableByUser() ) { + $validity->addError(new \WP_Error( + 'ame_permission_denied', + 'You do not have permission to change this setting.' + )); + $response->addNote(sprintf('%s: %s', $settingId, 'You do not have permission to change this setting.')); + continue; + } + + //Validate. + $validationResult = $setting->validate(new \WP_Error(), $value); + if ( is_wp_error($validationResult) ) { + $validity->addError($validationResult); + $response->addNote(sprintf('%s: Validation error: %s', $settingId, $validationResult->get_error_message())); + continue; + } + $sanitizedValue = $validationResult; + + //Finally, update the value. + $changeset->setSettingValue($settingId, $sanitizedValue); + $successfulChanges++; + + $response->addNote(sprintf('%s: %s', $settingId, 'Value updated.')); + $validity->setValid(true); + } + + $response->setValidationResults($validationResults); + $response->setChangeset($changeset); + + $hasValidationErrors = false; + foreach ($validationResults as $validity) { + if ( !$validity->isValid() ) { + $hasValidationErrors = true; + break; + } + } + + if ( $hasValidationErrors && !$partialUpdatesAllowed ) { + wp_send_json_error( + $response->error('There were one or more validation errors. Changes were not saved.'), + 400 + ); + exit; + } + + //A published or trashed changeset cannot be modified. + $currentStatus = $changeset->getStatus(); + if ( in_array($currentStatus, ['publish', 'future', 'trash']) ) { + wp_send_json_error( + $response->error('This changeset is already published or trashed, so it cannot be modified.'), + 400 + ); + exit; + } + + //Will the status change? + $newStatus = null; + if ( + !empty($post['status']) + && AcChangeset::isSupportedStatus($post['status']) + && ($post['status'] !== $currentStatus) + ) { + $newStatus = $post['status']; + } + + //We don't support deleting changesets through this endpoint. + if ( $newStatus === 'trash' ) { + wp_send_json_error( + $response->error('This endpoint does not support changeset deletion.'), + 400 + ); + exit; + } + + //Is the user allowed to apply the new status? + if ( $newStatus !== null ) { + $postId = $changeset->getPostId(); + if ( ($newStatus === 'publish') || ($newStatus === 'future') ) { + if ( $postId ) { + $allowed = current_user_can('publish_post', $postId); + } else { + $allowed = current_user_can($postType->cap->publish_posts); + } + if ( !$allowed ) { + wp_send_json_error( + $response->error('You are not allowed to publish or schedule this changeset.'), + 403 + ); + exit; + } + } + } + + $response->mergeWith(['updatedValues' => $successfulChanges]); + + if ( ($successfulChanges > 0) || $createNewChangeset || ($newStatus !== null) ) { + $changeset->setLastModifiedToNow(); + $saved = $this->saveChangeset($changeset, $newStatus); + if ( is_wp_error($saved) ) { + wp_send_json_error( + $response->error( + 'Error saving changeset: ' . $saved->get_error_message(), + $saved->get_error_code() + ), + 500 + ); + exit; + } + + //If the changeset was published or trashed, the customizer will need + //a new changeset. + if ( in_array($changeset->getStatus(), ['publish', 'future', 'trash']) ) { + $this->createNewChangeset()->each( + function (AcChangeset $newChangeset) use ($response) { + $response->mergeWith(['nextChangeset' => $newChangeset->getName()]); + } + ); + } + + wp_send_json_success( + $response->mergeWith([ + 'message' => 'Changeset saved.', + 'changesetWasPublished' => ($newStatus === 'publish'), + ]), + 200 //Requires WP 4.7+ + ); + } else { + //We don't need to save the changeset if there are no actual changes, + //but this is still technically a success. + wp_send_json_success( + $response->mergeWith(['message' => 'No changes were made.']), + 200 //Requires WP 4.7+ + ); + } + } + + public function ajaxTrashChangeset() { + check_ajax_referer(self::TRASH_CHANGESET_ACTION); + + if ( !$this->userCanAccessModule() ) { + wp_send_json_error( + ['message' => 'You are not allowed to edit Admin Customizer changesets.'], + 403 + ); + exit; + } + + $postType = get_post_type_object(self::CHANGESET_POST_TYPE); + if ( !$postType ) { + wp_send_json_error(['message' => 'The changeset post type is missing.'], 500); + exit; + } + + $post = $this->menuEditor->get_post_params(); + $csOption = $this->loadChangeset((string)($post['changeset'])); + if ( $csOption->isEmpty() ) { + wp_send_json_error(['message' => 'Changeset not found.'], 400); + exit; + } + $changeset = $csOption->get(); + + $currentStatus = $changeset->getStatus(); + if ( $currentStatus === 'trash' ) { + wp_send_json_error(['message' => 'This changeset is already trashed.'], 400); + exit; + } + + $postId = $changeset->getPostId(); + if ( isset($postType->cap->delete_post) ) { + $allowed = current_user_can($postType->cap->delete_post, $postId); + } else { + $allowed = current_user_can('delete_post', $postId); + } + if ( !$allowed ) { + wp_send_json_error(['message' => 'You are not allowed to trash this changeset.'], 403); + exit; + } + + if ( !wp_trash_post($postId) ) { + wp_send_json_error(['message' => 'Unexpected error while moving the changeset to Trash.'], 500); + } else { + wp_send_json_success(['message' => 'Changeset trashed.'], 200); + } + exit; + } + + /** + * @param string $newStatus + * @param string $oldStatus + * @param \WP_Post $post + * @return void + */ + public function applyChangesOnPublish($newStatus, $oldStatus, $post) { + if ( $oldStatus === $newStatus ) { + return; //No change. + } + + $isChangesetBeingPublished = ($newStatus === 'publish') + && ($post instanceof \WP_Post) + && ($post->post_type === self::CHANGESET_POST_TYPE); + if ( !$isChangesetBeingPublished ) { + return; + } + + //Instantiate a changeset from the post. + $option = AcChangeset::fromPost($post); + if ( !$option->isDefined() ) { + return; //Not a valid changeset. + } + $changeset = $option->get(); + /** @var AcChangeset $changeset */ + + //Ensure settings are registered. + $this->registerCustomizableItems(); + + //Validate, update settings, then save all updated settings. + $affectedSettings = []; + foreach ($changeset as $settingId => $value) { + $setting = $this->getSettingById($settingId); + if ( $setting === null ) { + continue; + } + + $validationResult = $setting->validate(new \WP_Error(), $value, true); + if ( is_wp_error($validationResult) ) { + continue; + } + $sanitizedValue = $validationResult; + + $setting->update($sanitizedValue); + $affectedSettings[] = $setting; + } + + AbstractSetting::saveAll($affectedSettings); + + //Trash the changeset after it has been applied. We don't want to keep old + //changesets in the database - they take up space and serve no useful purpose. + wp_trash_post($post->ID); + } + + private function userCanAccessModule() { + return $this->menuEditor->current_user_can_edit_menu(); + } + + /** + * This method has side effects due to enabling preview for settings that + * are part of the changeset. It is not safe to call it multiple times with + * different changesets. + * + * @param \YahnisElsts\AdminMenuEditor\AdminCustomizer\AcChangeset $changeset + * @param \YahnisElsts\AdminMenuEditor\AdminCustomizer\AcAdminThemeMetadata $metadata + * @return \YahnisElsts\AdminMenuEditor\AdminCustomizer\AcAdminTheme + */ + private function createAdminTheme(AcChangeset $changeset, AcAdminThemeMetadata $metadata) { + //We need the setting objects to find out which settings are tagged + //as part of the admin theme, and also to enable preview so that we + //can generate admin theme CSS using the current values. + $this->registerCustomizableItems(); + + //Find admin theme settings. + //Note: Even if some children of structs do not inherit the admin theme tag from + //the parent, the children will still be included. + $settings = array_filter( + $this->registeredSettings, + function (AbstractSetting $setting) { + return $setting->hasTag(AbstractSetting::TAG_ADMIN_THEME); + } + ); + + //Enable preview for all admin theme settings that are in the changeset. + //This way CSS generation should use the current values. + foreach ($changeset as $settingId => $value) { + if ( !isset($settings[$settingId]) ) { + continue; + } + $settings[$settingId]->preview($value); + } + + $adminTheme = new AcAdminTheme($metadata, $settings); + + //Generate admin theme CSS. + $themeCssParts = []; + $addThemeCss = function ($css) use (&$themeCssParts) { + if ( !is_string($css) || empty($css) ) { + return; + } + $themeCssParts[] = $css; + }; + + //Modules can add CSS by using this action and calling $addThemeCss. + do_action('admin_menu_editor-ac_admin_theme_css', $addThemeCss, $adminTheme); + + if ( empty($themeCssParts) ) { + throw new \RuntimeException('The current settings did not generate any CSS for an admin theme.'); + } + $adminTheme->setMainStylesheet(implode("\n", $themeCssParts)); + + //Optionally, the admin theme can include an admin color scheme. + $colorScheme = apply_filters('admin_menu_editor-ac_admin_theme_color_scheme', null, $adminTheme); + if ( $colorScheme !== null ) { + $adminTheme->setColorScheme($colorScheme); + } + + return $adminTheme; + } + + public function ajaxCreateAdminTheme() { + check_ajax_referer(self::CREATE_THEME_ACTION); + + if ( !$this->userCanAccessModule() ) { + wp_send_json_error( + ['message' => 'You do not have permission to create an admin theme.'], + 403 + ); + exit; + } + + $post = $this->menuEditor->get_post_params(); + + //The changeset name must be specified explicitly. + if ( !isset($post['changeset']) ) { + wp_send_json_error( + ['message' => 'The changeset name must be specified.'], + 400 + ); + exit; + } + + //The request must specify a cookie that will be used to detect that + //the download has started. + if ( !isset($post['downloadCookieName']) ) { + wp_send_json_error( + ['message' => 'The cookie name must be specified.'], + 400 + ); + exit; + } + + //For security, the cookie name must start with a known prefix and must + //only contain alphanumeric characters and underscores. + $cookieName = strval($post['downloadCookieName']); + if ( substr($cookieName, 0, strlen(self::DOWNLOAD_COOKIE_PREFIX)) !== self::DOWNLOAD_COOKIE_PREFIX ) { + wp_send_json_error(['message' => 'The cookie name is invalid.'], 400); + exit; + } + if ( strlen($cookieName) > 200 ) { + wp_send_json_error(['message' => 'The cookie name is too long.'], 400); + exit; + } + if ( preg_match('/[^a-zA-Z0-9_]/', substr($cookieName, strlen(self::DOWNLOAD_COOKIE_PREFIX))) ) { + wp_send_json_error(['message' => 'The cookie name contains invalid characters.'], 400); + exit; + } + + //Load the changeset and check permissions. + $csOption = $this->loadChangeset((string)($post['changeset'])); + if ( $csOption->isEmpty() ) { + wp_send_json_error(['message' => 'Changeset not found.'], 400); + exit; + } + $changeset = $csOption->get(); + + if ( !current_user_can('read_post', $changeset->getPostId()) ) { + wp_send_json_error( + ['message' => 'You do not have permission to read the specified changeset.'], + 403 + ); + exit; + } + + //Note: Any pending changes to the changeset should be saved before + //calling this AJAX action. Otherwise, the admin theme will be generated + //without those changes. + + //Get the admin theme metadata. + if ( !isset($post['metadata']) ) { + wp_send_json_error( + ['message' => 'The admin theme metadata must be specified.'], + 400 + ); + exit; + } + try { + $metadata = AcAdminThemeMetadata::parseJson((string)$post['metadata']); + } catch (\Exception $e) { + wp_send_json_error( + ['message' => $e->getMessage(), 'code' => 'metadata_parse_error'], + 400 + ); + exit; + } + + //Required WP version must be at least 4.7. + if ( empty($metadata->requiredWpVersion) || version_compare($metadata->requiredWpVersion, '4.7', '<') ) { + $metadata->requiredWpVersion = '4.7'; + } + //Tested WP version is at least the current WP version. + try { + $currentWpVersion = AcAdminThemeMetadata::parseVersionNumber(get_bloginfo('version')); + } catch (\Exception $e) { + $currentWpVersion = ''; + } + if ( + !empty($currentWpVersion) + && ( + empty($metadata->testedWpVersion) + || version_compare($metadata->testedWpVersion, $currentWpVersion, '<') + ) + ) { + $metadata->testedWpVersion = $currentWpVersion; + } + + //Create the admin theme. + try { + $adminTheme = $this->createAdminTheme($changeset, $metadata); + $zipFileContent = $adminTheme->toZipString(); + } catch (\Exception $e) { + //There should be no exceptions here, but just in case. + wp_send_json_error( + ['message' => $e->getMessage(), 'code' => 'admin_theme_generation_error'], + 500 + ); + exit; + } + + //Set a cookie that will be used to detect that the download has started. + $cookieValue = uniqid('', true); + if ( version_compare(phpversion(), '7.3', '>=') ) { + setcookie( + $cookieName, + $cookieValue, + [ + 'expires' => 0, //Session cookie. + 'path' => '/', + 'samesite' => 'Lax', + 'secure' => is_ssl(), + 'httponly' => false, //Our JS needs to read the cookie. + ] + ); + } else { + setcookie($cookieName, $cookieValue, 0, '/', '', is_ssl(), false); + } + + //Output the file as a download. + $filename = $adminTheme->getZipFileName(); + header('Content-Type: application/zip'); + header('Content-Disposition: attachment; filename="' . $filename . '"'); + header('Content-Length: ' . strlen($zipFileContent)); + header('Content-Transfer-Encoding: binary'); + + header('Cache-Control: private, no-transform, no-store, must-revalidate'); + header('Pragma: no-cache'); + header('Expires: 0'); + + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $zipFileContent is binary data. + echo $zipFileContent; + exit; + } +} + +class AcChangeset implements \IteratorAggregate, \JsonSerializable, \Countable { + /** + * @var array + */ + private $settingValues = []; + + private $postId = null; + private $name = null; + private $lastModified; + + private $status = null; + + public function __construct($name = null) { + if ( $name !== null ) { + $this->name = $name; + } + $this->lastModified = time(); + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function getIterator() { + return new \ArrayIterator($this->settingValues); + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function jsonSerialize() { + return [ + 'settings' => $this->settingValues, + 'lastModified' => $this->lastModified, + ]; + } + + /** + * @param \WP_Post $post + * @return Option + */ + public static function fromPost(\WP_Post $post) { + return static::fromJson($post->post_content)->each( + function ($changeset) use ($post) { + $changeset->postId = $post->ID; + $changeset->name = $post->post_name; + } + ); + } + + /** + * @param string $jsonString + * @return Option + */ + public static function fromJson($jsonString) { + $data = json_decode($jsonString, true); + if ( !is_array($data) ) { + return None::getInstance(); + } + return Option::fromValue(static::fromArray($data)); + } + + /** + * @param $data + * @return static + */ + public static function fromArray($data) { + $changeset = new static(); + $changeset->settingValues = $data['settings']; + $changeset->lastModified = isset($data['lastModified']) ? intval($data['lastModified']) : null; + return $changeset; + } + + /** + * @param null|int $postId + * @return AcChangeset + */ + public function setPostId($postId) { + if ( $this->postId !== null ) { + throw new \RuntimeException('Cannot change the post ID of an existing changeset.'); + } + $this->postId = $postId; + return $this; + } + + /** + * @return null|int + */ + public function getPostId() { + return $this->postId; + } + + /** + * @return string|null + */ + public function getName() { + return $this->name; + } + + public function setSettingValue($settingId, $value) { + $this->settingValues[$settingId] = $value; + } + + /** + * @param string $settingId + * @return mixed|null + */ + public function getSettingValue($settingId) { + if ( array_key_exists($settingId, $this->settingValues) ) { + return $this->settingValues[$settingId]; + } + return null; + } + + /** + * @param string $settingId + * @return bool + */ + public function hasValueFor($settingId) { + return array_key_exists($settingId, $this->settingValues); + } + + public static function isSupportedStatus($status) { + return in_array($status, [ + 'draft', + 'auto-draft', + 'publish', + 'future', + 'trash', + 'private', + 'pending', + ]); + } + + /** + * @return string|null + */ + public function getStatus() { + if ( !empty($this->postId) ) { + $status = get_post_status($this->postId); + if ( $status ) { + return $status; + } + } + return $this->status; + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function count() { + return count($this->settingValues); + } + + /** + * @return int|null + */ + public function getLastModified() { + return $this->lastModified; + } + + public function setLastModifiedToNow() { + $this->lastModified = time(); + } +} + +class AcValidationState implements \JsonSerializable { + /** + * @var bool + */ + protected $isValid = true; + /** + * @var \WP_Error[] + */ + protected $errors = []; + + public function addError(\WP_Error $error, $markAsInvalid = true) { + $this->errors[] = $error; + if ( $markAsInvalid ) { + $this->isValid = false; + } + } + + public function setValid($valid) { + $this->isValid = $valid; + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function jsonSerialize() { + //Convert WP_Error instances to JSON-compatible associative arrays. + $serializableErrors = []; + foreach ($this->errors as $error) { + foreach ($error->errors as $code => $messages) { + foreach ($messages as $message) { + $serializableErrors[] = [ + 'code' => $code, + 'message' => $message, + ]; + } + } + } + + return [ + 'isValid' => $this->isValid, + 'errors' => $serializableErrors, + ]; + } + + /** + * @return bool + */ + public function isValid() { + return $this->isValid; + } +} + +class AcSaveResponse implements \JsonSerializable { + private $fields = []; + private $notes = []; + /** + * @var AcChangeset|null + */ + private $changeset = null; + + /** + * @param array $additionalFields + * @return $this + */ + public function mergeWith($additionalFields) { + $this->fields = array_merge($this->fields, $additionalFields); + return $this; + } + + public function error($message, $code = null) { + $this->fields['message'] = $message; + if ( $code !== null ) { + $this->fields['code'] = $code; + } + return $this; + } + + /** + * @param array $validationResults + * @return $this + */ + public function setValidationResults($validationResults) { + $this->fields['validationResults'] = $validationResults; + return $this; + } + + public function setChangeset(AcChangeset $changeset) { + $this->changeset = $changeset; + } + + /** + * @param string $message + * @return $this + */ + public function addNote($message) { + $this->notes[] = $message; + return $this; + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function jsonSerialize() { + $result = $this->fields; + if ( $this->changeset !== null ) { + $result = array_merge($result, [ + 'changeset' => $this->changeset->getName(), + 'changesetItemCount' => count($this->changeset), + 'changesetStatus' => $this->changeset->getStatus(), + ]); + } + if ( !empty($this->notes) ) { + $result['notes'] = $this->notes; + } + return $result; + } +} + +class AcAdminThemeMetadata implements \JsonSerializable { + const MAX_FIELD_LENGTH = 1024; + const MIN_ID_PREFIX_LENGTH = 10; + const DESIRED_ID_PREFIX_LENGTH = 16; + const ID_PREFIX_HASH_LENGTH = 6; + + public $pluginName = 'Custom Admin Theme'; + public $pluginSlug = 'custom-admin-theme'; + public $pluginVersion = '1.0'; + public $pluginUrl = ''; + public $authorName = ''; + public $authorUrl = ''; + public $requiredWpVersion = '4.7'; //Current required version for AME itself. + public $testedWpVersion = '6.2'; + public $shortDescription = ' '; + public $identifierPrefix = ''; + + /** + * @param string $jsonString + * @return self + */ + public static function parseJson($jsonString) { + $parsed = json_decode($jsonString, true); + if ( $parsed === null ) { + throw new \InvalidArgumentException('Invalid JSON string'); + } + if ( !is_array($parsed) ) { + throw new \InvalidArgumentException('JSON string does not represent an object'); + } + return self::parseArray($parsed); + } + + /** + * @param array $inputArray + * @return self + */ + public static function parseArray($inputArray) { + $result = new self(); + + //Tested WP version defaults to the current version. + $result->testedWpVersion = get_bloginfo('version'); + if ( !empty($result->testedWpVersion) ) { + //Keep only the major and minor version numbers. + $result->testedWpVersion = preg_replace('/^(\d+\.\d+).*$/i', '$1', $result->testedWpVersion); + } + + $parsers = [ + 'pluginName' => [__CLASS__, 'parsePluginName'], + 'pluginSlug' => 'sanitize_title', + 'requiredWpVersion' => [__CLASS__, 'parseVersionNumber'], + 'testedWpVersion' => [__CLASS__, 'parseVersionNumber'], + 'pluginVersion' => [__CLASS__, 'parseVersionNumber'], + 'authorName' => [__CLASS__, 'parseAuthorName'], + 'authorUrl' => 'esc_url_raw', + 'pluginUrl' => 'esc_url_raw', + 'shortDescription' => [__CLASS__, 'parseShortDescription'], + 'identifierPrefix' => [__CLASS__, 'parseIdentifierPrefix'], + ]; + + foreach ($parsers as $key => $parser) { + if ( isset($inputArray[$key]) ) { + if ( strlen($inputArray[$key]) > self::MAX_FIELD_LENGTH ) { + throw new \InvalidArgumentException(sprintf( + 'Field "%s" is too long (max %d characters)', + $key, + self::MAX_FIELD_LENGTH + )); + } + + $result->$key = is_callable($parser) + ? call_user_func($parser, $inputArray[$key]) + : $inputArray[$key]; + } + } + + //Plugin slug defaults to the plugin name. + if ( empty($result->pluginSlug) ) { + $result->pluginSlug = sanitize_title($result->pluginName); + } + + //Slug should not be longer than, say, 64 characters. + $result->pluginSlug = substr($result->pluginSlug, 0, 64); + + //Fallback for the prefix. + if ( empty($result->identifierPrefix) ) { + $result->identifierPrefix = self::generateIdentifierPrefix($result); + } else if ( strlen($result->identifierPrefix) < self::MIN_ID_PREFIX_LENGTH ) { + //Prefix should be long enough to be unique. + $result->identifierPrefix = ( + $result->identifierPrefix + . self::generateHashForPrefix( + $result, + self::MIN_ID_PREFIX_LENGTH - strlen($result->identifierPrefix) + ) + ); + } + + //Plugin version defaults to 1.0. + if ( empty($result->pluginVersion) ) { + $result->pluginVersion = '1.0'; + } + + return $result; + } + + public static function parseVersionNumber($input) { + if ( !is_string($input) || empty($input) ) { + return null; + } + if ( preg_match('/^(\d{1,3}+)((?:\.\d{1,5}+){0,2})/i', trim($input), $matches) ) { + $parts = array_slice($matches, 1, 3); + return implode('', $parts); + } else { + throw new \InvalidArgumentException('Invalid version number'); + } + } + + private static function parsePluginName($name) { + if ( !is_string($name) || empty($name) ) { + throw new \InvalidArgumentException('Invalid plugin name'); + } + + //Strip out any HTML tags. + $name = wp_strip_all_tags($name); + + //Limit the length to 100 characters. + return substr($name, 0, 100); + } + + private static function parseShortDescription($description) { + if ( !is_string($description) ) { + throw new \InvalidArgumentException('Invalid short description'); + } + + $description = sanitize_text_field($description); + + return substr($description, 0, 500); + } + + private static function parseAuthorName($name) { + if ( !is_string($name) ) { + throw new \InvalidArgumentException('Invalid author name'); + } + + $name = sanitize_text_field($name); + + return substr($name, 0, 100); + } + + private static function parseIdentifierPrefix($prefix) { + if ( !is_string($prefix) ) { + throw new \InvalidArgumentException('Invalid identifier prefix'); + } + + $prefix = self::sanitizeIdPrefix($prefix); + + return substr($prefix, 0, 20); + } + + protected static function generateIdentifierPrefix(AcAdminThemeMetadata $meta) { + $prefix = sanitize_title($meta->pluginName); + + $prefix = str_replace('-', ' ', $prefix); + $prefix = ucwords($prefix); + $prefix = self::sanitizeIdPrefix($prefix); + + //Prefix should always start with a letter. + if ( !preg_match('/^[a-z]/i', $prefix) ) { + $prefix = 'At' . $prefix; //At = Admin theme + } + + //Truncate the prefix so that there's space for a hash. + $prefix = substr($prefix, 0, max(self::DESIRED_ID_PREFIX_LENGTH - self::ID_PREFIX_HASH_LENGTH, 1)); + + //Add a hash to make the prefix more likely to be unique. + $prefix .= self::generateHashForPrefix( + $meta, + max(self::ID_PREFIX_HASH_LENGTH, self::DESIRED_ID_PREFIX_LENGTH - strlen($prefix)) + ); + return $prefix; + } + + protected static function generateHashForPrefix(AcAdminThemeMetadata $meta, $length) { + $hash = sha1($meta->pluginName . '|' . $meta->authorName . '|' . wp_rand()); + return substr($hash, 0, $length); + } + + private static function sanitizeIdPrefix($prefix) { + //Allow alphanumeric characters and underscores. No dashes since the prefix + //is also used in class names. + return preg_replace('/[^a-zA-Z0-9_]/', '', $prefix); + } + + /** @noinspection PhpLanguageLevelInspection */ + #[\ReturnTypeWillChange] + public function jsonSerialize() { + return [ + 'pluginName' => $this->pluginName, + 'pluginSlug' => $this->pluginSlug, + 'pluginVersion' => $this->pluginVersion, + 'pluginUrl' => $this->pluginUrl, + 'authorName' => $this->authorName, + 'authorUrl' => $this->authorUrl, + 'requiredWpVersion' => $this->requiredWpVersion, + 'testedWpVersion' => $this->testedWpVersion, + 'shortDescription' => $this->shortDescription, + 'identifierPrefix' => $this->identifierPrefix, + ]; + } +} + +class AcAdminColorSchemeData { + public $mainCss = ''; + public $adminBarCss = ''; + public $colorMeta = [ + 'demo' => [], + 'icons' => [], + ]; + + public function __construct($mainCss, $adminBarCss, $colorMeta) { + $this->mainCss = $mainCss; + $this->adminBarCss = $adminBarCss; + $this->colorMeta = $colorMeta; + } +} + +class AcAdminTheme { + /** + * @var AcAdminThemeMetadata + */ + public $meta; + + /** + * @var array + */ + protected $files = []; + + /** + * @var array + */ + public $settings = []; + + /** + * @param \YahnisElsts\AdminMenuEditor\AdminCustomizer\AcAdminThemeMetadata $metadata + * @param AbstractSetting[] $settings + */ + public function __construct(AcAdminThemeMetadata $metadata, $settings = []) { + $this->meta = $metadata; + foreach (AbstractSetting::recursivelyIterateSettings($settings, true) as $setting) { + $this->settings[$setting->getId()] = $setting->getValue(); + } + } + + public function toZipString() { + if ( !class_exists('ZipArchive') ) { + throw new \RuntimeException('ZipArchive class is not available on this server.'); + } + + $files = $this->files; + $files['admin-theme.php'] = $this->populateTemplate('admin-theme.php'); + $files['readme.txt'] = $this->populateTemplate('readme.txt'); + $files['settings.json'] = wp_json_encode($this->settings, JSON_PRETTY_PRINT); + $files['metadata.json'] = wp_json_encode($this->meta, JSON_PRETTY_PRINT); + + $tempFileName = get_temp_dir() . uniqid('ac-cat-') . '.zip'; + $directoryName = $this->meta->pluginSlug; + + $zip = new \ZipArchive(); + if ( $zip->open($tempFileName, \ZipArchive::CREATE) !== true ) { + throw new \RuntimeException('Failed to create temporary ZIP file.'); + } + $zip->addEmptyDir($directoryName); + foreach ($files as $fileName => $fileContents) { + $zip->addFromString($directoryName . '/' . $fileName, $fileContents); + } + $zip->close(); + + //phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown -- Local temp file. + $content = file_get_contents($tempFileName); + //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_unlink -- This is a temporary file. + unlink($tempFileName); + + return $content; + } + + public function getZipFileName() { + return $this->meta->pluginSlug . '.zip'; + } + + public function setMainStylesheet($css) { + $this->files['custom-admin-theme.css'] = $css; + } + + public function setColorScheme(AcAdminColorSchemeData $colorScheme) { + $this->files['color-scheme.css'] = $colorScheme->mainCss; + if ( !empty($colorScheme->adminBarCss) ) { + $this->files['admin-bar-colors.css'] = $colorScheme->adminBarCss; + } + $this->files['color-scheme.json'] = wp_json_encode($colorScheme->colorMeta, JSON_PRETTY_PRINT); + } + + protected function populateTemplate($relativeFileName) { + $template = file_get_contents(__DIR__ . '/admin-theme-template/' . $relativeFileName); + return $this->replacePlaceholders($template); + } + + protected function replacePlaceholders($content) { + $placeholders = [ + 'pluginName', + 'pluginUrl', + 'pluginSlug', + 'pluginVersion', + 'authorName', + 'authorUrl', + 'requiredWpVersion', + 'testedWpVersion', + 'shortDescription', + 'identifierPrefix', + 'optionalPluginHeaders', + ]; + + $values = []; + foreach ($placeholders as $placeholder) { + if ( isset($this->meta->$placeholder) ) { + $values[$placeholder] = $this->meta->$placeholder; + } + } + + $values['randomHash'] = substr(sha1(time() . '|' . wp_rand() . '|' . $this->meta->pluginVersion), 0, 8); + + if ( empty($values['optionalPluginHeaders']) ) { + $optionalHeaderNames = [ + 'Plugin URI' => 'pluginUrl', + 'Author' => 'authorName', + 'Author URI' => 'authorUrl', + ]; + $optionalHeaders = []; + foreach ($optionalHeaderNames as $headerName => $metaKey) { + if ( isset($this->meta->$metaKey) && !empty($this->meta->$metaKey) ) { + $optionalHeaders[] = ' * ' . $headerName . ': ' . $this->meta->$metaKey; + } + } + $values['optionalPluginHeaders'] = implode("\n", $optionalHeaders); + } + + //The syntax is `{placeholder}`. + foreach ($values as $placeholder => $value) { + $content = str_replace('{' . $placeholder . '}', $value, $content); + } + + //acIdentPrefix is a special case, it's not in curly braces. + return str_replace('acIdentPrefix', $this->meta->identifierPrefix, $content); + } +} \ No newline at end of file diff --git a/extras/modules/admin-customizer/admin-customizer.scss b/extras/modules/admin-customizer/admin-customizer.scss new file mode 100644 index 0000000..e9e8a4b --- /dev/null +++ b/extras/modules/admin-customizer/admin-customizer.scss @@ -0,0 +1,908 @@ +@use "sass:math"; +@import "../../../css/input-group"; +@import "../../../css/forms"; + +//region Variables +$navHoverColor: #2271b1; +$navHoverBackgroundColor: #f6f7f7; +$contentColor: #50575e; +$boxBorderColor: #dcdcde; +$boxBorder: 1px solid $boxBorderColor; + +$backButtonWidth: 48px; + +$labelFontSize: 14px; +$controlLeftPadding: 12px; +$controlRightPadding: 12px; +//endregion + +//region Overall structure +#ame-ac-admin-customizer { + display: flex; + flex-direction: row; + align-items: stretch; + + box-sizing: border-box; + width: 100%; + height: 100%; + max-height: 100%; +} + +#ame-ac-sidebar { + flex-basis: 18%; + min-width: 320px; + max-width: 600px; + flex-grow: 1; + + display: flex; + flex-direction: column; + + background: #f4f4f5; + border-right: $boxBorder; +} + +#ame-ac-container-collection { + position: relative; + width: 100%; +} + +#ame-ac-preview-container { + flex-basis: 82%; + flex-grow: 1; + background: #ddd; + position: relative; //Let the refresh spinner position itself relative to the container. +} + +#ame-ac-primary-actions { + min-height: 30px; + border-bottom: $boxBorder; + + display: flex; + flex-direction: row; + align-items: center; + padding-right: 15px; +} + +#ame-ac-sidebar-content { + //background: lightyellow; + flex-grow: 1; + + //Scroll contents vertically. + overflow-y: auto; + overflow-x: hidden; + + //Allow the "save/publish in progress" overlay to position itself correctly. + position: relative; +} + +#ame-ac-sidebar-blocker-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.4); + display: none; +} + +//endregion + +//region Action buttons +#ame-ac-exit-admin-customizer { + box-sizing: border-box; + + $buttonSize: 45px; + $topBorderWidth: 4px; + $hoverColor: #2271b1; + + display: block; + height: $buttonSize; + //Match at least the width of the "back" button in section navigation. + width: math.max($buttonSize, $backButtonWidth); + //Push everything else to the right. + margin-right: auto; + + border-right: 1px solid #dcdcde; + border-top: $topBorderWidth solid transparent; + color: #3c434a; + text-decoration: none; //No underline for the link. + + position: relative; + text-align: center; + + &:before { + font: normal 22px/#{$buttonSize} dashicons; + content: "\f335"; + position: relative; + top: (1px - $topBorderWidth); + } + + &:hover { + background: #fff; + color: $hoverColor; + border-top-color: $hoverColor; + cursor: pointer; + } +} + +#ame-ac-primary-actions { + .spinner { + margin-top: 0; + float: none; + } +} + +#ame-ac-save-button-wrapper { + #ame-ac-apply-changes { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + #ame-ac-extra-actions-trigger { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + + min-width: 30px; + padding-left: 0; + padding-right: 0; + } +} + +//endregion + +//region Info +#ame-ac-sidebar-info { + .notice { + margin: 0; + padding-top: 9px; + padding-left: $controlLeftPadding; + padding-bottom: 9px; + //Intentionally leave the padding-right property unchanged for the "dismiss" button. + } + + #ame-ac-global-notification-area { + border-bottom: $boxBorder; + overflow-x: hidden; + + &:empty { + display: none; + } + } +} + +//endregion + +//region Controls +.ame-ac-control-label { + display: block; + box-sizing: border-box; + + font-size: $labelFontSize; + line-height: 1.75; + font-weight: 600; + margin-bottom: 4px; +} + +.ame-ac-control { + display: block; + box-sizing: border-box; + width: 100%; + + margin-bottom: 12px; + padding-left: $controlLeftPadding; + padding-right: $controlRightPadding; + + .description { + color: $contentColor; + + display: block; + font-style: italic; + line-height: 1.5; + + margin-top: 0; + margin-bottom: 5px; + } + + p.description { + margin-top: 0.5em; + } +} + +.ame-ac-control-group .ame-ac-control { + padding-left: 0; + padding-right: 0; +} + +.ame-text-input-control { + width: 100%; +} + +.ame-ac-separator { + //Same margin as in the normal Customizer. + margin: 15px 0 0 0; + border: none; +} + +//endregion + +//region Validation errors + +#ame-ac-sidebar-content { + .ame-ac-validation-errors, ame-ac-validation-errors { + > ul { + margin: 0; + padding: 0; + } + + li { + list-style: none; + } + } + + .ame-ac-validation-error { + margin: 0 0 6px 0; + padding: 9px $controlLeftPadding; + } +} + +.ame-ac-control { + .ame-ac-has-error { + outline: 2px solid #d63638; + } +} + +//endregion + +//region Sections +.ame-ac-section { + list-style: none; + box-sizing: border-box; + margin: 0; + width: 100%; +} + +.ame-ac-section-link { + display: block; + box-sizing: border-box; + width: 100%; + margin: 0; + + cursor: pointer; + + //Emulate appearance of the the Theme Customizer. + .ame-ac-section-title { + display: block; + position: relative; + padding: 10px 10px 11px 14px; + margin: 0; + + color: $contentColor; + background-color: white; + border-bottom: $boxBorder; + border-left: 4px solid #fff; + + line-height: 1.55; + font-size: $labelFontSize; + + transition: .14s color ease-in-out, .14s background-color ease-in-out, .14s border-color ease-in-out; + + //Add a ">" indicator to the title. + &:after { + font: normal 20px/1 dashicons; + display: block; + content: "\f345"; + color: #a7aaad; + + position: absolute; + right: 10px; + top: calc(50% - 10px); + } + + &:hover { + color: $navHoverColor; + background: $navHoverBackgroundColor; + border-left-color: $navHoverColor; + + &:after { + color: $navHoverColor; + } + } + } +} + +//Top border for the first section link and for section links that are preceded +//by something that's not a section link (e.g. other controls). +.ame-ac-section .ame-ac-section-meta + .ame-ac-section-link, +li:not(.ame-ac-section-link) + .ame-ac-section-link { + border-top: $boxBorder; +} + +.ame-ac-section-meta { + display: block; + box-sizing: border-box; + width: 100%; + + background: white; +} + +.ame-ac-section-header { + $headerFontSize: 20px; + $headerLineHeight: 27px; + + display: flex; + border-bottom: $boxBorder; + margin-bottom: 15px; + + color: $contentColor; + + .ame-ac-section-title { + flex-grow: 1; + padding-left: 14px; + padding-right: 10px; + + font-size: 20px; + font-weight: 200; + line-height: 27px; + + //Note: 23 + 24 to match the height if the breadcrumbs are hidden. + margin-top: (23px / $headerFontSize) * 1em; + margin-bottom: (24px / $headerFontSize) * 1em; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .ame-ac-section-breadcrumbs { + display: block; + + font-size: 13px; + line-height: $headerLineHeight - 1px; + font-weight: 400; + color: #50575e; + + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + //Reduce margins when there are breadcrumbs and avoid making the header too tall. + //These values were chosen to visually match the Theme Customizer. + .ame-ac-section-title.ame-ac-has-breadcrumbs { + margin-top: (9px / $headerFontSize) * 1em; + margin-bottom: (11px / $headerFontSize) * 1em; + } +} + +.ame-ac-section-back-button { + display: block; + width: $backButtonWidth; + flex-shrink: 0; + + margin: 0; + padding: 0 3px 0 0; + + background: #fff; + color: $contentColor; + + border: none; + border-right: $boxBorder; + border-left: 4px solid #fff; + box-shadow: none; + + cursor: pointer; + text-align: center; + + &:before { + display: block; + font: normal 20px/1 dashicons; + content: "\f341"; + margin-right: 1px; //To line up with the close button. + } + + &:hover { + color: $navHoverColor; + border-left-color: $navHoverColor; + background: $navHoverBackgroundColor; + } +} + +//No back button for the root section. +#ame-ac-section-structure-root .ame-ac-section-back-button { + display: none; +} + +//Slide sections left or right when the user navigates to/away from them. +.ame-ac-section { + //By default, all sections are hidden on the right side. + transform: translateX(100%); + visibility: hidden; + height: 0; + overflow: hidden; + + position: absolute; + top: 0; + left: 0; + + transition-property: transform, visibility; + transition-duration: 0.182s; + transition-timing-function: cubic-bezier(0.65, 0.05, 0.36, 1); + + &.ame-ac-transitioning { + visibility: visible; + height: auto; + overflow: auto; + } +} + +.ame-ac-current-section { + //The current section is not translated, so it's visible. + transform: none; + visibility: visible; + height: auto; + overflow: auto; +} + +.ame-ac-previous-section { + transform: translateX(-100%); +} + +//Disable section transitions for users who prefer reduced motion. +@media (prefers-reduced-motion: reduce) { + .ame-ac-section { + transition: none; + } +} + +//region Content sections +$contentSectionBottomPadding: 5px; + +//These sections serve as headings for groups of controls. +.ame-ac-content-section { + background: #fff; + border-top: 1px solid $boxBorderColor; + border-bottom: 1px solid $boxBorderColor; + + padding-top: 4px; + padding-bottom: $contentSectionBottomPadding; +} + +.ame-ac-content-section-title { + margin: 0; + font-size: $labelFontSize + 1px; +} + +//Increase the top margin of the heading item unless it's the first thing in the parent section. +.ame-ac-control + .ame-ac-content-section { + margin-top: 18px; +} + +//Slightly separate a content section from a preceding section link. +.ame-ac-section-link + .ame-ac-content-section { + margin-top: 0.5em; +} + +//Nested content sections are less prominent. +.ame-ac-content-section-2 { + border-top-style: none; + background-color: transparent; + + $borderOffset: math.min(8px, $controlLeftPadding); + margin-left: $borderOffset; + padding-left: math.max($controlLeftPadding - $borderOffset, 0); + width: calc(100% - #{$borderOffset}); + + padding-top: 0; + padding-bottom: 0; + + .ame-ac-content-section-title { + //font-size: $labelFontSize; + } +} + +//endregion + +//endregion + +//region Preview +#ame-ac-preview { + display: block; + box-sizing: border-box; + width: 100%; + height: 100%; + + margin: 0; + padding: 0; + border: none; +} + +#ame-ac-preview-refresh-indicator { + display: block; + position: absolute; + //Position the spinner in the center of the preview. + top: 0; + left: 0; + bottom: 0; + right: 0; + + z-index: 2; + background: rgba(255, 255, 255, 0.5); + + $fadeTime: 250ms; + visibility: hidden; + opacity: 0; + //Fade the overlay in and out. When fading out, delay the visibility change + //until the fade is complete. + transition: opacity $fadeTime ease-in, visibility 0ms ease-in $fadeTime; + + &.ame-ac-show-indicator { + visibility: visible; + opacity: 1; + + //When fading in, change visibility immediately instead of waiting for + //the delay. Does not affect opacity because it already had no delay. + transition-delay: 0ms; + } + + #ame-ac-refresh-spinner { + display: none; //Not used. + /*box-sizing: border-box; + width: 10em; + height: 10em; + border-radius: 50%; + + $backColor: rgba(100, 100, 100, 0.4); + border: 1.1em solid $backColor; + border-left: 1.1em solid #ffffff; + + animation: ame-ac-basic-spin 1.1s infinite linear;*/ + } +} + +@keyframes ame-ac-basic-spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +//endregion + +//region jQuery UI menu + +.ui-menu { + $primary: #ffffff; + $secondary: #444a4a; + $highlight: #0073aa; + $light-grey: #e5e5e5; + $medium-grey: #8c8f94; + $dark-grey: #32373c; + $white: #ffffff; + $black: #000000; + $delete: #dc3232; + + background-color: $primary; + border-radius: 5px; + border: 1px solid $secondary; + box-shadow: 0 2px 5px rgba($black, 0.1); + z-index: 9999; + + .ui-menu-item { + color: $dark-grey; + padding: 8px 16px; + margin: 0; + + cursor: pointer; + font-size: 14px; + line-height: 1.4; + white-space: nowrap; + + &:hover, + &.ui-state-focus { + background-color: $highlight; + color: $white; + outline: none; + } + + //Disable color transitions for Dashicons inside menu items. The gradual transition + //looks weird when the rest of the menu item changes color instantly. + .dashicons { + transition: none; + } + } + + .ui-menu-divider { + border-top: 1px solid $light-grey; + margin: 5px 0; + } + + .ui-menu-item.ui-state-disabled { + color: $medium-grey; + cursor: default; + + &:hover, + &.ui-state-focus { + background-color: transparent; + } + } + + .ui-menu-icon { + float: right; + margin-top: 2px; + } + + .ui-menu .ui-menu { + position: absolute; + top: 0; + left: 100%; + border: none; + margin-top: -2px; + margin-left: -1px; + } + + //Delete/discard menu items. + .ui-menu-item-delete, .ame-ac-menu-item-delete { + color: $delete; + + &:hover, + &.ui-state-focus { + background-color: lighten($delete, 15%); + color: $white; + } + + &.ui-state-disabled { + &:hover, + &.ui-state-focus { + color: $medium-grey; + background-color: transparent; + } + } + } +} + +#ame-ac-extra-actions-menu { + //This is a dropdown menu, so it should be absolutely positioned. + position: absolute; +} + +//endregion + +//region jQuery UI dialog + +$dialogContentVerticalPadding: 8px; +$dialogContentHorizontalPadding: 8px; + +.ui-dialog { + $borderRadius: 3px; + $closeButtonSize: 36px; + $baseFontSize: 14px; + + background-color: white; + border: 1px solid #c0c0c0; + border-radius: $borderRadius; + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.25); + + .ui-dialog-titlebar { + background-color: #fcfcfc; + border-radius: $borderRadius $borderRadius 0 0; + border-bottom: 1px solid #dfdfdf; + + padding: 0 $closeButtonSize 0 8px; + } + + .ui-dialog-title { + font-size: 18px; + font-weight: 600; + line-height: 2; + margin: 0; + } + + .ui-dialog-titlebar-close { + background-color: transparent; + border: none; + + color: #666; + cursor: pointer; + + padding: 0; + margin: 0; + position: absolute; + top: 0; + right: 0; + width: $closeButtonSize; + height: $closeButtonSize; + text-align: center; + + &:hover { + //background-color: #dcdcde; + color: #000; + } + + &:before { + font: normal 20px/#{$closeButtonSize} "dashicons"; + content: "\f158"; + vertical-align: middle; + } + } + + .ui-dialog-content { + font-size: $baseFontSize; + line-height: 20px/$baseFontSize; + padding: $dialogContentVerticalPadding $dialogContentHorizontalPadding; + } + + .ui-dialog-buttonpane { + background-color: #fcfcfc; + border-top: 1px solid #dcdcde; + padding: 8px; + text-align: right; + + //Move the confirmation button to the left. + .button-primary { + float: left; + } + } + + .ui-dialog-buttonset { + + } +} + +.ui-widget-overlay { + background-color: rgba(0, 0, 0, 0.7); + position: fixed; + left: 0; + top: 0; + right: 0; + bottom: 0; + + width: 100%; + height: 100%; +} + +.ui-front { + z-index: 10000; +} + +.ame-ac-dialog-label { + display: block; + font-size: 14px; + font-weight: 600; + margin-bottom: 3px; +} + +.ame-ac-dialog-row { + margin-bottom: (10px/14px) * 1em; + + input, select, textarea { + width: 100%; + } +} + +.ame-ac-more-toggle { + text-decoration: none; + margin-bottom: 8px; + line-height: 20px; + + &:before { + font: normal 20px/1 "dashicons"; + content: "\f140"; + vertical-align: top; + + //The icon is not aligned with its own left edge, so we need to move it a bit + //to the left to visually align it with the form fields. + margin-left: -3px; + } + + &.ame-ac-more-toggle-active:before { + content: "\f142"; + } +} + +.ame-ac-dialog-help { + margin: #{-$dialogContentVerticalPadding} #{-$dialogContentHorizontalPadding} $dialogContentVerticalPadding; + padding: 3px $dialogContentHorizontalPadding; + + //Light grey + background-color: #f5f5f5; + border-bottom: 1px solid #dcdcdc; + + p:first-of-type { + margin-top: 0; + } + + ul { + margin-top: 0.5em; + + li { + list-style: disc inside; + margin-left: 0.5em; + } + } + + .ame-ac-more-toggle.ame-ac-more-toggle-inactive:before { + content: "\f348 "; + } +} + +.ame-ac-dialog { + @include ame-invalid-input-styles; +} + +//endregion + +//region Download theme dialog +#ame-ac-download-theme-dialog { + position: relative; +} + +#ame-ac-download-progress-overlay { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + z-index: 2; + cursor: wait; +} +//endregion + +//region Import theme feature +#ame-ac-latest-import-report { + table { + th { + text-align: left; + } + td { + text-align: right; + padding-left: 1em; + } + } +} + +//endregion + +//region Misc +.ame-ac-visually-hidden { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; +} + +.ame-ac-general-progress-spinner { + $borderWidth: 7px; + + width: 50px; + height: 50px; + background-color: rgba(255, 255, 255, 0.8); + border: $borderWidth solid #f3f3f3; + border-top: $borderWidth solid #3498db; + border-radius: 50%; + + animation: ac-progress-spin 2s linear infinite; +} + +@keyframes ac-progress-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.ame-ac-spinner-container { + width: 100%; + height: 100%; + + /* Flexbox properties for centering the spinner */ + display: flex; + justify-content: center; + align-items: center; +} +//endregion \ No newline at end of file diff --git a/extras/modules/admin-customizer/admin-customizer.ts b/extras/modules/admin-customizer/admin-customizer.ts new file mode 100644 index 0000000..a941791 --- /dev/null +++ b/extras/modules/admin-customizer/admin-customizer.ts @@ -0,0 +1,1961 @@ +'use strict'; + +/// +/// +/// + +import {AmeCustomizable, AmeCustomizableViewModel} from '../../pro-customizables/assets/customizable.js'; +import {registerBaseComponents} from '../../pro-customizables/ko-components/ame-components.js'; +import AmeAcStructure from './ko-components/ame-ac-structure.js'; +import AmeAcSection from './ko-components/ame-ac-section.js'; +import AmeAcSectionLink from './ko-components/ame-ac-section-link.js'; +import AmeAcControl from './ko-components/ame-ac-control.js'; +import AmeAcControlGroup from './ko-components/ame-ac-control-group.js'; +import AmeAcContentSection from './ko-components/ame-ac-content-section.js'; +import {AmeAdminCustomizerBase} from './admin-customizer-base.js'; +import AmeAcSeparator from './ko-components/ame-ac-separator.js'; +import AmeAcValidationErrors from './ko-components/ame-ac-validation-errors.js'; +import z, {ZodError, ZodType} from '../../zod/lib/index.js'; + +declare var wsAmeLodash: _.LoDashStatic; +declare const wsAmeAdminCustomizerData: AmeAdminCustomizer.ScriptData; + +export namespace AmeAdminCustomizer { + import Setting = AmeCustomizable.Setting; + import SettingCollection = AmeCustomizable.SettingCollection; + import InterfaceStructureData = AmeCustomizable.InterfaceStructureData; + import InterfaceStructure = AmeCustomizable.InterfaceStructure; + import unserializeUiElement = AmeCustomizable.unserializeUiElement; + import unserializeSetting = AmeCustomizable.unserializeSetting; + import AnySpecificElementData = AmeCustomizable.AnySpecificElementData; + import CustomizableVmInterface = AmeCustomizableViewModel.CustomizableVmInterface; + + const $ = jQuery; + const _ = wsAmeLodash; + + registerBaseComponents(); + ko.components.register('ame-ac-structure', AmeAcStructure); + ko.components.register('ame-ac-section', AmeAcSection); + ko.components.register('ame-ac-section-link', AmeAcSectionLink); + ko.components.register('ame-ac-content-section', AmeAcContentSection); + ko.components.register('ame-ac-control-group', AmeAcControlGroup); + ko.components.register('ame-ac-control', AmeAcControl); + ko.components.register('ame-ac-separator', AmeAcSeparator); + ko.components.register('ame-ac-validation-errors', AmeAcValidationErrors); + + export interface ScriptData extends AmeAdminCustomizerBase.ScriptData { + ajaxUrl: string; + saveChangesetNonce: string; + trashChangesetNonce: string; + changesetItemCount: number; + changesetStatus: string; + refreshPreviewNonce: string; + initialPreviewUrl: string; + interfaceStructure: InterfaceStructureData; + } + + const reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); + let prefersReducedMotion = reducedMotionQuery && reducedMotionQuery.matches; + reducedMotionQuery.addEventListener('change', () => { + prefersReducedMotion = reducedMotionQuery.matches; + }); + + class CustomizerSettingsCollection extends SettingCollection { + /** + * Settings that have changed since the last save attempt. + */ + private pendingSettings: Record = {}; + /** + * Settings that in the process of being sent to the server to be saved. + * They might not be saved yet. + */ + private sentSettings: Record = {}; + private currentChangesetRequest: JQueryXHR | null = null; + private saveTriggerTimeoutId: null | ReturnType = null; + + private readonly currentChangeset: KnockoutObservable; + + public readonly changesetName: KnockoutComputed; + + public readonly isExclusiveOperationInProgress: KnockoutComputed; + private readonly exclusiveOperation: KnockoutObservable = ko.observable(false); + + constructor( + public readonly ajaxUrl: string, + public readonly saveChangesetNonce: string, + public readonly trashChangesetNonce: string, + changesetName: string, + changesetItemCount: number = 0, + changesetStatus: string | null = null + ) { + super(); + const self = this; + + this.currentChangeset = ko.observable( + new Changeset(changesetName, changesetItemCount, changesetStatus) + ); + this.changesetName = ko.pureComputed(() => { + return (self.currentChangeset()?.name()) || ''; + }); + + //Automatically save the changeset when any settings change. + const totalChangeCount = ko.pureComputed(() => { + const changeset = self.currentChangeset(); + return (changeset ? changeset.currentSessionChanges() : 0); + }); + totalChangeCount.subscribe(_.debounce( + (counter) => { + if (counter > 0) { + self.queueChangesetUpdate() + } + }, + 3000, + {leading: true, trailing: true} + )); + + this.isExclusiveOperationInProgress = ko.pureComputed(() => { + return self.exclusiveOperation(); + }); + + //Keep track of unsaved changes and changesets. + this.addChangeListener((setting: Setting) => { + this.pendingSettings[setting.id] = setting; + + let changeset = this.currentChangeset(); + //If the current changeset cannot be modified, create a new one + //for the changed setting(s). + if (!changeset?.canBeModified()) { + changeset = new Changeset(); + this.currentChangeset(changeset); + } + //Track the number of changes in the current session. + changeset.currentSessionChanges(changeset.currentSessionChanges() + 1); + }); + } + + queueChangesetUpdate(delay: number = 0) { + if (delay > 0) { + if (this.saveTriggerTimeoutId !== null) { + //Replace the existing timeout with a new one. + clearTimeout(this.saveTriggerTimeoutId); + } + this.saveTriggerTimeoutId = setTimeout(() => { + this.saveTriggerTimeoutId = null; + this.queueChangesetUpdate(0); + }, delay); + return; + } + + if (this.saveTriggerTimeoutId !== null) { + return; //Another timeout is already waiting. + } + + if (this.currentChangesetRequest !== null) { + //There's an in-progress request, so wait until it's done. + this.currentChangesetRequest.always(() => { + //Wait a bit to avoid hammering the server. + this.queueChangesetUpdate(1000); + }); + return; + } + + this.saveChangeset(); + } + + private saveChangeset(status: string | null = null): JQueryPromise { + //Do nothing if there are no changes. + if (_.isEmpty(this.pendingSettings) && (status === null)) { + return $.Deferred().reject(new Error('There are no changes to save.')).promise(); + } + + if (this.isExclusiveOperationInProgress()) { + return $.Deferred().reject( + new Error('Another exclusive changeset operation is in progress.') + ).promise(); + } + + let isExclusiveRequest = (status === 'publish') || (status === 'trash'); + if (isExclusiveRequest) { + this.exclusiveOperation(true); + } + + const savedChangeset = this.currentChangeset(); + + //Keep a local copy of the settings in case something changes instance + //properties while the request is in progress (should never happen). + const settingsToSend = this.pendingSettings; + this.sentSettings = settingsToSend; + this.pendingSettings = {}; + + const modifiedSettings = _.mapValues(settingsToSend, setting => setting.value()); + const requestData: Record = { + action: 'ws_ame_ac_save_changeset', + _ajax_nonce: this.saveChangesetNonce, + changeset: savedChangeset?.name ?? '', + modified: JSON.stringify(modifiedSettings), + }; + if (status !== null) { + requestData['status'] = status; + } + //If the changeset doesn't have a name, it is new. + if (!savedChangeset?.hasName()) { + requestData['createNew'] = 1; + } + + const request = $.ajax({ + url: this.ajaxUrl, + method: 'POST', + data: requestData, + dataType: 'json', + timeout: 20000, + }); + this.currentChangesetRequest = request; + + interface ServerValidationResults { + [settingId: string]: { + isValid: boolean; + errors: Array<{ code: string; message: string; }>; + } + } + + const self = this; + + function storeValidationResultsFrom(serverResponse: any) { + const results: ServerValidationResults = _.get( + serverResponse, + ['data', 'validationResults'] + ); + if (typeof results !== 'object') { + return; + } + + for (const settingId in results) { + const setting = self.get(settingId); + if (!setting.isDefined()) { + continue; + } + + if (!modifiedSettings.hasOwnProperty(settingId)) { + continue; + } + const sentValue = modifiedSettings[settingId]; + + const state = results[settingId]; + if (state.isValid) { + setting.get().clearValidationErrorsForValue(sentValue); + } else { + //Since the server response is not fully validated, some typeof checks + //are still useful. + // noinspection SuspiciousTypeOfGuard + setting.get().addValidationErrorsForValue( + sentValue, + _.filter(state.errors, error => (typeof error.message === 'string')) + ); + } + } + } + + function storeChangesetDetailsFrom(serverResponse: any) { + if (!savedChangeset) { + return; + } + + //Store the returned changeset name in case a new changeset was created. + if (!savedChangeset.hasName()) { + const newName = _.get(serverResponse, ['data', 'changeset']); + if (typeof newName === 'string') { + savedChangeset.name(newName); + } + } + //Store the changeset status. + const newStatus = _.get(serverResponse, ['data', 'changesetStatus']); + if (typeof newStatus === 'string') { + savedChangeset.status(newStatus); + } + + //Store the number of changes in the changeset. + const newChangeCount = _.get(serverResponse, ['data', 'changesetItemCount']); + if (typeof newChangeCount === 'number') { + savedChangeset.knownItemCount(newChangeCount); + } + + //Was the changeset published? Because changesets are typically moved + //to trash after publishing, "status" might be "trash" instead of "publish", + //but we still want to know if it was successfully published. + const wasPublished = _.get(serverResponse, ['data', 'changesetWasPublished'], null); + if (wasPublished) { + savedChangeset.wasPublished(wasPublished); + } + } + + request.done(function (response) { + storeChangesetDetailsFrom(response); + storeValidationResultsFrom(response); + + //After successfully publishing a changeset, it has no more + //unsaved changes. + const isPublished = + (savedChangeset.status() === 'publish') + || (savedChangeset.status() === 'future') + || (savedChangeset.wasPublished()); + if (isPublished) { + savedChangeset.currentSessionChanges(0); + } + + //After a changeset is published or trashed, it can no longer + //be edited. We may be able to replace it with a new changeset + //that was created on the server. + if (!self.currentChangeset().canBeModified()) { + const nextChangeset = _.get(response, ['data', 'nextChangeset']); + if ((typeof nextChangeset === 'string') && (nextChangeset !== '')) { + self.currentChangeset(new Changeset(nextChangeset)); + } + } + }); + + request.fail((requestObject: JQueryXHR) => { + if (typeof requestObject.responseJSON === 'object') { + storeValidationResultsFrom(requestObject.responseJSON); + storeChangesetDetailsFrom(requestObject.responseJSON); + } + + //Add the unsaved settings back to the pending list. + for (const id in settingsToSend) { + //Keep only settings that still exist. + if (this.get(id).isDefined()) { + this.pendingSettings[id] = settingsToSend[id]; + } + } + + //We don't automatically retry because the problem might be something + //that doesn't get better on its own, like missing permissions. + }); + + request.always(() => { + this.currentChangesetRequest = null; + this.sentSettings = {}; + if (isExclusiveRequest) { + this.exclusiveOperation(false); + } + }); + + return request; + } + + public savePendingSettings(timeout: number = 20): JQueryPromise { + if (this.isExclusiveOperationInProgress()) { + //Wait for the exclusive operation to finish. + const deferred = $.Deferred(); + const result = deferred.then(() => this.doSavePendingSettings()); + + const startTime = Date.now(); + const timer = setInterval(() => { + if (!this.isExclusiveOperationInProgress()) { + clearInterval(timer); + deferred.resolve(); + } else if ((Date.now() - startTime) > timeout) { + clearInterval(timer); + deferred.reject(new Error('Exclusive operation timed out.')); + } + }, 200); + + return result; + } + + return this.doSavePendingSettings(); + } + + private doSavePendingSettings(): JQueryPromise { + //If there are no changes, we don't need to do anything. + if (_.isEmpty(this.pendingSettings)) { + return $.Deferred().resolve().promise(); + } + return this.saveChangeset(); + } + + public getCurrentChangeset(): Changeset { + return this.currentChangeset(); + } + + /** + * Get any unsaved setting changes. + * + * @returns An object mapping setting IDs to their modified values. + */ + public get unsavedChanges(): Record { + //Include both pending settings and sent settings. Sent settings + //might not be saved yet. + let unsavedSettings: Record = {}; + _.defaults(unsavedSettings, this.pendingSettings, this.sentSettings); + + return _.mapValues(unsavedSettings, setting => setting.value()); + } + + public publishChangeset(): JQueryPromise { + if (this.isExclusiveOperationInProgress()) { + return $.Deferred() + .reject(new Error('Another exclusive changeset operation is already in progress.')) + .promise(); + } + return this.saveChangeset('publish'); + } + + public trashChangeset(): JQueryPromise { + if (this.isExclusiveOperationInProgress()) { + return $.Deferred() + .reject(new Error('Another exclusive changeset operation is already in progress.')) + .promise(); + } + + const changeset = this.currentChangeset(); + if (!changeset.hasName()) { + //The changeset hasn't been saved yet, so we can just mark it as trashed. + changeset.status('trash'); + changeset.currentSessionChanges(0); + + //It's a success of sorts. + return $.Deferred().resolve(true).promise(); + } + + this.exclusiveOperation(true); + + const requestData: Record = { + action: 'ws_ame_ac_trash_changeset', + _ajax_nonce: this.trashChangesetNonce, + changeset: changeset.name + }; + + const request = $.ajax({ + url: this.ajaxUrl, + method: 'POST', + data: requestData, + dataType: 'json', + timeout: 20000, + }); + this.currentChangesetRequest = request; + + request.done(function () { + changeset.status('trash'); + changeset.currentSessionChanges(0); + }); + + //Unfortunately, jQuery doesn't seem to allow us to create a custom + //error object and pass it to other handlers, so code that uses this + //method will have to parse the error response itself. + + request.always(() => { + this.currentChangesetRequest = null; + this.exclusiveOperation(false); + }); + + return request; + } + } + + class Changeset { + public readonly name: KnockoutObservable; + public readonly knownItemCount: KnockoutObservable; + public readonly status: KnockoutObservable; + + /** + * The number of times settings have been changed in this changeset + * during the current customizer session. + * + * Note that this is not the same as the number settings in the changeset: + * if the same setting is changed X times, this counter will increase by X, + * but the changeset will still only have one entry for that setting. + */ + public readonly currentSessionChanges: KnockoutObservable = ko.observable(0); + + /** + * Once a changeset has been published or deleted, its contents can't be modified any more. + * @private + */ + private readonly fixedContentStatuses: Record = + {'publish': true, 'trash': true, 'future': true}; + + public readonly wasPublished: KnockoutObservable = ko.observable(false); + + constructor(name: string = '', knownItemCount: number = 0, initialStatus: string | null = '') { + this.name = ko.observable(name); + this.knownItemCount = ko.observable(knownItemCount); + this.status = ko.observable(initialStatus ?? ''); + } + + public hasName(): boolean { + const name = this.name(); + return (name !== ''); + } + + public canBeModified(): boolean { + return !this.fixedContentStatuses.hasOwnProperty(this.status()); + } + + public isNonEmpty(): boolean { + return (this.currentSessionChanges() > 0) || (this.knownItemCount() > 0) + } + } + + //region Admin theme + const UrlOrEmpty = z.union([ + z.string().url().max(1000), + z.literal('') + ]); + + const AdminThemeMetadata = z.object({ + pluginName: z.string().max(100), + shortDescription: z.string().max(500), + + pluginSlug: z.string().max(64).toLowerCase().default('') + .refine( + function (input: string) { + //Only allow alphanumeric characters, underscores, and dashes. + //Empty string is allowed. + return /^[a-z0-9_-]*$/.test(input); + }, + {message: 'The slug can only contain letters (a-z), numbers, underscores, and dashes.'} + ), + identifierPrefix: z.string().max(20).optional(), + + pluginVersion: z.string().default('1.0').optional(), + pluginUrl: UrlOrEmpty.optional(), + authorName: z.string().max(100).optional(), + authorUrl: UrlOrEmpty.optional(), + requiredWpVersion: z.string().max(30).default('4.7').optional(), + testedWpVersion: z.string().max(30).optional(), + }); + + type AdminThemeMetadata = z.infer; + + const AdminThemeSettings = z.record( + //Key type + z.string().min(1), + //Value type + z.any() + ); + + class AdminThemeImportReport { + public totalSettings: number = 0; + public importedSettings: number = 0; + public invalidSettings: number = 0; + public skippedSettings: number = 0; + public differentImportedSettings: number = 0; + + public readonly pluginName: string; + + constructor( + public readonly fileName: string, + public readonly metadata: AdminThemeMetadata + ) { + this.pluginName = metadata.pluginName || '(Unnamed)'; + } + } + + interface WithZodValidationResults extends ObservableValidationFields { + ameZodValidationError: KnockoutObservable; + } + + type ZodValidatedObservable = KnockoutComputed & WithZodValidationResults; + + function observableWithZodValidation>( + value: z.output, + schema: S + ): ZodValidatedObservable> { + const underlyingObservable = ko.observable(value); + + const observable: ZodValidatedObservable = ko.pureComputed({ + read: underlyingObservable, + write: (newValue: T) => { + const validationResult = schema.safeParse(newValue); + if (validationResult.success) { + underlyingObservable(validationResult.data); + observable.ameZodValidationError(null); + observable.ameValidationErrors([]); + } else { + observable.ameZodValidationError(validationResult.error); + //Convert Zod issues to ObservableValidationErrors. + observable.ameValidationErrors(validationResult.error.issues.map(issue => { + return { + code: issue.code, + message: issue.message + } satisfies ObservableValidationError; + })); + } + } + }) as ZodValidatedObservable; + + observable.ameZodValidationError = ko.observable(null); + observable.ameValidationErrors = ko.observable([] as ObservableValidationError[]); + observable.ameIsValid = ko.pureComputed(() => { + const errors = observable.ameValidationErrors(); + return !errors || errors.length === 0; + }); + + return observable; + } + + class ObservableThemeMetadata { + public readonly pluginName: ZodValidatedObservable; + public readonly shortDescription: ZodValidatedObservable; + public readonly pluginSlug: ZodValidatedObservable; + public readonly identifierPrefix: ZodValidatedObservable; + public readonly pluginVersion: ZodValidatedObservable; + public readonly pluginUrl: ZodValidatedObservable; + public readonly authorName: ZodValidatedObservable; + public readonly authorUrl: ZodValidatedObservable; + public readonly requiredWpVersion: ZodValidatedObservable; + public readonly testedWpVersion: ZodValidatedObservable; + + constructor(metadata: AdminThemeMetadata) { + this.pluginName = observableWithZodValidation( + metadata.pluginName, + AdminThemeMetadata.shape.pluginName + ); + this.shortDescription = observableWithZodValidation( + metadata.shortDescription, + AdminThemeMetadata.shape.shortDescription + ); + + this.pluginSlug = observableWithZodValidation( + metadata.pluginSlug ?? '', + AdminThemeMetadata.shape.pluginSlug + ); + this.identifierPrefix = observableWithZodValidation( + metadata.identifierPrefix ?? '', + AdminThemeMetadata.shape.identifierPrefix + ); + + this.pluginVersion = observableWithZodValidation( + metadata.pluginVersion ?? '', + AdminThemeMetadata.shape.pluginVersion + ); + this.pluginUrl = observableWithZodValidation( + metadata.pluginUrl ?? '', + AdminThemeMetadata.shape.pluginUrl + ); + this.authorName = observableWithZodValidation( + metadata.authorName ?? '', + AdminThemeMetadata.shape.authorName + ); + this.authorUrl = observableWithZodValidation( + metadata.authorUrl ?? '', + AdminThemeMetadata.shape.authorUrl + ); + this.requiredWpVersion = observableWithZodValidation( + metadata.requiredWpVersion ?? '', + AdminThemeMetadata.shape.requiredWpVersion + ); + this.testedWpVersion = observableWithZodValidation( + metadata.testedWpVersion ?? '', + AdminThemeMetadata.shape.testedWpVersion + ); + } + + public toObject(): AdminThemeMetadata { + return { + pluginName: this.pluginName(), + shortDescription: this.shortDescription(), + pluginSlug: this.pluginSlug(), + identifierPrefix: this.identifierPrefix(), + pluginVersion: this.pluginVersion(), + pluginUrl: this.pluginUrl(), + authorName: this.authorName(), + authorUrl: this.authorUrl(), + requiredWpVersion: this.requiredWpVersion(), + testedWpVersion: this.testedWpVersion(), + }; + } + + isValid(): boolean { + //This seems really inelegant, but I can't think of a better way to do it. + return this.pluginName.ameIsValid() + && this.shortDescription.ameIsValid() + && this.pluginSlug.ameIsValid() + && this.identifierPrefix.ameIsValid() + && this.pluginVersion.ameIsValid() + && this.pluginUrl.ameIsValid() + && this.authorName.ameIsValid() + && this.authorUrl.ameIsValid() + && this.requiredWpVersion.ameIsValid() + && this.testedWpVersion.ameIsValid(); + } + } + + class DownloadThemeDialog extends AmeBaseKnockoutDialog { + public readonly meta: KnockoutObservable; + public readonly areFieldsEditable: KnockoutComputed; + public readonly isOperationInProgress: KnockoutObservable = ko.observable(false); + + autoCancelButton: boolean = true; + isConfirmButtonEnabled: KnockoutObservable; + + advancedOptionsVisible: KnockoutObservable = ko.observable(false); + advancedOptionsToggleLabel: KnockoutComputed; + + helpVisible: KnockoutObservable = ko.observable(false); + helpToggleLabel: KnockoutComputed; + + changesetName: KnockoutObservable = ko.observable(''); + metadataJson: KnockoutObservable = ko.observable(''); + downloadCookieName: KnockoutObservable = ko.observable(''); + + private cleanupCurrentDownload: () => void = () => { + }; + + constructor( + private readonly getChangesetName: () => string, + private readonly savePendingChangesetData: () => JQueryPromise, + ) { + super(); + this.options.minWidth = 400; + + this.meta = ko.observable(new ObservableThemeMetadata(AdminThemeMetadata.parse({ + pluginName: 'Custom Admin Theme', + shortDescription: 'A custom admin theme generated using the Admin Menu Editor Pro plugin.', + pluginVersion: '1.0', + }))); + + this.isConfirmButtonEnabled = ko.computed(() => { + if (this.isOperationInProgress()) { + return false; + } + + if (getChangesetName() === '') { + //To generate an admin theme, the changeset must have already been saved. + return false; + } + return this.meta().isValid(); + }); + + this.areFieldsEditable = ko.computed(() => { + return !this.isOperationInProgress(); + }); + + this.advancedOptionsToggleLabel = ko.pureComputed((): string => { + return this.advancedOptionsVisible() ? 'Fewer options' : 'More options'; + }); + this.helpToggleLabel = ko.pureComputed((): string => { + return this.helpVisible() ? 'Hide info' : 'How it works'; + }); + } + + protected getConfirmButtonLabel(): string | null { + return 'Download Admin Theme'; + } + + toggleAdvancedOptions(): void { + this.advancedOptionsVisible(!this.advancedOptionsVisible()); + } + + toggleHelp(): void { + this.helpVisible(!this.helpVisible()); + } + + onConfirm(event: JQueryEventObject) { + //Sanity checks. + const changesetName = this.getChangesetName(); + if (changesetName === '') { + alert('Error: The changeset has not been saved yet (name is empty).'); + return; + } + + if (!this.meta().isValid()) { + //This should never happen because the confirm button is disabled + //when the metadata is invalid. + alert('Error: The admin theme details are not valid.'); + return; + } + const metadata = this.meta().toObject(); + + this.isOperationInProgress(true); + + const $form = $('#ame-ac-theme-download-request-form'); + const $frame = $('#ame-ac-theme-download-frame'); + + //Cancel the operation and re-enable buttons if the request takes too long. + let isCancelledOrDone: boolean = false; + const requestTimeoutMs = 30000; + const requestStartTime = (new Date()).getTime(); + let statusCheckInterval: ReturnType | null = null; + + const cleanup = this.cleanupCurrentDownload = () => { + isCancelledOrDone = true; + + $frame.off('load.ameAcDownloadAdminTheme'); + if (timeoutTimer) { + clearTimeout(timeoutTimer); + } + if (statusCheckInterval) { + clearInterval(statusCheckInterval); + } + $frame.attr('src', 'about:blank'); + + this.isOperationInProgress(false); + + if (this.cleanupCurrentDownload === cleanup) { + this.cleanupCurrentDownload = () => { + }; + } + } + + const timeoutTimer = setTimeout(() => { + cleanup(); + alert('Error: The download operation timed out.'); + }, requestTimeoutMs); + + this.savePendingChangesetData().then( + () => { + if (isCancelledOrDone) { + return; + } + + this.changesetName(changesetName); + this.metadataJson(JSON.stringify(metadata)); + + //The server will set a cookie with a unique name that can be used + //to check if the download has been initiated. Note that the user + //can still cancel the download. + const cookieName = ('ameAcFileDownload_' + + new Date().getTime() + + '_' + + Math.round(Math.random() * 10000) //No dots allowed these cookie names. + ); + this.downloadCookieName(cookieName); + + //Clear the frame to prevent the old response from being read. + $frame.attr('src', 'about:blank'); + try { + $frame.contents().find('body').html(''); + } catch (e) { + //Ignore but log cross-origin errors. These should not happen in practice. + if (console && console.error) { + console.error(e); + } + } + + statusCheckInterval = setInterval(() => { + const cookieValue = $.cookie(cookieName); + if (cookieValue) { + cleanup(); + $.removeCookie(cookieName); + + //Close the dialog when the download starts. + this.isOpen(false); + return; + } + + if ((new Date()).getTime() - requestStartTime > requestTimeoutMs) { + cleanup(); + } + }, 1000); + + $frame.on('load.ameAcDownloadAdminTheme', () => { + //Get the response from the frame. It should be JSON displayed as text. + const responseText = String($frame.contents().text()).trim(); + const response = JSON.parse(responseText); + + cleanup(); + + if ((response === null) || (typeof response !== 'object')) { + alert('Error: Received an invalid response from the server.'); + } else { + if (!response.success) { + let errorMessage; + if (response.data.message) { + errorMessage = response.data.message; + } else { + errorMessage = 'An unknown error occurred on the server.'; + } + alert(errorMessage); + } else { + //This should never happen in practice. + alert('Error: The server did not start the download correctly.'); + } + } + }); + + $form.trigger('submit'); + }, + () => { + if (isCancelledOrDone) { + return; + } + + cleanup(); + alert('Error: Could not save the changeset data before generating an admin theme.') + } + ); + } + + onClose(event: JQueryEventObject, ui: any) { + this.cleanupCurrentDownload(); + } + } + + //endregion + + class SectionNavigation { + private sectionNavStack: KnockoutObservableArray = ko.observableArray([] as string[]); + private $sectionList: JQuery; + + public readonly breadcrumbs: KnockoutObservable; + + constructor() { + this.$sectionList = $('#ame-ac-container-collection'); + + this.$sectionList.on('click', '.ame-ac-section-link', (event) => { + event.preventDefault() + + if (event.currentTarget === null) { + return; //Shouldn't happen in practice, but let's satisfy the type checker. + } + + const targetId = $(event.currentTarget).data('target-id'); + if (targetId) { + this.navigateToSection(targetId); + } + }); + + this.$sectionList.on('click', '.ame-ac-section-back-button', (event) => { + event.preventDefault() + this.navigateBack(); + }); + + this.breadcrumbs = ko.pureComputed(() => { + return this.sectionNavStack() + .map((sectionId) => $('#' + sectionId)) + .filter(($section) => $section.length > 0) + .map(($section) => { + return { + title: $section.find('.ame-ac-section-title .ame-ac-section-own-title') + .first().text() + } + }); + }); + } + + navigateToSection(sectionElementId: string) { + const $section = $('#' + sectionElementId); + if ($section.length === 0) { + return; + } + + if ($section.hasClass('ame-ac-current-section')) { + return; //Already on this section. + } + + //If the requested section is in the navigation stack, navigate back + //to it instead of putting more sections on the stack. + const stackIndex = this.sectionNavStack.indexOf(sectionElementId); + if (stackIndex !== -1) { + while (this.sectionNavStack().length > stackIndex) { + this.navigateBack(); + } + return; + } + + const $previousSection = this.$sectionList.find('.ame-ac-current-section'); + if ($previousSection.length > 0) { + this.expectTransition($previousSection, '.ame-ac-section'); + $previousSection + .removeClass('ame-ac-current-section') + .addClass('ame-ac-previous-section'); + this.sectionNavStack.push($previousSection.attr('id')); + + $previousSection.trigger('adminMenuEditor:leaveSection'); + } + + this.expectTransition($section, '.ame-ac-section'); + $section.addClass('ame-ac-current-section'); + + $section.trigger('adminMenuEditor:enterSection'); + } + + navigateBack() { + if (this.sectionNavStack().length < 1) { + return; + } + const $newCurrentSection = $('#' + this.sectionNavStack.pop()); + if ($newCurrentSection.length === 0) { + return; + } + + const $oldCurrentSection = this.$sectionList.find('.ame-ac-current-section'); + this.expectTransition($oldCurrentSection, '.ame-ac-section'); + $oldCurrentSection.removeClass('ame-ac-current-section ame-ac-previous-section'); + $oldCurrentSection.trigger('adminMenuEditor:leaveSection'); + + const $oldPreviousSection = this.$sectionList.find('.ame-ac-previous-section'); + $oldPreviousSection.removeClass('ame-ac-previous-section'); + + //Show the new current section. + this.expectTransition($newCurrentSection, '.ame-ac-section'); + $newCurrentSection.addClass('ame-ac-current-section'); + $newCurrentSection.trigger('adminMenuEditor:enterSection'); + + //The next section in the stack becomes the previous section. + if (this.sectionNavStack().length > 0) { + this.$sectionList.find('#' + this.sectionNavStack()[this.sectionNavStack().length - 1]) + .addClass('ame-ac-previous-section'); + } + } + + //Add a special class to sections when they have an active CSS transition. + //This is used to keep both sections visible while the previous section + //slides out and the next section slides in. + expectTransition($element: JQuery, requiredSelector: string) { + if (prefersReducedMotion) { + return; + } + + if ($element.data('ameHasTransitionEvents')) { + return; //Event handler(s) already added. + } + + const transitionEvents = 'transitionend transitioncancel'; + + $element.addClass('ame-ac-transitioning'); + + function transitionEndCallback(event: JQueryEventObject) { + //Ignore events that bubble from child elements. + if (!$(event.target).is(requiredSelector)) { + return; + } + + $element + .off(transitionEvents, transitionEndCallback) + .data('ameHasTransitionEvents', null) + .removeClass('ame-ac-transitioning'); + } + + $element.data('ameHasTransitionEvents', true); + $element.on(transitionEvents, transitionEndCallback); + } + } + + export interface NavigationBreadcrumb { + title: string; + } + + export class AdminCustomizer extends AmeAdminCustomizerBase.AdminCustomizerBase implements CustomizableVmInterface { + private readonly exitPromptMessage = 'Unsaved changes will be lost if you navigate away from this page.'; + //Admin themes generated by this plugin should be fairly small. + private readonly maxImportFileSize = 500 * 1024; + + sectionNavigation: SectionNavigation; + settings: CustomizerSettingsCollection; + public readonly interfaceStructure: InterfaceStructure; + + private readonly $previewFrame: JQuery; + + /** + * Preview frame URL. + */ + private currentPreviewUrl: string | null = null; + /** + * The default preview URL that can be used when the current frame URL cannot be detected. + */ + private readonly initialPreviewUrl: string; + private previewConnection: ReturnType | null = null; + private readonly refreshPreviewNonce: string; + + private readonly $saveButton: JQuery; + + public readonly downloadThemeDialog: DownloadThemeDialog; + private $extraActionMenu: JQuery | null = null; + private $extraActionButton: JQuery | null = null; + + private $importFileInput: JQuery | null = null; + private isImporting: KnockoutObservable = ko.observable(false); + private lastImportReport: KnockoutObservable = ko.observable(null); + private isImportReportVisible: KnockoutObservable = ko.observable(true); + + private isDiscardingChanges: KnockoutObservable = ko.observable(false); + + public readonly isGeneralOverlayVisible: KnockoutObservable; + + private readonly importActionEnabled: KnockoutComputed; + private readonly discardChangesActionEnabled: KnockoutComputed; + private readonly downloadThemeActionEnabled: KnockoutComputed; + + constructor(scriptData: ScriptData) { + super(scriptData); + + this.settings = new CustomizerSettingsCollection( + scriptData.ajaxUrl, + scriptData.saveChangesetNonce, + scriptData.trashChangesetNonce, + scriptData.changesetName, + scriptData.changesetItemCount, + scriptData.changesetStatus + ); + _.forOwn(scriptData.settings, (data, id) => { + if (typeof id === 'string') { + this.settings.add(unserializeSetting(id, data)); + } + }); + + let sectionIdCounter = 0; + + this.interfaceStructure = unserializeUiElement( + scriptData.interfaceStructure, + this.settings.get.bind(this.settings), + (data: AnySpecificElementData) => { + switch (data.t) { + case 'section': + data.component = 'ame-ac-section'; + //All sections must have unique IDs for navigation to work. + if (!data.id) { + data.id = 'autoID-' + (++sectionIdCounter); + } + break; + case 'control-group': + data.component = 'ame-ac-control-group'; + break; + case 'control': + //Tell controls that use number inputs to position the popup + //slider within the customizer sidebar. + if ( + (data.component === 'ame-number-input') + || (data.component === 'ame-box-dimensions') + ) { + data.params = data.params || {}; + data.params.popupSliderWithin = '#ame-ac-sidebar-content'; + } + //Replace regular separators with AC-specific ones. + if (data.component === 'ame-horizontal-separator') { + data.component = 'ame-ac-separator'; + } + } + } + ); + + //Add the changeset name to the URL (if not already present). + const currentUrl = new URL(window.location.href); + if (currentUrl.searchParams.get('ame-ac-changeset') !== this.settings.changesetName()) { + currentUrl.searchParams.set('ame-ac-changeset', this.settings.changesetName()); + window.history.replaceState({}, '', currentUrl.href); + } + + //When the changeset name changes, also change the URL. Discourage navigating + //to the old URL (no pushState()) because the name is only expected to change + //when the old changeset becomes invalid (e.g. it's deleted or published). + this.settings.changesetName.subscribe((changesetName) => { + const url = new URL(window.location.href); + url.searchParams.set('ame-ac-changeset', changesetName); + window.history.replaceState({}, '', url.href); + }); + + this.$saveButton = $('#ame-ac-apply-changes'); + + //The save button should be enabled when: + // - There are non-zero changes in the current changeset. + // - All settings are valid. + // - The changeset is not in the process of being published, deleted, etc. + // - The contents of the changeset can be modified (e.g. not already published). + const isSaveButtonEnabled = ko.pureComputed(() => { + const changeset = this.settings.getCurrentChangeset(); + return ( + changeset.isNonEmpty() + && changeset.canBeModified() + && !this.settings.isExclusiveOperationInProgress() + && !this.settings.hasValidationErrors() + ); + }); + //Update button state when the customizer loads. + this.$saveButton.prop('disabled', !isSaveButtonEnabled()); + //And also on changes. + isSaveButtonEnabled.subscribe((isEnabled) => { + this.$saveButton.prop('disabled', !isEnabled); + //Change the text back to the default when the button is enabled. + if (isEnabled) { + this.$saveButton.val(this.$saveButton.data('default-text') ?? 'Save Changes'); + } + }); + + //Handle the "Save Changes" button. + this.$saveButton.on('click', () => { + //Show the spinner. + const $spinner = $('#ame-ac-primary-actions .spinner'); + $spinner.css('visibility', 'visible').show(); + + const publishFailNoticeId = 'ame-ac-publish-failed-notice'; + //Remove the previous error notification, if any. + $('#' + publishFailNoticeId).remove(); + + const promise = this.settings.publishChangeset(); + + promise.fail((error) => { + //Show a dismissible error notification. + let message = 'An unexpected error occurred while saving changes.'; + if (typeof error === 'string') { + message = error; + } else if (error instanceof Error) { + message = error.message; + } else if (typeof error.responseJSON === 'object') { + message = _.get(error.responseJSON, ['data', 'message'], message); + } + + const $notice = $('
    ') + .attr('id', publishFailNoticeId) + .addClass('notice notice-error is-dismissible') + .text(message); + + //WordPress won't automatically add the dismiss button to a dynamically + //generated notice like this, so we have to do it. + $notice.append( + $('') + .append('Dismiss this notice') + .on('click', (event) => { + event.preventDefault(); + $notice.remove(); //Not as fancy as WP does it. + }) + ); + + const $container = $('#ame-ac-global-notification-area'); + $container.append($notice); + }) + + promise.done(() => { + this.$saveButton.val(this.$saveButton.data('published-text') ?? 'Saved'); + + //The preview could be stale. For example, the color scheme module + //switches between "actual" and "preview" color schemes dynamically, + //but the "actual" scheme could change after applying new settings. + //Let's reload the preview frame to make sure it's up-to-date. + this.queuePreviewFrameReload(); + }); + + promise.always(() => { + $spinner.css('visibility', 'hidden'); + }); + }); + + //Prevent the user from interacting with settings while the changeset is being modified. + this.settings.isExclusiveOperationInProgress.subscribe((isInProgress) => { + $('#ame-ac-sidebar-blocker-overlay').toggle(isInProgress); + }); + + //Show a general overlay with a progress spinner while something is happening. + this.isGeneralOverlayVisible = ko.pureComputed(() => { + return this.isImporting() || this.isDiscardingChanges(); + }); + + //Initialize the "download admin theme" dialog. + this.downloadThemeDialog = new DownloadThemeDialog( + () => this.settings.getCurrentChangeset().name(), + () => this.settings.savePendingSettings() + ); + + //Toggle available extra actions based on changeset status. + this.importActionEnabled = ko.pureComputed(() => { + const changeset = this.settings.getCurrentChangeset(); + return changeset && changeset.canBeModified() + && !this.settings.isExclusiveOperationInProgress(); + }); + this.importActionEnabled.subscribe((isEnabled) => { + if (this.$extraActionMenu) { + this.$extraActionMenu.find('.ame-ac-import-theme-action') + .toggleClass('ui-state-disabled', !isEnabled); + } + }); + this.discardChangesActionEnabled = ko.pureComputed(() => { + const changeset = this.settings.getCurrentChangeset(); + return changeset && changeset.isNonEmpty() && changeset.canBeModified() + && !this.settings.isExclusiveOperationInProgress() + }); + this.discardChangesActionEnabled.subscribe((isEnabled) => { + if (this.$extraActionMenu) { + this.$extraActionMenu.find('.ame-ac-discard-changes-action') + .toggleClass('ui-state-disabled', !isEnabled); + } + }); + this.downloadThemeActionEnabled = ko.pureComputed(() => { + return !this.settings.isExclusiveOperationInProgress(); + }); + this.downloadThemeActionEnabled.subscribe((isEnabled) => { + if (this.$extraActionMenu) { + this.$extraActionMenu.find('.ame-ac-download-theme-action') + .toggleClass('ui-state-disabled', !isEnabled); + } + }); + + this.sectionNavigation = new SectionNavigation(); + + //Set up the preview frame. + this.$previewFrame = $('iframe#ame-ac-preview'); + + this.initialPreviewUrl = scriptData.initialPreviewUrl; + this.refreshPreviewNonce = scriptData.refreshPreviewNonce; + + this.$previewFrame.on('load', () => { + this.isFrameLoading = false; + + //The URL that was actually loaded might not match the one that + //was requested (e.g. because there was a redirect). + this.currentPreviewUrl = null; + + //Close the previous postMessage connection. + if (this.previewConnection) { + this.previewConnection.disconnect(); + this.previewConnection = null; + } + + const frame = this.$previewFrame.get(0) as HTMLIFrameElement; + if (!frame || !(frame instanceof HTMLIFrameElement)) { + return; + } + + //Try to get the preview URL from the iframe. + try { + const url = frame.contentWindow?.location.href; + if (url) { + this.currentPreviewUrl = url; + } + } catch (e) { + //We can't get the URL directly, probably because it's a cross-origin iframe. + } + + this.previewConnection = AmeAcCommunicator.connectToChild( + frame, + { + 'setPreviewUrl': (url: string) => { + if (this.isPreviewableUrl(url)) { + this.previewUrl = url; + } + }, + 'notifyPreviewUrlChanged': (url: string) => { + this.currentPreviewUrl = url; + } + }, + this.allowedCommOrigins, + scriptData.isWpDebugEnabled + ); + + this.previewConnection.promise.then((connection) => { + if (typeof connection === 'undefined') { + //This should never happen, but the type checker doesn't know that. + throw new Error('Unexpected error: Connection apparently succeeded, but the connection object is undefined'); + } + + connection.execute('getCurrentUrl').then((url) => { + if (url && (typeof url === 'string')) { + this.currentPreviewUrl = url; + } + }); + + //Notify other scripts that the preview frame is loaded and + //the postMessage connection is ready for use. + $('body').trigger('adminMenuEditor:acPreviewConnectionReady'); + }); + }); + + this.previewUrl = this.initialPreviewUrl; + + //Notify other scripts. This lets them register custom controls and so on. + $('#ame-ac-admin-customizer').trigger('adminMenuEditor:acRegister', [this]); + + const throttledReloadPreview = _.throttle( + () => { + this.queuePreviewFrameReload(); + }, + 1000, //The reload method does its own throttling, so we use a low wait time here. + {leading: true, trailing: true} + ); + + //Refresh the preview when any setting changes. + this.settings.addChangeListener((setting, newValue) => { + if ( + setting.supportsPostMessage + && this.previewConnection + && this.previewConnection.isConnected + ) { + this.previewConnection.execute('previewSetting', setting.id, newValue); + } else { + throttledReloadPreview(); + } + }); + + const registerUnloadPrompt = () => { + //Ask for confirmation when the user tries to leave the page and the changeset + //has unpublished changes. + $(window).on('beforeunload.ame-ac-exit-confirm', (event) => { + if (this.hasUnpublishedChanges()) { + event.preventDefault(); + //Note: The confirmation prompt will only be displayed if the user + //has interacted with the page (e.g. clicked something). + + //As of this writing, MDN says that some browsers still don't support triggering + //an "unsaved changes" prompt with event.preventDefault(). You need to set + //event.returnValue to a string or return a string from the event handler. + //Modern browsers will ignore the content and display their own generic message. + return this.exitPromptMessage; + } + }); + } + + /* + Allegedly, registering a beforeunload handler can cause the browser to + disable some optimizations, so let's only do it when the user changes + something or the changeset already contains some changes. + */ + if (this.settings.getCurrentChangeset().isNonEmpty()) { + registerUnloadPrompt(); + } else { + const listenerId = this.settings.addChangeListener(() => { + //Remove the listener after it has been triggered once. + this.settings.removeChangeListener(listenerId); + registerUnloadPrompt(); + }); + } + } + + getSettingObservable(settingId: string, defaultValue: any): KnockoutObservable { + //Let's just implement this temporarily while working on refactoring this + //stuff to use KO components. + return this.settings + .get(settingId) + .map(setting => setting.value) + .getOrElse(ko.observable(defaultValue)); + } + + getAllSettingValues(): Record { + throw new Error('Method not implemented.'); + } + + get previewUrl(): string | null { + return this.currentPreviewUrl; + } + + set previewUrl(url: string | null) { + if (url === this.currentPreviewUrl) { + return; + } + //The URL starts out as null, but it cannot be set to NULL again after + //the preview frame has been loaded. + if (url === null) { + throw new Error('Cannot directly set preview URL to null'); + } + + if (this.isPreviewableUrl(url)) { + this.navigatePreviewFrame(url); + } + } + + private navigatePreviewFrame(url: string | null = null, forceReload: boolean = false) { + const oldUrl = this.previewUrl; + if (url === null) { + url = oldUrl ?? this.initialPreviewUrl; + } + + const isSameUrl = (oldUrl === url); + if (isSameUrl && !forceReload) { + return; + } + + //If there are any unsaved changes, let's include them in the preview by simulating + //a form submission and sending the changes as form data. The server-side component + //will merge these changes with existing changeset data. + const unsavedChanges = this.settings.unsavedChanges; + const simulateFormSubmission = !_.isEmpty(unsavedChanges); + + const parsedUrl = new URL(url); + + //If we're not using form submission, add a special parameter + //to the URL to force a refresh. + const refreshParam = '_ame-ac-refresh-trigger'; + if (isSameUrl && !simulateFormSubmission) { + parsedUrl.searchParams.set(refreshParam, Date.now() + '_' + Math.random()); + } else { + //Otherwise, remove the parameter just to be safe. + parsedUrl.searchParams.delete(refreshParam); + } + + //Ensure that the changeset used in the preview matches the current + //changeset and preview is enabled. This is just a precaution. Normally, + //the preview script automatically changes link URLs. + parsedUrl.searchParams.set('ame-ac-changeset', this.settings.changesetName()); + parsedUrl.searchParams.set('ame-ac-preview', '1'); + + this.hasPendingPreviewReload = false; //Reloading now, so no longer pending. + this.isFrameLoading = true; + + //console.info('navigatePreviewFrame: Navigating to ' + parsedUrl.href); + if (simulateFormSubmission) { + const formData = { + action: 'ws_ame_ac_refresh_preview_frame', + "ame-ac-changeset": this.settings.changesetName(), + modified: JSON.stringify(unsavedChanges), + nonce: this.refreshPreviewNonce + } + + const $form = $('') + .attr('method', 'post') + .attr('action', parsedUrl.href) + .attr('target', 'ame-ac-preview-frame') + .appendTo('body'); + + let key: keyof typeof formData; + for (key in formData) { + const value = formData[key]; + $('') + .attr('type', 'hidden') + .attr('name', key) + .val(value) + .appendTo($form); + } + + this.currentPreviewUrl = parsedUrl.href; + $form.trigger('submit'); + $form.remove(); + } else { + this.currentPreviewUrl = parsedUrl.href; + this.$previewFrame.attr('src', this.currentPreviewUrl); + } + } + + private _isFrameLoading: boolean = false; + private frameLoadingTimeoutId: number | null = null; + private lastPreviewLoadTimestamp: Date = new Date(0); + + private reloadWaitTimeoutId: number | null = null; + private hasPendingPreviewReload: boolean = false; + + private set isFrameLoading(isLoading: boolean) { + const wasLoadingBefore = this._isFrameLoading; + if (!isLoading && (isLoading === wasLoadingBefore)) { + return; + } + //In some circumstances, we may start to load a new URL before + //the previous one has finished loading. This is valid and should + //reset the load timeout. + + $('#ame-ac-preview-refresh-indicator').toggleClass('ame-ac-show-indicator', isLoading); + if (this.frameLoadingTimeoutId) { + clearTimeout(this.frameLoadingTimeoutId); + this.frameLoadingTimeoutId = null; + } + + if (isLoading) { + //As a precaution, we'll assume that if the frame doesn't load in a reasonable + //time, it will never finish loading. + this.frameLoadingTimeoutId = window.setTimeout(() => { + if (this.isFrameLoading) { + this.isFrameLoading = false; + } + }, 20000); + } + this._isFrameLoading = isLoading; + + if (wasLoadingBefore && !isLoading) { + this.lastPreviewLoadTimestamp = new Date(); + } + + //Once the frame is loaded, trigger any pending reload. + if (!isLoading && this.hasPendingPreviewReload) { + this.hasPendingPreviewReload = false; + this.queuePreviewFrameReload(); + } + } + + public get isFrameLoading(): boolean { + return this._isFrameLoading; + } + + private queuePreviewFrameReload() { + if (this.reloadWaitTimeoutId) { + return; //The frame will reload soon. + } + + if (this.isFrameLoading) { + this.hasPendingPreviewReload = true; + return; + } + + //To avoid stressing the server, wait at least X ms after the last + //load completes before reloading the frame. + const reloadWaitTime = 2000; + const now = new Date(); + const timeSinceLastLoad = now.getTime() - this.lastPreviewLoadTimestamp.getTime(); + if (timeSinceLastLoad < reloadWaitTime) { + this.reloadWaitTimeoutId = window.setTimeout(() => { + this.reloadWaitTimeoutId = null; + this.queuePreviewFrameReload(); + }, reloadWaitTime - timeSinceLastLoad); + return; + } + + //Actually reload the frame. + this.navigatePreviewFrame(null, true); + } + + onBindingsApplied(rootElement: HTMLElement) { + //Navigate to the root section. In the current implementation this can't happen + //until bindings have been applied, so it's not part of the constructor. + this.navigateToRootSection(); + + //Initialize the action menu. + this.$extraActionButton = jQuery('#ame-ac-extra-actions-trigger', rootElement); + this.$extraActionMenu = jQuery('#ame-ac-extra-actions-menu', rootElement).menu(); + + //Update menu states. + this.importActionEnabled.notifySubscribers(this.importActionEnabled()); + this.discardChangesActionEnabled.notifySubscribers(this.discardChangesActionEnabled()); + + //Get the file picker. + this.$importFileInput = jQuery('#ame-ac-import-admin-theme-file', rootElement); + } + + navigateToRootSection() { + this.sectionNavigation.navigateToSection('ame-ac-section-structure-root'); + } + + // noinspection JSUnusedGlobalSymbols -- Used in at least one add-on. + /** + * Execute an RPC method in the preview frame. + * + * @param {string} methodName + * @param {*} args + */ + executeRpcMethod(methodName: string, ...args: any): JQueryPromise { + if (!this.previewConnection || !this.previewConnection.isConnected) { + return $.Deferred().reject('The preview frame is not connected.').promise(); + } + return this.previewConnection.execute(methodName, ...args); + } + + confirmExit() { + if (this.hasUnpublishedChanges()) { + if (window.confirm(this.exitPromptMessage)) { + //Remove the confirmation prompt that appears when leaving the page. + //We don't want to show two prompts. + $(window).off('beforeunload.ame-ac-exit-confirm'); + return true; + } + return false; + } + return true; + } + + private hasUnpublishedChanges(): boolean { + const changeset = this.settings.getCurrentChangeset(); + return ( + changeset.isNonEmpty() + && !changeset.wasPublished() + && (changeset.status() !== 'trash') //Can't publish a trashed changeset. + ); + } + + // noinspection JSUnusedGlobalSymbols -- Used in the Knockout template. + toggleExtraActionMenu() { + if (!this.$extraActionMenu) { + return; + } + this.$extraActionMenu.toggle(); + + if (this.$extraActionMenu.is(':visible')) { + //Position the menu below the button. + const $button = $('#ame-ac-extra-actions-trigger'); + this.$extraActionMenu.position({ + my: 'right top', + at: 'right bottom', + of: $button, + collision: 'flipfit' + }); + + //Hide the menu when the user clicks outside the menu or the button. + $(document).on('mousedown.ameAcExtraMenuHide', this.handleClickOutsideActionMenu.bind(this)); + } else { + //Remove the click listener if it's still active. + $(document).off('mousedown.ameAcExtraMenuHide'); + } + } + + handleClickOutsideActionMenu(event: JQueryEventObject) { + if ( + !this.$extraActionMenu + || !this.$extraActionMenu.is(':visible') + || !this.$extraActionButton + ) { + //The event listener should not be active if the menu is not visible. + $(document).off('mousedown.ameAcExtraMenuHide'); + return; + } + + const menuElement = this.$extraActionMenu.get(0); + const buttonElement = this.$extraActionButton.get(0); + const isClickOutsideMenu = !menuElement.contains(event.target); + const isClickOutsideButton = !buttonElement.contains(event.target); + + if (isClickOutsideMenu && isClickOutsideButton) { + this.hideExtraActionMenu(); + } + } + + private hideExtraActionMenu() { + if (!this.$extraActionMenu) { + return; + } + + this.$extraActionMenu.hide(); + //Stop listening for clicks outside the menu. + $(document).off('mousedown.ameAcExtraMenuHide'); + } + + actionOpenDownloadDialog() { + if (!this.downloadThemeActionEnabled()) { + return; + } + + this.downloadThemeDialog.isOpen(true); + this.isImportReportVisible(false); + this.hideExtraActionMenu(); + } + + actionOpenImportDialog() { + if (!this.importActionEnabled()) { + //Can't import if there is no changeset or the changeset can't be edited. + //The menu item should be disabled in this case, but we'll check anyway. + return false; + } + this.hideExtraActionMenu(); + + //Allow the default action to proceed, which will open the file picker. + return true; + } + + actionDiscardChanges() { + if (!this.discardChangesActionEnabled()) { + return; + } + this.hideExtraActionMenu(); + + if (this.settings.isExclusiveOperationInProgress()) { + alert('Another operation is in progress. Please wait for it to complete before discarding changes.'); + return; + } + + if (!confirm('Are you sure you want to discard your unsaved changes?')) { + return; + } + + this.isImportReportVisible(false); + this.isDiscardingChanges(true); + + this.settings.trashChangeset() + .then(() => { + //Reload the customizer with a new changeset. + + //First, to get the customizer's base URL, get the current URL + //and remove all query parameters except "page". + const url = new URL(window.location.href); + const page = url.searchParams.get('page'); + url.search = ''; + url.searchParams.set('page', page || 'ame-admin-customizer'); + //Don't need the hash either. + url.hash = ''; + + //Add a random parameter to force a reload. + url.searchParams.set('_ame-ac-reload', Math.random().toString(36).substring(7)); + + //Navigate to the new URL. + window.location.href = url.toString(); + + //Note that the isDiscardingChanges flag is not reset here, + //so the progress overlay will stay visible until the page reloads. + }) + .fail((requestObject) => { + let message: string = requestObject.statusText || 'Unknown error.'; + + if (typeof requestObject.responseJSON === 'object') { + const customMessage = _.get(requestObject.responseJSON, ['data', 'message']); + if (typeof customMessage === 'string') { + message = customMessage; + } + } + + alert('Error: ' + message); + this.isDiscardingChanges(false); + }); + } + + handleImportFileSelection() { + if (!this.$importFileInput) { + return; + } + const fileInput = this.$importFileInput.get(0) as HTMLInputElement; + if (!fileInput || !fileInput.files || (fileInput.files.length < 1)) { + return; + } + + //Get the first file. Normally, there should only be one. + const selectedFile = fileInput.files.item(0); + if (!selectedFile) { + return; + } + + //Limit the file size. + if (selectedFile.size > this.maxImportFileSize) { + alert( + 'Error: The selected file is too large. The maximum file size is ' + + Math.round(this.maxImportFileSize / 1024) + ' KiB' + ); + //Clear the file input. + this.$importFileInput.val(''); + return; + } + + this.isImporting(true); + this.lastImportReport(null); + + JSZip.loadAsync(selectedFile).then( + (zip) => { + const metadataFileRegex = /^([\\/]?[a-zA-Z0-9_-]+[\\/])metadata\.json$/; + const foundMetadataFiles = zip.file(metadataFileRegex); + if (!foundMetadataFiles || (foundMetadataFiles.length < 1)) { + throw new Error('The selected file is not an admin theme generated by this tool.'); + } + const metadataFile = foundMetadataFiles[0]; + + //Get the directory name and separator from the metadata file path. + //The prefix will usually be something like "admin-theme-slug/". + const matches = metadataFileRegex.exec(metadataFile.name); + let directoryPrefix: string; + if (!matches || (matches.length < 2)) { + throw new Error('The directory structure of this ZIP file is not recognized.'); + } else { + directoryPrefix = matches[1]; + } + + const settingsFile = zip.file(directoryPrefix + 'settings.json'); + if (!settingsFile) { + throw new Error('The selected ZIP file is missing a settings.json file.'); + } + + //Read both files. + return Promise.all([ + metadataFile.async('string'), + settingsFile.async('string') + ]); + }, + (error) => { + const errorMessage = error.message || error; + throw new Error('Error reading "' + selectedFile.name + '": ' + errorMessage); + } + ).then((fileContents) => { + if (!fileContents) { + throw new Error('Failed to read settings and metadata from the ZIP file.'); + } + + const metadata = this.parseImportedAdminThemeFile( + fileContents[0], + 'metadata.json', + AdminThemeMetadata + ); + const settings = this.parseImportedAdminThemeFile( + fileContents[1], + 'settings.json', + AdminThemeSettings + ); + const report = new AdminThemeImportReport(selectedFile.name, metadata); + + //Import metadata. + this.downloadThemeDialog.meta(new ObservableThemeMetadata(metadata)); + + //Import settings. + for (const [settingId, value] of Object.entries(settings)) { + report.totalSettings++; + + const foundSetting = this.settings.get(settingId); + foundSetting.forEach((setting) => { + const oldValue = setting.value(); + const errors = setting.tryUpdate(value); + if (errors && errors.length) { + report.invalidSettings++; + } else { + report.importedSettings++; + if (oldValue != value) { + report.differentImportedSettings++; + } + } + }); + + if (foundSetting.isEmpty()) { + report.skippedSettings++; + } + } + + this.lastImportReport(report); + this.isImportReportVisible(true); + + }).catch((error) => { + //Error handling: Show the error message to the user. + let errorMessage: string; + if (error instanceof Error) { + errorMessage = error.message; + } else { + errorMessage = String(error); + } + alert('Error: ' + errorMessage); + }).finally(() => { + this.isImporting(false); + this.$importFileInput?.val(''); + }); + } + + private parseImportedAdminThemeFile( + content: string, + name: string, + schema: T + ): ReturnType { + try { + const parsedJson = JSON.parse(content); + return schema.parse(parsedJson); + } catch (error) { + let errorMessage: string; + if (error instanceof ZodError) { + //Convert issues to a newline-separated string. + errorMessage = error.issues.map((issue) => { + return issue.path.join('.') + ': ' + issue.message; + }).join('\n'); + } else if (error instanceof Error) { + errorMessage = error.message; + } else { + errorMessage = String(error); + } + //Add the file name to the error message. + throw new Error('Error parsing ' + name + ':\n' + errorMessage); + } + } + + dismissImportReport(): void { + this.isImportReportVisible(false); + } + } +} + +declare global { + interface Window { + wsAdminCustomizer: AmeAdminCustomizer.AdminCustomizer; + } +} + +jQuery(function () { + //Give other scripts a chance to load before we start. + //Some of them also use jQuery to run when the DOM is ready. + setTimeout(() => { + window.wsAdminCustomizer = new AmeAdminCustomizer.AdminCustomizer(wsAmeAdminCustomizerData); + const rootElement = document.getElementById('ame-ac-admin-customizer'); + if (rootElement === null) { + throw new Error('The root element for the admin customizer was not found.'); + } + + ko.applyBindings(window.wsAdminCustomizer, rootElement); + + //Notify the customizer that bindings have been applied. It needs to do some + //additional setup that can't be done until the DOM structure is ready. + setTimeout(() => { + window.wsAdminCustomizer.onBindingsApplied(rootElement); + }, 5); //Components are rendered asynchronously. + }, 20); +}); \ No newline at end of file diff --git a/extras/modules/admin-customizer/admin-theme-template/admin-theme.php b/extras/modules/admin-customizer/admin-theme-template/admin-theme.php new file mode 100644 index 0000000..c997558 --- /dev/null +++ b/extras/modules/admin-customizer/admin-theme-template/admin-theme.php @@ -0,0 +1,166 @@ +getColorSchemeData(); + + $demoColors = array_merge( + [ + 'base' => '#23282d', + 'icon' => '#2c3338', + 'notification' => '#d54e21', + 'highlight' => '#0073aa', + ], + isset($config['colors']['demo']) ? $config['colors']['demo'] : [] + ); + $demoColors = array_slice(array_values($demoColors), 0, 4); + + $iconColors = array_merge( + [ + 'base' => '#23282d', + 'focus' => '#fff', + 'current' => '#fff', + ], + isset($config['colors']['icons']) ? $config['colors']['icons'] : [] + ); + + $name = trim('{pluginName}'); + if ( empty($name) ) { + $name = 'Custom Admin Color Scheme'; + } + + //Register the custom admin color scheme. + wp_admin_css_color( + self::COLOR_SCHEME_ID, + $name, + add_query_arg( + ['version' => '{randomHash}'], + plugins_url('color-scheme.css', __FILE__) + ), + $demoColors, + $iconColors + ); + + if ( $this->isColorOverrideEnabled() ) { + //Remove the "Admin Color Scheme" setting from the "Profile" page. + remove_action('admin_color_scheme_picker', 'admin_color_scheme_picker'); + //Force everyone to use the custom color scheme. + add_filter('get_user_option_admin_color', [$this, 'overrideUserColorScheme'], 10, 0); + } + } + + private function getColorSchemeData() { + //Use the cached data if available. + if ( $this->colorSchemeData !== null ) { + return $this->colorSchemeData; + } + + //Load from JSON. + $filePath = __DIR__ . '/color-scheme.json'; + if ( !file_exists($filePath) ) { + $this->colorSchemeData = []; + } else { + //This should always be a local file. + //phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown + $this->colorSchemeData = json_decode(file_get_contents($filePath), true); + if ( !is_array($this->colorSchemeData) ) { + $this->colorSchemeData = []; + } + } + return $this->colorSchemeData; + } + + private function isColorOverrideEnabled() { + $data = $this->getColorSchemeData(); + return !empty($data['isColorOverrideEnabled']); + } + + /** + * Override the admin color scheme. + * + * @return string The custom admin color scheme. + */ + function overrideUserColorScheme() { + return self::COLOR_SCHEME_ID; + } + + /** + * Apply the custom color scheme to the admin bar / Toolbar + * in the front-end. + */ + public function enqueueAdminBarStyle() { + //Only logged-in users can see the admin bar. + if ( !is_user_logged_in() ) { + return; + } + + //Does the stylesheet exist? + if ( !file_exists((__DIR__) . '/admin-bar-colors.css') ) { + return; + } + + //Should we use the custom color scheme for this user? + if ( + $this->isColorOverrideEnabled() + || (get_user_option('admin_color') === self::COLOR_SCHEME_ID) + ) { + return; + } + + wp_enqueue_style( + self::COLOR_SCHEME_ID . '-admin-bar', + plugins_url('admin-bar-colors.css', __FILE__), + [], + '{randomHash}' + ); + } +} + +new acIdentPrefixAdminTheme(); \ No newline at end of file diff --git a/extras/modules/admin-customizer/admin-theme-template/readme.txt b/extras/modules/admin-customizer/admin-theme-template/readme.txt new file mode 100644 index 0000000..874770b --- /dev/null +++ b/extras/modules/admin-customizer/admin-theme-template/readme.txt @@ -0,0 +1,17 @@ +=== {pluginName} === +Tags: admin theme, custom admin +Requires at least: {requiredWpVersion} +Tested up to: {testedWpVersion} +Stable tag: {pluginVersion} +Requires PHP: 5.6.20 + +{shortDescription} + +== Description == + +{shortDescription} + +== Installation == + +1. Upload the `{pluginSlug}` folder to the `/wp-content/plugins/` directory. +2. Activate the plugin through the 'Plugins' menu in WordPress. \ No newline at end of file diff --git a/extras/modules/admin-customizer/communicator.js b/extras/modules/admin-customizer/communicator.js new file mode 100644 index 0000000..f10093a --- /dev/null +++ b/extras/modules/admin-customizer/communicator.js @@ -0,0 +1,426 @@ +'use strict'; +/// +var AmeAcCommunicator; +(function (AmeAcCommunicator) { + const $ = jQuery; + const MessageFormatName = 'AmeAcCommunicator1'; + function connectToChild(frame, rpcMethods = {}, allowedOrigins = ['*'], enableConsoleLogging = false) { + if (frame.contentWindow === null) { + return new BadConnection(); + } + //Note: We could try to get the frame's origin from the src attribute, + //but that would not work consistently because the user could navigate + //to a different domain in the frame and the attribute would stay the same. + return new Connection('Parent', frame.contentWindow, rpcMethods, allowedOrigins, enableConsoleLogging); + } + AmeAcCommunicator.connectToChild = connectToChild; + function connectToParent(rpcMethods = {}, allowedOrigins = ['*'], enableConsoleLogging = false) { + if ((window.parent === null) || (window.parent === window)) { + return new BadConnection(); + } + return new Connection('Child', window.parent, rpcMethods, allowedOrigins, enableConsoleLogging); + } + AmeAcCommunicator.connectToParent = connectToParent; + class Connection { + constructor(name, target, rpcMethods = {}, allowedTargetOrigins = ['*'], consoleLoggingEnabled = false, connectionTimeout = 30000, myWindow = window) { + this.name = name; + this.target = target; + this.rpcMethods = rpcMethods; + this.allowedTargetOrigins = allowedTargetOrigins; + this.consoleLoggingEnabled = consoleLoggingEnabled; + this.myWindow = myWindow; + this.handshakeDone = false; + this.connectedOrigin = null; + this.rpcRequestCount = 0; + this.pendingRpcRequests = {}; + this.rpcTimeout = 30000; + this.isConnectionBroken = false; + this.log('Initializing...'); + this.rpcIdPrefix = 'aAcC_' + Connection.getRandomString(8) + '_'; + this.deferred = $.Deferred(); + this.promise = this.deferred.promise(); + //Try to auto-detect the origin. This won't work if the target is + //loaded from a different domain. + if (this.connectedOrigin === null) { + try { + this.connectedOrigin = this.target.location.origin; + this.log(`Detected target origin: ${this.connectedOrigin}`); + } + catch (e) { + //Leave the origin as null. + } + } + this.messageListener = (event) => { + if (!(event instanceof MessageEvent)) { + this.log('Ignoring non-message event.'); + return; + } + if (event.source !== this.target) { + this.log('Ignoring message from unknown source.'); + return; + } + if (!this.isAllowedOrigin(event.origin)) { + this.log('Ignoring message from disallowed origin: ' + event.origin); + return; + } + const message = parseMessage(event.data); + if (message === null) { + this.log('Ignoring invalid message.'); + return; + } + this.handleMessage(message, event.origin); + }; + this.myWindow.addEventListener('message', this.messageListener); + this.log('Event listener added.'); + //Send the handshake request to every allowed target origin. + for (const origin of this.allowedTargetOrigins) { + this.log(`Sending handshake request to ${origin}`); + this.target.postMessage(this.createHandshakeRequest(), origin); + } + //Give up if the other window doesn't respond in time. + if (connectionTimeout > 0) { + setTimeout(() => { + if (this.deferred.state() === 'pending') { + this.deferred.reject(); + } + }, connectionTimeout); + } + //Clean up event listener(s) if the connection fails for any reason. + this.deferred.fail(() => { + this.myWindow.removeEventListener('message', this.messageListener); + }); + } + handleMessage(message, origin) { + if (this.isConnectionBroken) { + this.log('Ignoring message because the connection is broken.'); + return; + } + this.log(`Received message from ${origin}: ${JSON.stringify(message)}`); + //Until the handshake is completed, process only handshake messages. + if (!this.handshakeDone) { + //We'll send a handshake request to the other window and wait + //for a response. Alternatively, the other window can send + //a handshake request to us. + if (isHandshakeResponse(message)) { + if (this.validateHandshakeResponse(message)) { + this.completeHandshake(message, origin); + } + else { + this.deferred.reject(); + } + } + else if (isHandshakeRequest(message)) { + if (this.validateHandshakeRequest(message)) { + this.send(this.createHandshakeResponse()); + this.completeHandshake(message, origin); + } + } + return; + } + if (isRpcRequest(message)) { + this.handleRpcRequest(message); + } + else if (isRpcResponse(message)) { + this.handleRpcResponse(message); + } + } + execute(method, ...args) { + if (this.isConnectionBroken) { + return $.Deferred().reject('Connection has already been closed.').promise(); + } + this.rpcRequestCount++; + const requestId = this.rpcIdPrefix + this.rpcRequestCount + '_' + Connection.getRandomString(6); + const requestDef = $.Deferred(); + //Remember the request so that we can resolve it when we get the response. + this.pendingRpcRequests[requestId] = requestDef; + //We'll set a timeout later. + let timeoutTimerId = null; + //Always clean up the request collection and the timer when the request + //is resolved, whether it was successful or not. + requestDef.always(() => { + delete this.pendingRpcRequests[requestId]; + if (timeoutTimerId !== null) { + clearTimeout(timeoutTimerId); + } + }); + const request = this.createMessage('rpc-request', { + 'method': method, + 'args': args, + 'requestId': requestId + }); + this.deferred + .done(() => { + //Time out the request eventually. + if (this.rpcTimeout > 0) { + timeoutTimerId = setTimeout(() => { + timeoutTimerId = null; + if (requestDef.state() === 'pending') { + requestDef.reject('RPC request timed out.'); + } + }, this.rpcTimeout); + } + this.send(request); + }) + .fail(() => { + requestDef.reject('Connection failed.'); + }); + return requestDef.promise(); + } + send(message) { + if (this.isConnectionBroken) { + return; + } + let origin = this.connectedOrigin; + if (origin === null) { + origin = '*'; + } + this.target.postMessage(message, origin); + } + /** + * Break the connection, e.g. when the iframe is removed from the page. + */ + disconnect() { + //Do nothing if already disconnected. + if (this.isConnectionBroken) { + return; + } + this.isConnectionBroken = true; + try { + this.myWindow.removeEventListener('message', this.messageListener); + } + catch (e) { + //Do nothing if the window is not valid. + } + if (this.deferred.state() === 'pending') { + this.deferred.reject(); + } + //Reject all pending RPC requests. + //The list could change during the loop, so we'll use a copy. + const pendingRequests = Object.keys(this.pendingRpcRequests).map(key => this.pendingRpcRequests[key]); + pendingRequests.forEach(request => { + if (request.state() === 'pending') { + request.reject('Connection explicitly closed.'); + } + }); + } + addRpcMethod(methodName, method) { + this.rpcMethods[methodName] = method; + } + createHandshakeRequest() { + return this.createMessage('handshake-request', { + 'myOrigin': this.getMyOrigin(), + }); + } + createHandshakeResponse() { + return this.createMessage('handshake-response', { + 'success': true, + 'myUrl': this.myWindow.location.href, + 'myOrigin': this.getMyOrigin(), + }); + } + getMyOrigin() { + let origin; + try { + origin = this.myWindow.location.origin; + } + catch (e) { + //Do nothing. + } + return origin !== null && origin !== void 0 ? origin : null; + } + isAllowedOrigin(origin) { + if (this.handshakeDone && this.connectedOrigin) { + //Use the connected origin. + return Connection.originMatches(this.connectedOrigin, origin); + } + //Check all allowed origins. + for (const allowedOrigin of this.allowedTargetOrigins) { + if (Connection.originMatches(allowedOrigin, origin)) { + return true; + } + } + return false; + } + static originMatches(required, input) { + //Null and '*' allow all origins. + if ((required === '*') || (required === null)) { + return true; + } + return required === input; + } + validateHandshakeRequest(request) { + return request.format === 'AmeAcCommunicator1' && request.tag === 'handshake-request'; + } + validateHandshakeResponse(response) { + return response.format === 'AmeAcCommunicator1' && response.tag === 'handshake-response'; + } + completeHandshake(message, origin) { + //Once the connection has been established, lock in the specific origin. + if (this.connectedOrigin === null) { + if (origin && (origin !== '*')) { + this.connectedOrigin = origin; + } + else if (message.myOrigin && (message.myOrigin !== '*')) { + this.connectedOrigin = message.myOrigin; + } + } + this.handshakeDone = true; + this.deferred.resolve(this); + this.log('Handshake complete. Connected origin: ' + this.connectedOrigin); + } + createMessage(tag, props) { + return Connection.combineObjects({ + 'format': 'AmeAcCommunicator1', + 'tag': tag + }, props); + } + /** + * Combine the properties of two objects into a new object (shallow merge). + * + * This is implemented as a separate method only to work around some TypeScript + * annoyances. Gradually constructing an object that obeys an interface is tricky. + */ + static combineObjects(one, two) { + let result = {}; + for (let key in one) { + result[key] = one[key]; + } + for (let key in two) { + result[key] = two[key]; + } + return result; + } + static getRandomString(length) { + if (typeof crypto !== 'undefined') { + try { + const bytes = crypto.getRandomValues(new Uint8Array(length)); + let pieces = []; + for (let i = 0; i < bytes.length; i++) { + pieces.push(bytes[i].toString(36)); + } + return pieces.join('').substring(0, length); + } + catch (e) { + //Fall through to the backup method. + } + } + let result = ''; + while (result.length < length) { + result += Math.random().toString(36).substring(2); + } + return result.substring(0, length); + } + handleRpcRequest(request) { + var _a; + if (!this.rpcMethods.hasOwnProperty(request.method)) { + this.send(this.createMessage('rpc-response', { + 'requestId': request.requestId, + 'result': null, + 'error': 'Unknown RPC method: ' + request.method + })); + return; + } + const method = this.rpcMethods[request.method]; + const args = (_a = request.args) !== null && _a !== void 0 ? _a : []; + try { + const result = method.apply(this, args); + this.send(this.createMessage('rpc-response', { + 'requestId': request.requestId, + 'result': (typeof result === 'undefined') ? null : result, + 'error': null + })); + } + catch (e) { + let errorMessage; + if (e && (typeof e === 'object') && e.message) { + errorMessage = e.message; + } + else { + errorMessage = e ? e.toString() : ''; + } + this.send(this.createMessage('rpc-response', { + 'requestId': request.requestId, + 'result': null, + 'error': errorMessage + })); + } + } + handleRpcResponse(response) { + //Is this a response to a request we actually sent? + const requestId = response.requestId; + if (!this.pendingRpcRequests.hasOwnProperty(requestId)) { + return; + } + const requestDef = this.pendingRpcRequests[requestId]; + if (requestDef.state() !== 'pending') { + //This normally shouldn't happen. + if (console && console.warn) { + console.warn('Received RPC response for request ID "' + requestId + + '", but the request has already been resolved.'); + } + delete this.pendingRpcRequests[requestId]; + return; + } + if ((typeof response.error !== 'undefined') && (response.error !== null)) { + requestDef.reject(response.error); + } + else { + requestDef.resolve(response.result); + } + //The request should remove itself from the pending list, + //but we'll do it here just in case. + delete this.pendingRpcRequests[requestId]; + } + get isConnected() { + return this.handshakeDone && !this.isConnectionBroken; + } + log(message) { + if (this.consoleLoggingEnabled && console && console.log) { + console.log('AmeAcCommunicator (' + this.name + '): ' + message); + } + } + } + class BadConnection { + constructor() { + this.promise = $.Deferred().reject('Not connected to a valid frame or window').promise(); + } + get isConnected() { + return false; + } + addRpcMethod(methodName, method) { + } + disconnect() { + } + execute(method, ...args) { + return $.Deferred().reject('Not connected to a valid frame or window').promise(); + } + } + function parseMessage(data) { + if ((typeof data !== 'object') + || (typeof data.format !== 'string') + || (typeof data.tag !== 'string') + || (data.format !== MessageFormatName)) { + return null; + } + return data; + } + function isHandshakeRequest(data) { + return data.tag === 'handshake-request'; + } + function isHandshakeResponse(data) { + return (data.tag === 'handshake-response') && (typeof data.success === 'boolean'); + } + function isRpcRequest(data) { + return ((data.tag === 'rpc-request') + && (typeof data.method === 'string') + && (typeof data.args !== 'undefined') + && (typeof data.requestId === 'string') + && (Array.isArray(data.args))); + } + function isRpcResponse(data) { + return ((data.tag === 'rpc-response') + && (typeof data.result !== 'undefined') + && (typeof data.requestId === 'string')); + } +})(AmeAcCommunicator || (AmeAcCommunicator = {})); +//# sourceMappingURL=communicator.js.map \ No newline at end of file diff --git a/extras/modules/admin-customizer/communicator.js.map b/extras/modules/admin-customizer/communicator.js.map new file mode 100644 index 0000000..903b8c9 --- /dev/null +++ b/extras/modules/admin-customizer/communicator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"communicator.js","sourceRoot":"","sources":["communicator.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/communicator.ts b/extras/modules/admin-customizer/communicator.ts new file mode 100644 index 0000000..ba88e92 --- /dev/null +++ b/extras/modules/admin-customizer/communicator.ts @@ -0,0 +1,577 @@ +'use strict'; + +/// + +namespace AmeAcCommunicator { + const $ = jQuery; + const MessageFormatName = 'AmeAcCommunicator1'; + + type RpcMethodCollection = { [methodName: string]: (...args: any[]) => any }; + + export function connectToChild( + frame: HTMLIFrameElement, + rpcMethods: RpcMethodCollection = {}, + allowedOrigins: string[] = ['*'], + enableConsoleLogging: boolean = false + ): ConnectionInterface { + if (frame.contentWindow === null) { + return new BadConnection(); + } + + //Note: We could try to get the frame's origin from the src attribute, + //but that would not work consistently because the user could navigate + //to a different domain in the frame and the attribute would stay the same. + return new Connection( + 'Parent', + frame.contentWindow, + rpcMethods, + allowedOrigins, + enableConsoleLogging + ); + } + + export function connectToParent( + rpcMethods: RpcMethodCollection = {}, + allowedOrigins: string[] = ['*'], + enableConsoleLogging: boolean = false + ): ConnectionInterface { + if ((window.parent === null) || (window.parent === window)) { + return new BadConnection(); + } + + return new Connection( + 'Child', + window.parent, + rpcMethods, + allowedOrigins, + enableConsoleLogging + ); + } + + interface ConnectionInterface { + promise: JQueryPromise; + + get isConnected(): boolean + + execute(method: string, ...args: any): JQueryPromise; + + /** + * Break the connection, e.g. when the iframe is removed from the page. + */ + disconnect(): void; + + addRpcMethod(methodName: string, method: (...args: any[]) => any): void; + } + + class Connection implements ConnectionInterface { + protected readonly deferred: JQueryDeferred; + public readonly promise: JQueryPromise; + + private handshakeDone: boolean = false; + private connectedOrigin: string | null = null; + private readonly messageListener: EventListener; + + private readonly rpcIdPrefix: string; + private rpcRequestCount: number = 0; + private pendingRpcRequests: Record> = {}; + private rpcTimeout: number = 30000; + + private isConnectionBroken: boolean = false; + + constructor( + protected readonly name: string, + protected readonly target: Window, + protected readonly rpcMethods: RpcMethodCollection = {}, + protected allowedTargetOrigins: string[] = ['*'], + public consoleLoggingEnabled: boolean = false, + connectionTimeout: number = 30000, + protected readonly myWindow: Window = window + ) { + this.log('Initializing...'); + this.rpcIdPrefix = 'aAcC_' + Connection.getRandomString(8) + '_'; + + this.deferred = $.Deferred(); + this.promise = this.deferred.promise(); + + //Try to auto-detect the origin. This won't work if the target is + //loaded from a different domain. + if (this.connectedOrigin === null) { + try { + this.connectedOrigin = this.target.location.origin; + this.log(`Detected target origin: ${this.connectedOrigin}`); + } catch (e) { + //Leave the origin as null. + } + } + + this.messageListener = (event: Event) => { + if (!(event instanceof MessageEvent)) { + this.log('Ignoring non-message event.'); + return; + } + + if (event.source !== this.target) { + this.log('Ignoring message from unknown source.'); + return; + } + + if (!this.isAllowedOrigin(event.origin)) { + this.log('Ignoring message from disallowed origin: ' + event.origin); + return; + } + + const message = parseMessage(event.data); + if (message === null) { + this.log('Ignoring invalid message.'); + return; + } + + this.handleMessage(message, event.origin); + }; + this.myWindow.addEventListener('message', this.messageListener); + this.log('Event listener added.'); + + //Send the handshake request to every allowed target origin. + for (const origin of this.allowedTargetOrigins) { + this.log(`Sending handshake request to ${origin}`); + this.target.postMessage(this.createHandshakeRequest(), origin); + } + + //Give up if the other window doesn't respond in time. + if (connectionTimeout > 0) { + setTimeout(() => { + if (this.deferred.state() === 'pending') { + this.deferred.reject(); + } + }, connectionTimeout); + } + + //Clean up event listener(s) if the connection fails for any reason. + this.deferred.fail(() => { + this.myWindow.removeEventListener('message', this.messageListener); + }); + } + + protected handleMessage(message: MessageData, origin: string): void { + if (this.isConnectionBroken) { + this.log('Ignoring message because the connection is broken.'); + return; + } + this.log(`Received message from ${origin}: ${JSON.stringify(message)}`); + + //Until the handshake is completed, process only handshake messages. + if (!this.handshakeDone) { + //We'll send a handshake request to the other window and wait + //for a response. Alternatively, the other window can send + //a handshake request to us. + if (isHandshakeResponse(message)) { + if (this.validateHandshakeResponse(message)) { + this.completeHandshake(message, origin); + } else { + this.deferred.reject(); + } + } else if (isHandshakeRequest(message)) { + if (this.validateHandshakeRequest(message)) { + this.send(this.createHandshakeResponse()); + this.completeHandshake(message, origin); + } + } + return; + } + + if (isRpcRequest(message)) { + this.handleRpcRequest(message); + } else if (isRpcResponse(message)) { + this.handleRpcResponse(message); + } + } + + execute(method: string, ...args: any): JQueryPromise { + if (this.isConnectionBroken) { + return $.Deferred().reject('Connection has already been closed.').promise(); + } + + this.rpcRequestCount++; + const requestId = this.rpcIdPrefix + this.rpcRequestCount + '_' + Connection.getRandomString(6); + + const requestDef = $.Deferred(); + //Remember the request so that we can resolve it when we get the response. + this.pendingRpcRequests[requestId] = requestDef; + //We'll set a timeout later. + let timeoutTimerId: null | ReturnType = null; + + //Always clean up the request collection and the timer when the request + //is resolved, whether it was successful or not. + requestDef.always(() => { + delete this.pendingRpcRequests[requestId]; + if (timeoutTimerId !== null) { + clearTimeout(timeoutTimerId); + } + }); + + const request: RpcRequest = this.createMessage('rpc-request', { + 'method': method, + 'args': args, + 'requestId': requestId + }); + + this.deferred + .done(() => { + //Time out the request eventually. + if (this.rpcTimeout > 0) { + timeoutTimerId = setTimeout(() => { + timeoutTimerId = null; + if (requestDef.state() === 'pending') { + requestDef.reject('RPC request timed out.'); + } + }, this.rpcTimeout); + } + + this.send(request); + }) + .fail(() => { + requestDef.reject('Connection failed.'); + }); + + return requestDef.promise(); + } + + protected send>(message: T): void { + if (this.isConnectionBroken) { + return; + } + + let origin = this.connectedOrigin; + if (origin === null) { + origin = '*'; + } + + this.target.postMessage(message, origin); + } + + /** + * Break the connection, e.g. when the iframe is removed from the page. + */ + disconnect() { + //Do nothing if already disconnected. + if (this.isConnectionBroken) { + return; + } + + this.isConnectionBroken = true; + + try { + this.myWindow.removeEventListener('message', this.messageListener); + } catch (e) { + //Do nothing if the window is not valid. + } + + if (this.deferred.state() === 'pending') { + this.deferred.reject(); + } + + //Reject all pending RPC requests. + //The list could change during the loop, so we'll use a copy. + const pendingRequests = Object.keys(this.pendingRpcRequests).map(key => this.pendingRpcRequests[key]); + pendingRequests.forEach(request => { + if (request.state() === 'pending') { + request.reject('Connection explicitly closed.'); + } + }); + } + + addRpcMethod(methodName: string, method: (...args: any[]) => any): void { + this.rpcMethods[methodName] = method; + } + + protected createHandshakeRequest(): HandshakeRequest { + return this.createMessage( + 'handshake-request', + { + 'myOrigin': this.getMyOrigin(), + } + ); + } + + protected createHandshakeResponse(): HandshakeResponse { + return this.createMessage( + 'handshake-response', + { + 'success': true, + 'myUrl': this.myWindow.location.href, + 'myOrigin': this.getMyOrigin(), + } + ); + } + + protected getMyOrigin(): string | null { + let origin; + try { + origin = this.myWindow.location.origin; + } catch (e) { + //Do nothing. + } + return origin ?? null; + } + + protected isAllowedOrigin(origin: string): boolean { + if (this.handshakeDone && this.connectedOrigin) { + //Use the connected origin. + return Connection.originMatches(this.connectedOrigin, origin); + } + + //Check all allowed origins. + for (const allowedOrigin of this.allowedTargetOrigins) { + if (Connection.originMatches(allowedOrigin, origin)) { + return true; + } + } + return false; + } + + private static originMatches(required: string | null, input: string): boolean { + //Null and '*' allow all origins. + if ((required === '*') || (required === null)) { + return true; + } + return required === input; + } + + protected validateHandshakeRequest(request: HandshakeRequest): boolean { + return request.format === 'AmeAcCommunicator1' && request.tag === 'handshake-request'; + } + + protected validateHandshakeResponse(response: HandshakeResponse): boolean { + return response.format === 'AmeAcCommunicator1' && response.tag === 'handshake-response'; + } + + protected completeHandshake(message: HandshakeResponse | HandshakeRequest, origin: string): void { + //Once the connection has been established, lock in the specific origin. + if (this.connectedOrigin === null) { + if (origin && (origin !== '*')) { + this.connectedOrigin = origin; + } else if (message.myOrigin && (message.myOrigin !== '*')) { + this.connectedOrigin = message.myOrigin; + } + } + this.handshakeDone = true; + this.deferred.resolve(this); + + this.log('Handshake complete. Connected origin: ' + this.connectedOrigin); + } + + protected createMessage>( + tag: T, props: P + ): MessageData & P { + return Connection.combineObjects( + { + 'format': 'AmeAcCommunicator1', + 'tag': tag + }, + props + ); + } + + /** + * Combine the properties of two objects into a new object (shallow merge). + * + * This is implemented as a separate method only to work around some TypeScript + * annoyances. Gradually constructing an object that obeys an interface is tricky. + */ + private static combineObjects, B extends Record>( + one: A, two: B + ): A & B { + let result: Record = {}; + for (let key in one) { + result[key] = one[key]; + } + for (let key in two) { + result[key] = two[key]; + } + return result as A & B; + } + + private static getRandomString(length: number): string { + if (typeof crypto !== 'undefined') { + try { + const bytes = crypto.getRandomValues(new Uint8Array(length)); + let pieces = []; + for (let i = 0; i < bytes.length; i++) { + pieces.push(bytes[i].toString(36)); + } + return pieces.join('').substring(0, length); + } catch (e) { + //Fall through to the backup method. + } + } + + let result = ''; + while (result.length < length) { + result += Math.random().toString(36).substring(2); + } + return result.substring(0, length); + } + + private handleRpcRequest(request: RpcRequest): void { + if (!this.rpcMethods.hasOwnProperty(request.method)) { + this.send(this.createMessage('rpc-response', { + 'requestId': request.requestId, + 'result': null, + 'error': 'Unknown RPC method: ' + request.method + })); + return; + } + + const method = this.rpcMethods[request.method]; + const args = request.args ?? []; + try { + const result = method.apply(this, args); + this.send(this.createMessage('rpc-response', { + 'requestId': request.requestId, + 'result': (typeof result === 'undefined') ? null : result, + 'error': null + })); + } catch (e) { + let errorMessage: string; + if (e && (typeof e === 'object') && (e as any).message) { + errorMessage = (e as any).message; + } else { + errorMessage = e ? e.toString() : ''; + } + + this.send(this.createMessage('rpc-response', { + 'requestId': request.requestId, + 'result': null, + 'error': errorMessage + })); + } + } + + private handleRpcResponse(response: RpcResponse): void { + //Is this a response to a request we actually sent? + const requestId = response.requestId; + if (!this.pendingRpcRequests.hasOwnProperty(requestId)) { + return; + } + + const requestDef = this.pendingRpcRequests[requestId]; + if (requestDef.state() !== 'pending') { + //This normally shouldn't happen. + if (console && console.warn) { + console.warn( + 'Received RPC response for request ID "' + requestId + + '", but the request has already been resolved.' + ); + } + delete this.pendingRpcRequests[requestId]; + return; + } + + if ((typeof response.error !== 'undefined') && (response.error !== null)) { + requestDef.reject(response.error); + } else { + requestDef.resolve(response.result); + } + + //The request should remove itself from the pending list, + //but we'll do it here just in case. + delete this.pendingRpcRequests[requestId]; + } + + get isConnected(): boolean { + return this.handshakeDone && !this.isConnectionBroken; + } + + private log(message: string) { + if (this.consoleLoggingEnabled && console && console.log) { + console.log('AmeAcCommunicator (' + this.name + '): ' + message); + } + } + } + + class BadConnection implements ConnectionInterface { + promise: JQueryPromise = + $.Deferred().reject('Not connected to a valid frame or window').promise(); + + get isConnected(): boolean { + return false; + } + + addRpcMethod(methodName: string, method: (...args: any[]) => any): void { + } + + disconnect(): void { + } + + execute(method: string, ...args: any): JQueryPromise { + return $.Deferred().reject('Not connected to a valid frame or window').promise(); + } + } + + interface MessageData { + format: string; + tag: TAG; + + [key: string]: any; + } + + interface HandshakeRequest extends MessageData<'handshake-request'> { + myOrigin: string | null; + } + + interface HandshakeResponse extends MessageData<'handshake-response'> { + success: boolean; + myUrl: string; + myOrigin: string | null; + } + + interface RpcRequest extends MessageData<'rpc-request'> { + method: string; + args: any[]; + requestId: string; + } + + interface RpcResponse extends MessageData<'rpc-response'> { + result: any; + error: any; + requestId: string; + } + + function parseMessage(data: any): MessageData | null { + if ( + (typeof data !== 'object') + || (typeof data.format !== 'string') + || (typeof data.tag !== 'string') + || (data.format !== MessageFormatName) + ) { + return null; + } + return data; + } + + function isHandshakeRequest(data: MessageData): data is HandshakeRequest { + return data.tag === 'handshake-request'; + } + + function isHandshakeResponse(data: MessageData): data is HandshakeResponse { + return (data.tag === 'handshake-response') && (typeof data.success === 'boolean'); + } + + function isRpcRequest(data: MessageData): data is RpcRequest { + return ( + (data.tag === 'rpc-request') + && (typeof data.method === 'string') + && (typeof data.args !== 'undefined') + && (typeof data.requestId === 'string') + && (Array.isArray(data.args)) + ); + } + + function isRpcResponse(data: MessageData): data is RpcResponse { + return ( + (data.tag === 'rpc-response') + && (typeof data.result !== 'undefined') + && (typeof data.requestId === 'string') + ); + } +} diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-content-section.js b/extras/modules/admin-customizer/ko-components/ame-ac-content-section.js new file mode 100644 index 0000000..1aaf0e9 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-content-section.js @@ -0,0 +1,39 @@ +import { AmeAcSection } from './ame-ac-section.js'; +import { createComponentConfig } from '../../../pro-customizables/ko-components/control-base.js'; +class AmeAcContentSection extends AmeAcSection { + constructor(params, $element) { + super(params, $element); + if ((typeof params.parentSectionLevel === 'function') && ko.isObservable(params.parentSectionLevel)) { + this.parentSectionLevel = params.parentSectionLevel; + } + else { + this.parentSectionLevel = null; + } + this.contentSectionLevel = ko.pureComputed(() => { + let parentLevel = 0; + if (this.parentSectionLevel !== null) { + parentLevel = this.parentSectionLevel(); + } + return parentLevel + 1; + }); + //Tell child sections about our section level. + this.childComponents().forEach((child) => { + if (child.name === 'ame-ac-content-section') { + child.params.parentSectionLevel = this.contentSectionLevel; + } + }); + this.sectionLevelClass = ko.pureComputed(() => { + const level = this.contentSectionLevel(); + return 'ame-ac-content-section-' + level; + }); + } +} +export default createComponentConfig(AmeAcContentSection, ` +
  • +

    +
  • + + + +`); +//# sourceMappingURL=ame-ac-content-section.js.map \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-content-section.js.map b/extras/modules/admin-customizer/ko-components/ame-ac-content-section.js.map new file mode 100644 index 0000000..47d75d6 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-content-section.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ame-ac-content-section.js","sourceRoot":"","sources":["ame-ac-content-section.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAC,qBAAqB,EAAoB,MAAM,0DAA0D,CAAC;AAElH,MAAM,mBAAoB,SAAQ,YAAY;IAM7C,YAAY,MAAyB,EAAE,QAAgB;QACtD,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAExB,IAAI,CAAC,OAAO,MAAM,CAAC,kBAAkB,KAAK,UAAU,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,kBAAkB,CAAC,EAAE;YACpG,IAAI,CAAC,kBAAkB,GAAG,MAAM,CAAC,kBAAkB,CAAC;SACpD;aAAM;YACN,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;SAC/B;QAED,IAAI,CAAC,mBAAmB,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE;YAC/C,IAAI,WAAW,GAAG,CAAC,CAAC;YACpB,IAAI,IAAI,CAAC,kBAAkB,KAAK,IAAI,EAAE;gBACrC,WAAW,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;aACxC;YACD,OAAO,WAAW,GAAG,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,8CAA8C;QAC9C,IAAI,CAAC,eAAe,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACxC,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB,EAAE;gBAC5C,KAAK,CAAC,MAAM,CAAC,kBAAkB,GAAG,IAAI,CAAC,mBAAmB,CAAC;aAC3D;QACF,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE;YAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACzC,OAAO,yBAAyB,GAAG,KAAK,CAAC;QAC1C,CAAC,CAAC,CAAC;IACJ,CAAC;CACD;AAED,eAAe,qBAAqB,CAAC,mBAAmB,EAAE;;;;;;;CAOzD,CAAC,CAAC"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-content-section.ts b/extras/modules/admin-customizer/ko-components/ame-ac-content-section.ts new file mode 100644 index 0000000..bb04903 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-content-section.ts @@ -0,0 +1,48 @@ +import {AmeAcSection} from './ame-ac-section.js'; +import {createComponentConfig, KoComponentParams} from '../../../pro-customizables/ko-components/control-base.js'; + +class AmeAcContentSection extends AmeAcSection { + public readonly contentSectionLevel: KnockoutObservable; + protected parentSectionLevel: KnockoutObservable | null; + + public readonly sectionLevelClass: KnockoutComputed; + + constructor(params: KoComponentParams, $element: JQuery) { + super(params, $element); + + if ((typeof params.parentSectionLevel === 'function') && ko.isObservable(params.parentSectionLevel)) { + this.parentSectionLevel = params.parentSectionLevel; + } else { + this.parentSectionLevel = null; + } + + this.contentSectionLevel = ko.pureComputed(() => { + let parentLevel = 0; + if (this.parentSectionLevel !== null) { + parentLevel = this.parentSectionLevel(); + } + return parentLevel + 1; + }); + + //Tell child sections about our section level. + this.childComponents().forEach((child) => { + if (child.name === 'ame-ac-content-section') { + child.params.parentSectionLevel = this.contentSectionLevel; + } + }); + + this.sectionLevelClass = ko.pureComputed(() => { + const level = this.contentSectionLevel(); + return 'ame-ac-content-section-' + level; + }); + } +} + +export default createComponentConfig(AmeAcContentSection, ` +
  • +

    +
  • + + + +`); \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-control-group.js b/extras/modules/admin-customizer/ko-components/ame-ac-control-group.js new file mode 100644 index 0000000..1eae1d2 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-control-group.js @@ -0,0 +1,47 @@ +import { ComponentBindingOptions, createComponentConfig, KoContainerViewModel } from '../../../pro-customizables/ko-components/control-base.js'; +import { AmeCustomizable } from '../../../pro-customizables/assets/customizable.js'; +var ControlGroup = AmeCustomizable.ControlGroup; +class AmeAcControlGroup extends KoContainerViewModel { + constructor(params, $element) { + var _a, _b; + super(params, $element); + this.labelFor = (_b = ((_a = this.uiElement) === null || _a === void 0 ? void 0 : _a.labelFor)) !== null && _b !== void 0 ? _b : null; + this.titleDisabled = (typeof params.titleDisabled !== 'undefined') ? (!!params.titleDisabled) : false; + } + getExpectedUiElementType() { + return ControlGroup; + } + mapChildToComponentBinding(child) { + if (child.component) { + return ComponentBindingOptions.fromElement(child); + } + return super.mapChildToComponentBinding(child); + } +} +export default createComponentConfig(AmeAcControlGroup, ` +
  • + + + + + + + + +
      +
    • + + + + +
    • +
    +
  • +`); +//# sourceMappingURL=ame-ac-control-group.js.map \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-control-group.js.map b/extras/modules/admin-customizer/ko-components/ame-ac-control-group.js.map new file mode 100644 index 0000000..361ecb1 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-control-group.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ame-ac-control-group.js","sourceRoot":"","sources":["ame-ac-control-group.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,uBAAuB,EACvB,qBAAqB,EAErB,oBAAoB,EACpB,MAAM,0DAA0D,CAAC;AAClE,OAAO,EAAC,eAAe,EAAC,MAAM,mDAAmD,CAAC;AAClF,IAAO,YAAY,GAAG,eAAe,CAAC,YAAY,CAAC;AAEnD,MAAM,iBAAkB,SAAQ,oBAAkC;IAIjE,YAAY,MAAyB,EAAE,QAAgB;;QACtD,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,MAAA,CAAC,MAAA,IAAI,CAAC,SAAS,0CAAE,QAAQ,CAAC,mCAAI,IAAI,CAAC;QACnD,IAAI,CAAC,aAAa,GAAG,CAAC,OAAO,MAAM,CAAC,aAAa,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACvG,CAAC;IAES,wBAAwB;QACjC,OAAO,YAAY,CAAC;IACrB,CAAC;IAES,0BAA0B,CAAC,KAAgC;QACpE,IAAI,KAAK,CAAC,SAAS,EAAE;YACpB,OAAO,uBAAuB,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;SAClD;QACD,OAAO,KAAK,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC;CACD;AAED,eAAe,qBAAqB,CAAC,iBAAiB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;CAyBvD,CAAC,CAAC"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-control-group.ts b/extras/modules/admin-customizer/ko-components/ame-ac-control-group.ts new file mode 100644 index 0000000..f54b801 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-control-group.ts @@ -0,0 +1,57 @@ +import { + ComponentBindingOptions, + createComponentConfig, + KoComponentParams, + KoContainerViewModel +} from '../../../pro-customizables/ko-components/control-base.js'; +import {AmeCustomizable} from '../../../pro-customizables/assets/customizable.js'; +import ControlGroup = AmeCustomizable.ControlGroup; + +class AmeAcControlGroup extends KoContainerViewModel { + public readonly labelFor: string|null; + public readonly titleDisabled: boolean; + + constructor(params: KoComponentParams, $element: JQuery) { + super(params, $element); + this.labelFor = (this.uiElement?.labelFor) ?? null; + this.titleDisabled = (typeof params.titleDisabled !== 'undefined') ? (!!params.titleDisabled) : false; + } + + protected getExpectedUiElementType(): Constructor | null { + return ControlGroup; + } + + protected mapChildToComponentBinding(child: AmeCustomizable.UiElement): ComponentBindingOptions | null { + if (child.component) { + return ComponentBindingOptions.fromElement(child); + } + return super.mapChildToComponentBinding(child); + } +} + +export default createComponentConfig(AmeAcControlGroup, ` +
  • + + + + + + + + +
      +
    • + + + + +
    • +
    +
  • +`); \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-control.js b/extras/modules/admin-customizer/ko-components/ame-ac-control.js new file mode 100644 index 0000000..4a08fa3 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-control.js @@ -0,0 +1,40 @@ +import { createComponentConfig, KoControlViewModel } from '../../../pro-customizables/ko-components/control-base.js'; +import { AmeCustomizable } from '../../../pro-customizables/assets/customizable.js'; +var Control = AmeCustomizable.Control; +class MissingComponentError extends Error { + constructor(uiElement) { + super(`The UI element "${uiElement.label}" [${uiElement.id}] is missing a component name.`); + this.uiElement = uiElement; + } +} +class AmeAcControl extends KoControlViewModel { + constructor(params, $element) { + super(params, $element); + //uiElement is required for this component. + if (!this.uiElement) { + throw new Error('The uiElement parameter is required for AmeAcControl'); + } + this.wrapperLabelEnabled = (this.uiElement.label !== '') && (!this.uiElement.includesOwnLabel); + this.labelForId = this.uiElement.labelTargetId; + if (!this.uiElement.component) { + throw new MissingComponentError(this.uiElement); + } + } + getExpectedUiElementType() { + return Control; + } +} +export default createComponentConfig(AmeAcControl, ` +
  • + + + + + + + + + +
  • +`); +//# sourceMappingURL=ame-ac-control.js.map \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-control.js.map b/extras/modules/admin-customizer/ko-components/ame-ac-control.js.map new file mode 100644 index 0000000..0ea4f6f --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-control.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ame-ac-control.js","sourceRoot":"","sources":["ame-ac-control.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,EAAqB,kBAAkB,EAC5D,MAAM,0DAA0D,CAAC;AAClE,OAAO,EAAC,eAAe,EAAC,MAAM,mDAAmD,CAAC;AAClF,IAAO,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC;AAEzC,MAAM,qBAAsB,SAAQ,KAAK;IACxC,YAA4B,SAAkB;QAC7C,KAAK,CAAC,mBAAmB,SAAS,CAAC,KAAK,MAAM,SAAS,CAAC,EAAE,gCAAgC,CAAC,CAAC;QADjE,cAAS,GAAT,SAAS,CAAS;IAE9C,CAAC;CACD;AAED,MAAM,YAAa,SAAQ,kBAA2B;IAIrD,YAAY,MAAyB,EAAE,QAAgB;QACtD,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACxB,2CAA2C;QAC3C,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACpB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;SACxE;QAED,IAAI,CAAC,mBAAmB,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC/F,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC;QAE/C,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE;YAC9B,MAAM,IAAI,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;SAChD;IACF,CAAC;IAES,wBAAwB;QACjC,OAAO,OAAO,CAAC;IAChB,CAAC;CACD;AAED,eAAe,qBAAqB,CAAC,YAAY,EAAE;;;;;;;;;;;;CAYlD,CAAC,CAAC"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-control.ts b/extras/modules/admin-customizer/ko-components/ame-ac-control.ts new file mode 100644 index 0000000..c5bf5c9 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-control.ts @@ -0,0 +1,49 @@ +import { + createComponentConfig, KoComponentParams, KoControlViewModel +} from '../../../pro-customizables/ko-components/control-base.js'; +import {AmeCustomizable} from '../../../pro-customizables/assets/customizable.js'; +import Control = AmeCustomizable.Control; + +class MissingComponentError extends Error { + constructor(public readonly uiElement: Control) { + super(`The UI element "${uiElement.label}" [${uiElement.id}] is missing a component name.`); + } +} + +class AmeAcControl extends KoControlViewModel { + public readonly wrapperLabelEnabled: boolean; + public readonly labelForId: string; + + constructor(params: KoComponentParams, $element: JQuery) { + super(params, $element); + //uiElement is required for this component. + if (!this.uiElement) { + throw new Error('The uiElement parameter is required for AmeAcControl'); + } + + this.wrapperLabelEnabled = (this.uiElement.label !== '') && (!this.uiElement.includesOwnLabel); + this.labelForId = this.uiElement.labelTargetId; + + if (!this.uiElement.component) { + throw new MissingComponentError(this.uiElement); + } + } + + protected getExpectedUiElementType(): Constructor | null { + return Control; + } +} + +export default createComponentConfig(AmeAcControl, ` +
  • + + + + + + + + + +
  • +`); \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-section-link.js b/extras/modules/admin-customizer/ko-components/ame-ac-section-link.js new file mode 100644 index 0000000..07b8c59 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-section-link.js @@ -0,0 +1,23 @@ +import { createComponentConfig, KoContainerViewModel } from '../../../pro-customizables/ko-components/control-base.js'; +import { AmeCustomizable } from '../../../pro-customizables/assets/customizable.js'; +var Section = AmeCustomizable.Section; +import { AmeAcSection } from './ame-ac-section.js'; +class AmeAcSectionLink extends KoContainerViewModel { + constructor(params, $element) { + super(params, $element); + //uiElement is required for this component. + if (!this.uiElement) { + throw new Error('The uiElement parameter is required for AmeAcSectionLink'); + } + this.targetElementId = AmeAcSection.getSectionElementId(this.uiElement); + } + getExpectedUiElementType() { + return Section; + } +} +export default createComponentConfig(AmeAcSectionLink, ` + +`); +//# sourceMappingURL=ame-ac-section-link.js.map \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-section-link.js.map b/extras/modules/admin-customizer/ko-components/ame-ac-section-link.js.map new file mode 100644 index 0000000..18b1242 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-section-link.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ame-ac-section-link.js","sourceRoot":"","sources":["ame-ac-section-link.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,EAErB,oBAAoB,EACpB,MAAM,0DAA0D,CAAC;AAClE,OAAO,EAAC,eAAe,EAAC,MAAM,mDAAmD,CAAC;AAClF,IAAO,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC;AACzC,OAAO,EAAC,YAAY,EAAC,MAAM,qBAAqB,CAAC;AAEjD,MAAM,gBAAiB,SAAQ,oBAA6B;IAG3D,YAAY,MAAyB,EAAE,QAAgB;QACtD,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACxB,2CAA2C;QAC3C,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACpB,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;SAC5E;QACD,IAAI,CAAC,eAAe,GAAG,YAAY,CAAC,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzE,CAAC;IAES,wBAAwB;QACjC,OAAO,OAAO,CAAC;IAChB,CAAC;CACD;AAED,eAAe,qBAAqB,CAAC,gBAAgB,EAAE;;;;CAItD,CAAC,CAAC"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-section-link.ts b/extras/modules/admin-customizer/ko-components/ame-ac-section-link.ts new file mode 100644 index 0000000..2f899ae --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-section-link.ts @@ -0,0 +1,31 @@ +import { + createComponentConfig, + KoComponentParams, + KoContainerViewModel +} from '../../../pro-customizables/ko-components/control-base.js'; +import {AmeCustomizable} from '../../../pro-customizables/assets/customizable.js'; +import Section = AmeCustomizable.Section; +import {AmeAcSection} from './ame-ac-section.js'; + +class AmeAcSectionLink extends KoContainerViewModel
    { + public readonly targetElementId: string; + + constructor(params: KoComponentParams, $element: JQuery) { + super(params, $element); + //uiElement is required for this component. + if (!this.uiElement) { + throw new Error('The uiElement parameter is required for AmeAcSectionLink'); + } + this.targetElementId = AmeAcSection.getSectionElementId(this.uiElement); + } + + protected getExpectedUiElementType(): Constructor | null { + return Section; + } +} + +export default createComponentConfig(AmeAcSectionLink, ` + +`); \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-section.js b/extras/modules/admin-customizer/ko-components/ame-ac-section.js new file mode 100644 index 0000000..1dce84b --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-section.js @@ -0,0 +1,106 @@ +import { ComponentBindingOptions, createComponentConfig, KoContainerViewModel } from '../../../pro-customizables/ko-components/control-base.js'; +import { AmeCustomizable } from '../../../pro-customizables/assets/customizable.js'; +var Section = AmeCustomizable.Section; +var Control = AmeCustomizable.Control; +var ControlGroup = AmeCustomizable.ControlGroup; +export class AmeAcSection extends KoContainerViewModel { + constructor(params, $element) { + super(params, $element); + //Must have an uiElement. + if (this.uiElement === null) { + throw new Error('AmeAcSection must have an uiElement.'); + } + this.elementId = AmeAcSection.getSectionElementId(this.uiElement); + if ((typeof params.breadcrumbs !== 'undefined') && ko.isObservable(params.breadcrumbs)) { + this.breadcrumbs = params.breadcrumbs; + } + else { + this.breadcrumbs = null; + } + //To keep the header text alignment consistent when navigating between sections, + //let's show something even if there are no breadcrumbs. + const emptyBreadcrumbText = 'Admin Menu Editor Pro'; + this.breadcrumbText = ko.pureComputed(() => { + if (this.breadcrumbs === null) { + return emptyBreadcrumbText; + } + const breadcrumbs = this.breadcrumbs(); + if (breadcrumbs.length < 1) { + return emptyBreadcrumbText; + } + let titles = breadcrumbs.map(crumb => crumb.title); + //Show the root section differently, "Admin Customizer" is too long. + //Not sure about what text to use here, could matching the Theme Customizer be confusing? + //Alternatives: 🛠️🎨, use \uFE0E to render the emoji without colors (only works for some). + //Alternatives: ⋯ and … + titles[0] = 'Customizing'; + //Due to space constraints, show only the last 2 breadcrumbs. + if (titles.length > 2) { + titles = titles.slice(titles.length - 2); + } + return titles.join(' \u25B8 '); + }); + } + getExpectedUiElementType() { + return Section; + } + mapChildToComponentBinding(child) { + if (child instanceof Section) { + if (child.preferredRole === 'content') { + return ComponentBindingOptions.fromElement(child, 'ame-ac-content-section'); + } + else { + return ComponentBindingOptions.fromElement(child, 'ame-ac-section-link'); + } + } + else if (child instanceof ControlGroup) { + return ComponentBindingOptions.fromElement(child, 'ame-ac-control-group'); + } + else if ((child instanceof Control) + && (['ame-ac-separator', 'ame-horizontal-separator'].indexOf(child.component) < 0)) { + //Wrap each control in a control group if it's not already in one. + //Separators are an exception because they're cosmetic and need different styling. + const controlGroup = child.createControlGroup(); + return this.mapChildToComponentBinding(controlGroup); + } + else { + return ComponentBindingOptions.fromElement(child); + } + } + static getSectionElementId(section) { + const prefix = 'ame-ac-section-'; + if (section.id) { + return prefix + section.id; + } + const slug = section.title.toLowerCase().replace(/[^a-z0-9]/g, '-'); + if (slug !== '') { + return prefix + slug; + } + throw new Error('Cannot generate a section element ID because the section does not have an ID or a title.'); + } + dispose() { + super.dispose(); + this.childComponents.dispose(); + } +} +export default createComponentConfig(AmeAcSection, ` +
      + + + + +
    +`); +//# sourceMappingURL=ame-ac-section.js.map \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-section.js.map b/extras/modules/admin-customizer/ko-components/ame-ac-section.js.map new file mode 100644 index 0000000..ced236d --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-section.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ame-ac-section.js","sourceRoot":"","sources":["ame-ac-section.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,uBAAuB,EACvB,qBAAqB,EAErB,oBAAoB,EACpB,MAAM,0DAA0D,CAAC;AAClE,OAAO,EAAC,eAAe,EAAC,MAAM,mDAAmD,CAAC;AAClF,IAAO,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC;AACzC,IAAO,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC;AACzC,IAAO,YAAY,GAAG,eAAe,CAAC,YAAY,CAAC;AAInD,MAAM,OAAO,YAAa,SAAQ,oBAA6B;IAK9D,YAAY,MAAyB,EAAE,QAAgB;QACtD,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACxB,yBAAyB;QACzB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE;YAC5B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;SACxD;QACD,IAAI,CAAC,SAAS,GAAG,YAAY,CAAC,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAElE,IAAI,CAAC,OAAO,MAAM,CAAC,WAAW,KAAK,WAAW,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE;YACvF,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;SACtC;aAAM;YACN,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;SACxB;QAED,gFAAgF;QAChF,wDAAwD;QACxD,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;QAEpD,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE;YAC1C,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE;gBAC9B,OAAO,mBAAmB,CAAC;aAC3B;YACD,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YACvC,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC3B,OAAO,mBAAmB,CAAC;aAC3B;YAED,IAAI,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAEnD,oEAAoE;YACpE,yFAAyF;YACzF,2FAA2F;YAC3F,uBAAuB;YACvB,MAAM,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC;YAE1B,6DAA6D;YAC7D,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;gBACtB,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;aACzC;YAED,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACJ,CAAC;IAES,wBAAwB;QACjC,OAAO,OAAO,CAAC;IAChB,CAAC;IAES,0BAA0B,CAAC,KAAgC;QACpE,IAAI,KAAK,YAAY,OAAO,EAAE;YAC7B,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS,EAAE;gBACtC,OAAO,uBAAuB,CAAC,WAAW,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAC;aAC5E;iBAAM;gBACN,OAAO,uBAAuB,CAAC,WAAW,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;aACzE;SACD;aAAM,IAAI,KAAK,YAAY,YAAY,EAAE;YACzC,OAAO,uBAAuB,CAAC,WAAW,CAAC,KAAK,EAAE,sBAAsB,CAAC,CAAC;SAC1E;aAAM,IACN,CAAC,KAAK,YAAY,OAAO,CAAC;eACvB,CAAC,CAAC,kBAAkB,EAAE,0BAA0B,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EACjF;YACD,kEAAkE;YAClE,kFAAkF;YAClF,MAAM,YAAY,GAAG,KAAK,CAAC,kBAAkB,EAAE,CAAC;YAChD,OAAO,IAAI,CAAC,0BAA0B,CAAC,YAAY,CAAC,CAAC;SACrD;aAAM;YACN,OAAO,uBAAuB,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;SAClD;IACF,CAAC;IAED,MAAM,CAAC,mBAAmB,CAAC,OAAgB;QAC1C,MAAM,MAAM,GAAG,iBAAiB,CAAC;QACjC,IAAI,OAAO,CAAC,EAAE,EAAE;YACf,OAAO,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;SAC3B;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QACpE,IAAI,IAAI,KAAK,EAAE,EAAE;YAChB,OAAO,MAAM,GAAG,IAAI,CAAC;SACrB;QACD,MAAM,IAAI,KAAK,CACd,0FAA0F,CAC1F,CAAC;IACH,CAAC;IAED,OAAO;QACN,KAAK,CAAC,OAAO,EAAE,CAAC;QAChB,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC;IAChC,CAAC;CACD;AAED,eAAe,qBAAqB,CAAC,YAAY,EAAE;;;;;;;;;;;;;;;;;;;CAmBlD,CAAC,CAAC"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-section.ts b/extras/modules/admin-customizer/ko-components/ame-ac-section.ts new file mode 100644 index 0000000..f01f393 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-section.ts @@ -0,0 +1,128 @@ +import { + ComponentBindingOptions, + createComponentConfig, + KoComponentParams, + KoContainerViewModel +} from '../../../pro-customizables/ko-components/control-base.js'; +import {AmeCustomizable} from '../../../pro-customizables/assets/customizable.js'; +import Section = AmeCustomizable.Section; +import Control = AmeCustomizable.Control; +import ControlGroup = AmeCustomizable.ControlGroup; +import {AmeAdminCustomizer} from '../admin-customizer'; +import NavigationBreadcrumb = AmeAdminCustomizer.NavigationBreadcrumb; + +export class AmeAcSection extends KoContainerViewModel
    { + public readonly elementId: string; + protected readonly breadcrumbs: KnockoutObservable | null; + public readonly breadcrumbText: KnockoutObservable; + + constructor(params: KoComponentParams, $element: JQuery) { + super(params, $element); + //Must have an uiElement. + if (this.uiElement === null) { + throw new Error('AmeAcSection must have an uiElement.'); + } + this.elementId = AmeAcSection.getSectionElementId(this.uiElement); + + if ((typeof params.breadcrumbs !== 'undefined') && ko.isObservable(params.breadcrumbs)) { + this.breadcrumbs = params.breadcrumbs; + } else { + this.breadcrumbs = null; + } + + //To keep the header text alignment consistent when navigating between sections, + //let's show something even if there are no breadcrumbs. + const emptyBreadcrumbText = 'Admin Menu Editor Pro'; + + this.breadcrumbText = ko.pureComputed(() => { + if (this.breadcrumbs === null) { + return emptyBreadcrumbText; + } + const breadcrumbs = this.breadcrumbs(); + if (breadcrumbs.length < 1) { + return emptyBreadcrumbText; + } + + let titles = breadcrumbs.map(crumb => crumb.title); + + //Show the root section differently, "Admin Customizer" is too long. + //Not sure about what text to use here, could matching the Theme Customizer be confusing? + //Alternatives: 🛠️🎨, use \uFE0E to render the emoji without colors (only works for some). + //Alternatives: ⋯ and … + titles[0] = 'Customizing'; + + //Due to space constraints, show only the last 2 breadcrumbs. + if (titles.length > 2) { + titles = titles.slice(titles.length - 2); + } + + return titles.join(' \u25B8 '); + }); + } + + protected getExpectedUiElementType(): Constructor | null { + return Section; + } + + protected mapChildToComponentBinding(child: AmeCustomizable.UiElement): ComponentBindingOptions | null { + if (child instanceof Section) { + if (child.preferredRole === 'content') { + return ComponentBindingOptions.fromElement(child, 'ame-ac-content-section'); + } else { + return ComponentBindingOptions.fromElement(child, 'ame-ac-section-link'); + } + } else if (child instanceof ControlGroup) { + return ComponentBindingOptions.fromElement(child, 'ame-ac-control-group'); + } else if ( + (child instanceof Control) + && (['ame-ac-separator', 'ame-horizontal-separator'].indexOf(child.component) < 0) + ) { + //Wrap each control in a control group if it's not already in one. + //Separators are an exception because they're cosmetic and need different styling. + const controlGroup = child.createControlGroup(); + return this.mapChildToComponentBinding(controlGroup); + } else { + return ComponentBindingOptions.fromElement(child); + } + } + + static getSectionElementId(section: Section): string { + const prefix = 'ame-ac-section-'; + if (section.id) { + return prefix + section.id; + } + const slug = section.title.toLowerCase().replace(/[^a-z0-9]/g, '-'); + if (slug !== '') { + return prefix + slug; + } + throw new Error( + 'Cannot generate a section element ID because the section does not have an ID or a title.' + ); + } + + dispose() { + super.dispose(); + this.childComponents.dispose(); + } +} + +export default createComponentConfig(AmeAcSection, ` +
      + + + + +
    +`); \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-separator.js b/extras/modules/admin-customizer/ko-components/ame-ac-separator.js new file mode 100644 index 0000000..f0fe74d --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-separator.js @@ -0,0 +1,10 @@ +import { createComponentConfig, KoStandaloneControl } from '../../../pro-customizables/ko-components/control-base.js'; +class AmeAcSeparator extends KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + } +} +export default createComponentConfig(AmeAcSeparator, ` +
  • +`); +//# sourceMappingURL=ame-ac-separator.js.map \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-separator.js.map b/extras/modules/admin-customizer/ko-components/ame-ac-separator.js.map new file mode 100644 index 0000000..3fb6efa --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-separator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ame-ac-separator.js","sourceRoot":"","sources":["ame-ac-separator.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,EAErB,mBAAmB,EACnB,MAAM,0DAA0D,CAAC;AAElE,MAAM,cAAe,SAAQ,mBAAmB;IAE/C,YAAY,MAAyB,EAAE,QAAgB;QACtD,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACzB,CAAC;CACD;AAED,eAAe,qBAAqB,CAAC,cAAc,EAAE;;CAEpD,CAAC,CAAC"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-separator.ts b/extras/modules/admin-customizer/ko-components/ame-ac-separator.ts new file mode 100644 index 0000000..3b75183 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-separator.ts @@ -0,0 +1,16 @@ +import { + createComponentConfig, + KoComponentParams, + KoStandaloneControl +} from '../../../pro-customizables/ko-components/control-base.js'; + +class AmeAcSeparator extends KoStandaloneControl { + + constructor(params: KoComponentParams, $element: JQuery) { + super(params, $element); + } +} + +export default createComponentConfig(AmeAcSeparator, ` +
  • +`); \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-structure.js b/extras/modules/admin-customizer/ko-components/ame-ac-structure.js new file mode 100644 index 0000000..fc2b9b3 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-structure.js @@ -0,0 +1,38 @@ +import { createRendererComponentConfig, KoRendererViewModel } from '../../../pro-customizables/ko-components/control-base.js'; +import { AmeCustomizable } from '../../../pro-customizables/assets/customizable.js'; +var Section = AmeCustomizable.Section; +class AmeAcStructure extends KoRendererViewModel { + constructor(params, $element) { + var _a; + super(params, $element); + this.allSections = []; + const rootSection = new Section({ + t: 'section', + id: 'structure-root', + title: (_a = this.structure.title) !== null && _a !== void 0 ? _a : 'Root', + }, this.structure.children); + //Recursively collect all sections. + function collectChildSections(section, accumulator = []) { + accumulator.push(section); + for (const child of section.children) { + if (child instanceof Section) { + collectChildSections(child, accumulator); + } + } + return accumulator; + } + this.allSections = collectChildSections(rootSection); + //Give the breadcrumb list to each section, if available. + if (typeof params.breadcrumbs !== 'undefined') { + for (const section of this.allSections) { + section.componentParams.breadcrumbs = params.breadcrumbs; + } + } + } +} +export default createRendererComponentConfig(AmeAcStructure, ` + + + +`); +//# sourceMappingURL=ame-ac-structure.js.map \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-structure.js.map b/extras/modules/admin-customizer/ko-components/ame-ac-structure.js.map new file mode 100644 index 0000000..9b28119 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-structure.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ame-ac-structure.js","sourceRoot":"","sources":["ame-ac-structure.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,6BAA6B,EAE7B,mBAAmB,EACnB,MAAM,0DAA0D,CAAC;AAClE,OAAO,EAAC,eAAe,EAAC,MAAM,mDAAmD,CAAC;AAClF,IAAO,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC;AAEzC,MAAM,cAAe,SAAQ,mBAAmB;IAG/C,YAAY,MAAyB,EAAE,QAAgB;;QACtD,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAHT,gBAAW,GAAc,EAAE,CAAC;QAK3C,MAAM,WAAW,GAAG,IAAI,OAAO,CAC9B;YACC,CAAC,EAAE,SAAS;YACZ,EAAE,EAAE,gBAAgB;YACpB,KAAK,EAAE,MAAA,IAAI,CAAC,SAAS,CAAC,KAAK,mCAAI,MAAM;SACrC,EACD,IAAI,CAAC,SAAS,CAAC,QAAQ,CACvB,CAAC;QAEF,mCAAmC;QACnC,SAAS,oBAAoB,CAAC,OAAgB,EAAE,cAAyB,EAAE;YAC1E,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,EAAE;gBACrC,IAAI,KAAK,YAAY,OAAO,EAAE;oBAC7B,oBAAoB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;iBACzC;aACD;YACD,OAAO,WAAW,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,WAAW,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;QAErD,yDAAyD;QACzD,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,WAAW,EAAE;YAC9C,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE;gBACvC,OAAO,CAAC,eAAe,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;aACzD;SACD;IACF,CAAC;CACD;AAED,eAAe,6BAA6B,CAAC,cAAc,EAAE;;;;CAI5D,CAAC,CAAC"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-structure.ts b/extras/modules/admin-customizer/ko-components/ame-ac-structure.ts new file mode 100644 index 0000000..82e556c --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-structure.ts @@ -0,0 +1,50 @@ +import { + createRendererComponentConfig, + KoComponentParams, + KoRendererViewModel +} from '../../../pro-customizables/ko-components/control-base.js'; +import {AmeCustomizable} from '../../../pro-customizables/assets/customizable.js'; +import Section = AmeCustomizable.Section; + +class AmeAcStructure extends KoRendererViewModel { + public readonly allSections: Section[] = []; + + constructor(params: KoComponentParams, $element: JQuery) { + super(params, $element); + + const rootSection = new Section( + { + t: 'section', + id: 'structure-root', + title: this.structure.title ?? 'Root', + }, + this.structure.children + ); + + //Recursively collect all sections. + function collectChildSections(section: Section, accumulator: Section[] = []) { + accumulator.push(section); + for (const child of section.children) { + if (child instanceof Section) { + collectChildSections(child, accumulator); + } + } + return accumulator; + } + + this.allSections = collectChildSections(rootSection); + + //Give the breadcrumb list to each section, if available. + if (typeof params.breadcrumbs !== 'undefined') { + for (const section of this.allSections) { + section.componentParams.breadcrumbs = params.breadcrumbs; + } + } + } +} + +export default createRendererComponentConfig(AmeAcStructure, ` + + + +`); \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js b/extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js new file mode 100644 index 0000000..124bd4b --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js @@ -0,0 +1,29 @@ +import { createComponentConfig, KoStandaloneControl } from '../../../pro-customizables/ko-components/control-base.js'; +class AmeAcValidationErrors extends KoStandaloneControl { + constructor(params, $element) { + super(params, $element); + if (typeof params.errors !== 'undefined') { + if (Array.isArray(params.errors)) { + this.errors = params.errors; + } + else if (ko.isObservable(params.errors)) { + this.errors = params.errors; + } + else { + throw new Error('The "errors" parameter must be an array or an observable array.'); + } + } + else { + console.log('Params:', params); + throw new Error('The "errors" parameter is required for the AmeAcValidationErrors component.'); + } + } +} +export default createComponentConfig(AmeAcValidationErrors, ` +
      +
    • + +
    • +
    +`); +//# sourceMappingURL=ame-ac-validation-errors.js.map \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js.map b/extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js.map new file mode 100644 index 0000000..688fe10 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ame-ac-validation-errors.js","sourceRoot":"","sources":["ame-ac-validation-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,EAErB,mBAAmB,EACnB,MAAM,0DAA0D,CAAC;AAIlE,MAAM,qBAAsB,SAAQ,mBAAmB;IAGtD,YAAmB,MAAyB,EAAE,QAAgB;QAC7D,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAExB,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE;YACzC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;gBACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;aAC5B;iBAAM,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;gBAC1C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAqD,CAAC;aAC3E;iBAAM;gBACN,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;aACnF;SACD;aAAM;YACN,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;SAC/F;IACF,CAAC;CACD;AAED,eAAe,qBAAqB,CAAC,qBAAqB,EAAE;;;;;;CAM3D,CAAC,CAAC"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.ts b/extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.ts new file mode 100644 index 0000000..58c90e7 --- /dev/null +++ b/extras/modules/admin-customizer/ko-components/ame-ac-validation-errors.ts @@ -0,0 +1,36 @@ +import { + createComponentConfig, + KoComponentParams, + KoStandaloneControl +} from '../../../pro-customizables/ko-components/control-base.js'; +import {AmeCustomizable} from '../../../pro-customizables/assets/customizable.js'; +import ValidationErrorWithId = AmeCustomizable.ValidationErrorWithId; + +class AmeAcValidationErrors extends KoStandaloneControl { + public readonly errors: ValidationErrorWithId[] | KnockoutObservable; + + public constructor(params: KoComponentParams, $element: JQuery,) { + super(params, $element); + + if (typeof params.errors !== 'undefined') { + if (Array.isArray(params.errors)) { + this.errors = params.errors; + } else if (ko.isObservable(params.errors)) { + this.errors = params.errors as KnockoutObservable; + } else { + throw new Error('The "errors" parameter must be an array or an observable array.'); + } + } else { + console.log('Params:', params); + throw new Error('The "errors" parameter is required for the AmeAcValidationErrors component.'); + } + } +} + +export default createComponentConfig(AmeAcValidationErrors, ` +
      +
    • + +
    • +
    +`); \ No newline at end of file diff --git a/extras/modules/admin-customizer/preview-handler.js b/extras/modules/admin-customizer/preview-handler.js new file mode 100644 index 0000000..028d39c --- /dev/null +++ b/extras/modules/admin-customizer/preview-handler.js @@ -0,0 +1,143 @@ +'use strict'; +import { AmeCustomizable } from '../../pro-customizables/assets/customizable.js'; +import { AmeAdminCustomizerBase } from './admin-customizer-base.js'; +import { AmeStyleGenerator } from '../../style-generator/style-generator.js'; +//Compatibility note: This script is not compatible with IE11 because it uses some +//modern JS features like the URLSearchParams class. +var AmeAdminCustomizerPreview; +(function (AmeAdminCustomizerPreview) { + var ThrottledPreviewRegistry = AmeCustomizable.ThrottledPreviewRegistry; + const $ = jQuery; + class PreviewHandler extends AmeAdminCustomizerBase.AdminCustomizerBase { + constructor(scriptData) { + super(scriptData); + this.currentPreviewValues = {}; + this.changesetName = scriptData.changesetName; + this.previewRegistry = new ThrottledPreviewRegistry((settingId, defaultResult) => { + if (this.currentPreviewValues.hasOwnProperty(settingId)) { + return this.currentPreviewValues[settingId]; + } + //Try the script data. It should have the current value from the changeset. + if (scriptData.settings.hasOwnProperty(settingId) + && scriptData.settings[settingId].hasOwnProperty('value')) { + return scriptData.settings[settingId].value; + } + return defaultResult; + }); + this.connection = AmeAcCommunicator.connectToParent({ + 'previewSetting': (settingId, value) => { + this.currentPreviewValues[settingId] = value; + if (!this.previewRegistry.canPreview(settingId)) { + return false; + } + this.previewRegistry.queuePreview(settingId); + return true; + }, + 'getCurrentUrl': () => { + return window.location.href; + } + }, scriptData.allowedCommOrigins, scriptData.isWpDebugEnabled); + this.connection.promise.then((c) => { + if (typeof c === 'undefined') { + if (console && console.warn) { + console.warn('Connection succeeded, but the communicator is undefined. This should be impossible.'); + } + return; //This should never happen. + } + //Let the parent know the current URL. The parent might not be able to + //read it due to cross-domain restrictions, and if there are any redirects, + //the actual URL might not match the frame src that was set by the parent. + c.execute('notifyPreviewUrlChanged', window.location.href); + }); + $(() => { + this.addPreviewParamsToLinks(); + //Handle clicks on links. + $(document.body).on('click.ame-ac-preview', 'a', (event) => { + return this.handleLinkClick(event); + }); + //Block form submissions. Theme Customizer supports those, but we don't + //(at least for now). + $(document.body).on('submit.ame-ac-preview', 'form', function (event) { + event.preventDefault(); + }); + }); + //For convenience, support for StyleGenerator previews is built-in. + for (const previewConfig of (scriptData.stylePreviewConfigs || [])) { + const previewInstance = new AmeStyleGenerator.Preview.StyleGeneratorPreview(previewConfig); + this.previewRegistry.registerPreviewUpdater(previewInstance.getPreviewableSettingIDs(), previewInstance); + } + } + /** + * Add preview-specific query parameters to all links. + */ + addPreviewParamsToLinks() { + const self = this; + $('a[href]').each(function () { + const element = this; + if (!(element instanceof HTMLAnchorElement)) { + return; + } + const $link = $(this); + //Don't modify internal anchors like "#abc". + if (self.isInternalAnchor($link)) { + return; + } + //Flag and skip non-previewable links. + if (!self.isPreviewableLink(element)) { + $link.addClass('ame-ac-not-previewable'); + return; + } + //Add the preview query parameter(s). + const params = new URLSearchParams(element.search); + params.set('ame-ac-preview', '1'); + params.set('ame-ac-changeset', self.changesetName); + element.search = '?' + params.toString(); + }); + } + isPreviewableLink(element) { + return this.isPreviewableUrl(element); + } + isInternalAnchor($link) { + const href = $link.attr('href'); + if (typeof href === 'undefined') { + return false; + } + return (href.substring(0, 1) === '#'); + } + handleLinkClick(event) { + const $link = $(event.target).closest('a'); + //Let anchors work as normal. + if (this.isInternalAnchor($link)) { + return; + } + //Prevent the browser from navigating to non-previewable links. + const anchorElement = $link.get(0); + if (!this.isPreviewableLink(anchorElement)) { + event.preventDefault(); + return; + } + //Tell the parent (i.e. the admin customizer) to load the link. + if (this.connection.isConnected) { + event.preventDefault(); + this.connection.execute('setPreviewUrl', anchorElement.href); + } + } + // noinspection JSUnusedGlobalSymbols Used in other modules. + registerPreviewer(settingId, callback) { + this.previewRegistry.registerPreviewCallback(settingId, callback); + } + // noinspection JSUnusedGlobalSymbols Also used in other modules. + registerPreviewUpdater(settingIds, updater) { + this.previewRegistry.registerPreviewUpdater(settingIds, updater); + } + // noinspection JSUnusedGlobalSymbols + registerRpcMethod(methodName, handler) { + this.connection.addRpcMethod(methodName, handler); + } + } + AmeAdminCustomizerPreview.PreviewHandler = PreviewHandler; + const previewHandler = new PreviewHandler(wsAmeAcPreviewData); + window['wsAdminCustomizerPreview'] = previewHandler; + $('body').trigger('adminMenuEditor:acPreviewStart', [previewHandler]); +})(AmeAdminCustomizerPreview || (AmeAdminCustomizerPreview = {})); +//# sourceMappingURL=preview-handler.js.map \ No newline at end of file diff --git a/extras/modules/admin-customizer/preview-handler.js.map b/extras/modules/admin-customizer/preview-handler.js.map new file mode 100644 index 0000000..7bb7b7f --- /dev/null +++ b/extras/modules/admin-customizer/preview-handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"preview-handler.js","sourceRoot":"","sources":["preview-handler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/preview-handler.ts b/extras/modules/admin-customizer/preview-handler.ts new file mode 100644 index 0000000..f1dff82 --- /dev/null +++ b/extras/modules/admin-customizer/preview-handler.ts @@ -0,0 +1,196 @@ +'use strict'; + +import {AmeCustomizable} from '../../pro-customizables/assets/customizable.js'; +import {AmeAdminCustomizerBase} from './admin-customizer-base.js'; +import {AmeStyleGenerator} from '../../style-generator/style-generator.js'; + +declare var wsAmeAcPreviewData: AmeAdminCustomizerPreview.PreviewScriptData; + +//Compatibility note: This script is not compatible with IE11 because it uses some +//modern JS features like the URLSearchParams class. + +namespace AmeAdminCustomizerPreview { + import ThrottledPreviewRegistry = AmeCustomizable.ThrottledPreviewRegistry; + const $ = jQuery; + + export interface PreviewScriptData extends AmeAdminCustomizerBase.ScriptData { + stylePreviewConfigs?: AmeStyleGenerator.Preview.StyleGeneratorPreviewConfig[]; + } + + export class PreviewHandler extends AmeAdminCustomizerBase.AdminCustomizerBase { + private readonly changesetName: string; + private readonly connection: ReturnType; + + private readonly previewRegistry: ThrottledPreviewRegistry; + private readonly currentPreviewValues: Record = {}; + + constructor(scriptData: PreviewScriptData) { + super(scriptData); + this.changesetName = scriptData.changesetName; + + this.previewRegistry = new ThrottledPreviewRegistry( + (settingId: string, defaultResult: any) => { + if (this.currentPreviewValues.hasOwnProperty(settingId)) { + return this.currentPreviewValues[settingId]; + } + //Try the script data. It should have the current value from the changeset. + if ( + scriptData.settings.hasOwnProperty(settingId) + && scriptData.settings[settingId].hasOwnProperty('value') + ) { + return scriptData.settings[settingId].value; + } + return defaultResult; + } + ); + + this.connection = AmeAcCommunicator.connectToParent( + { + 'previewSetting': (settingId: string, value: any) => { + this.currentPreviewValues[settingId] = value; + + if (!this.previewRegistry.canPreview(settingId)) { + return false; + } + this.previewRegistry.queuePreview(settingId); + return true; + }, + 'getCurrentUrl': () => { + return window.location.href; + } + }, + scriptData.allowedCommOrigins, + scriptData.isWpDebugEnabled + ); + + this.connection.promise.then((c) => { + if (typeof c === 'undefined') { + if (console && console.warn) { + console.warn('Connection succeeded, but the communicator is undefined. This should be impossible.'); + } + return; //This should never happen. + } + + //Let the parent know the current URL. The parent might not be able to + //read it due to cross-domain restrictions, and if there are any redirects, + //the actual URL might not match the frame src that was set by the parent. + c.execute('notifyPreviewUrlChanged', window.location.href); + }); + + $(() => { + this.addPreviewParamsToLinks(); + + //Handle clicks on links. + $(document.body).on('click.ame-ac-preview', 'a', (event) => { + return this.handleLinkClick(event); + }); + + //Block form submissions. Theme Customizer supports those, but we don't + //(at least for now). + $(document.body).on('submit.ame-ac-preview', 'form', function (event) { + event.preventDefault(); + }); + }); + + //For convenience, support for StyleGenerator previews is built-in. + for (const previewConfig of (scriptData.stylePreviewConfigs || [])) { + const previewInstance = new AmeStyleGenerator.Preview.StyleGeneratorPreview(previewConfig); + this.previewRegistry.registerPreviewUpdater( + previewInstance.getPreviewableSettingIDs(), + previewInstance + ); + } + } + + /** + * Add preview-specific query parameters to all links. + */ + addPreviewParamsToLinks() { + const self = this; + $('a[href]').each(function (this: HTMLElement) { + const element = this; + if (!(element instanceof HTMLAnchorElement)) { + return; + } + const $link = $(this); + + //Don't modify internal anchors like "#abc". + if (self.isInternalAnchor($link)) { + return; + } + + //Flag and skip non-previewable links. + if (!self.isPreviewableLink(element)) { + $link.addClass('ame-ac-not-previewable'); + return; + } + + //Add the preview query parameter(s). + const params = new URLSearchParams(element.search); + params.set('ame-ac-preview', '1'); + params.set('ame-ac-changeset', self.changesetName); + element.search = '?' + params.toString(); + }); + } + + isPreviewableLink(element: HTMLAnchorElement): boolean { + return this.isPreviewableUrl(element); + } + + isInternalAnchor($link: JQuery): boolean { + const href = $link.attr('href'); + if (typeof href === 'undefined') { + return false; + } + return (href.substring(0, 1) === '#'); + } + + handleLinkClick(event: JQueryEventObject) { + const $link = $(event.target).closest('a'); + + //Let anchors work as normal. + if (this.isInternalAnchor($link)) { + return; + } + + //Prevent the browser from navigating to non-previewable links. + const anchorElement = $link.get(0) as HTMLAnchorElement; + if (!this.isPreviewableLink(anchorElement)) { + event.preventDefault(); + return; + } + + //Tell the parent (i.e. the admin customizer) to load the link. + if (this.connection.isConnected) { + event.preventDefault(); + this.connection.execute('setPreviewUrl', anchorElement.href); + } + } + + // noinspection JSUnusedGlobalSymbols Used in other modules. + registerPreviewer(settingId: string, callback: (newValue: any) => void) { + this.previewRegistry.registerPreviewCallback(settingId, callback); + } + + // noinspection JSUnusedGlobalSymbols Also used in other modules. + registerPreviewUpdater(settingIds: string[], updater: AmeCustomizable.PreviewUpdater) { + this.previewRegistry.registerPreviewUpdater(settingIds, updater); + } + + // noinspection JSUnusedGlobalSymbols + registerRpcMethod(methodName: string, handler: (...args: any) => any) { + this.connection.addRpcMethod(methodName, handler); + } + } + + const previewHandler = new PreviewHandler(wsAmeAcPreviewData); + window['wsAdminCustomizerPreview'] = previewHandler; + + $('body').trigger('adminMenuEditor:acPreviewStart', [previewHandler]); +} + +declare global { + interface Window { + wsAdminCustomizerPreview: AmeAdminCustomizerPreview.PreviewHandler; + } +} \ No newline at end of file diff --git a/extras/modules/admin-customizer/preview.css b/extras/modules/admin-customizer/preview.css new file mode 100644 index 0000000..c8fc5c2 --- /dev/null +++ b/extras/modules/admin-customizer/preview.css @@ -0,0 +1,5 @@ +.ame-ac-not-previewable { + cursor: not-allowed; +} + +/*# sourceMappingURL=preview.css.map */ diff --git a/extras/modules/admin-customizer/preview.css.map b/extras/modules/admin-customizer/preview.css.map new file mode 100644 index 0000000..14ffef1 --- /dev/null +++ b/extras/modules/admin-customizer/preview.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["preview.scss"],"names":[],"mappings":"AAAA;EACC","file":"preview.css"} \ No newline at end of file diff --git a/extras/modules/admin-customizer/preview.scss b/extras/modules/admin-customizer/preview.scss new file mode 100644 index 0000000..30d5c98 --- /dev/null +++ b/extras/modules/admin-customizer/preview.scss @@ -0,0 +1,3 @@ +.ame-ac-not-previewable { + cursor: not-allowed; +} \ No newline at end of file diff --git a/extras/modules/admin-menu-colors/admin-menu-colors.js b/extras/modules/admin-menu-colors/admin-menu-colors.js new file mode 100644 index 0000000..53fb0d2 --- /dev/null +++ b/extras/modules/admin-menu-colors/admin-menu-colors.js @@ -0,0 +1,408 @@ +/// +import { AmeCustomizableViewModel } from '../../pro-customizables/assets/customizable.js'; +jQuery(function ($) { + class PresetDropdownControl { + constructor($control, initialPresets = {}) { + this.isActive = true; + this.areColorEventsIgnored = false; + this.currentPresets = {}; + this.isGlobalPresetVisible = true; + this.$control = $control; + this.currentPresets = initialPresets; + this.$mySection = $control.closest('.ame-mc-color-section'); + this.$presetSelect = $control.find('select').first(); + this.$presetSelect.on('change', this.onPresetChange.bind(this)); + this.$deleteButton = $control.find('.ame-mc-delete-color-preset').first(); + const globalVisibilityData = this.$control.data('global-preset-visible'); + if (typeof globalVisibilityData !== 'undefined') { + this.isGlobalPresetVisible = (globalVisibilityData == 1); //Loose comparison is intentional. + } + //When the user changes any of the colors, deselect the current preset. + const $colorInputs = this.getAssociatedColorInputs(); + $colorInputs.on('adminMenuEditor:colorPickerChange', () => { + if (this.areColorEventsIgnored) { + return; + } + this.deselectPresets(); + }); + this.$deleteButton.on('click', (event) => { + event.preventDefault(); + const presetName = this.$presetSelect.val(); + const targetPreset = this.getPresetByName(presetName); + if (!targetPreset || (presetName === '[global]') || (presetName === '[save_preset]')) { + return; + } + if (!confirm('Are you sure you want to delete the preset "' + presetName + '"?')) { + return false; + } + this.deletePreset(presetName); + this.deselectPresets(); + }); + this.$control.on('adminMenuEditor:observableValueChanged adminMenuEditor:observableBindingInit', (event, data) => { + this.currentPresets = data || {}; + this.populateSelect(this.currentPresets); + }); + //Initialize the dropdown. + this.populateSelect(this.getPresetCollection()); + } + onPresetChange() { + if (!this.isActive) { + return; //Ignore changes while inactive, e.g. when the dialog is closed. + } + const presetName = this.$presetSelect.val(); + //Show the "Delete" button only if the user selected a custom preset. + //The global preset cannot be deleted. + const metaPresets = ['[save_preset]', '[separator1]', '[global]', '']; + this.$deleteButton.toggle((metaPresets.indexOf(presetName) < 0) && (presetName !== null)); + if (presetName === '[save_preset]') { + //Create a new preset. + const colors = this.getCurrentColors(); + if (colors === null) { + this.deselectPresets(); + alert('Error: No colors selected'); + return; + } + const newPresetName = window.prompt('New preset name:', ''); + if ((newPresetName === null) || ((newPresetName.trim()) === '')) { + this.deselectPresets(); + return; + } + this.storePreset(newPresetName, colors); + this.selectPresetByName(newPresetName); + } + else if (presetName !== '') { + //Apply the selected preset. + const preset = this.getPresetByName(presetName); + if (preset) { + this.displayPresetColors(preset); + } + } + } + populateSelect(presets) { + //Remember the current value. + const currentValue = this.$presetSelect.val(); + //Clear the select. + this.$presetSelect.empty(); + //The first option is a placeholder that just says "Select a preset". + this.$presetSelect.append($(''); + $option.val(presetName); + $option.text(presetName); + this.$presetSelect.append($option); + } + //Add a separator between the presets and the option that adds a new preset. + this.$presetSelect.append($('
    '; + + //Enqueue our script if not already done. + wp_enqueue_script(MenuColorsModule::mainScriptHandle); + } +} \ No newline at end of file diff --git a/extras/modules/admin-menu-colors/admin-menu-colors.ts b/extras/modules/admin-menu-colors/admin-menu-colors.ts new file mode 100644 index 0000000..381d257 --- /dev/null +++ b/extras/modules/admin-menu-colors/admin-menu-colors.ts @@ -0,0 +1,519 @@ +/// + +import {AmeCustomizableViewModel} from '../../pro-customizables/assets/customizable.js'; + +jQuery(function ($: JQueryStatic) { + interface ColorPresetCollection { + [name: string]: ColorPreset; + } + + interface ColorPreset { + [colorVariable: string]: string; + } + + class PresetDropdownControl { + private $control: JQuery; + private $presetSelect: JQuery; + private $deleteButton: JQuery; + private $mySection: JQuery; + + public isActive: boolean = true; + + private areColorEventsIgnored: boolean = false; + + private currentPresets: ColorPresetCollection = {}; + private readonly isGlobalPresetVisible: boolean = true; + + constructor($control: JQuery, initialPresets: ColorPresetCollection = {}) { + this.$control = $control; + this.currentPresets = initialPresets; + this.$mySection = $control.closest('.ame-mc-color-section'); + + this.$presetSelect = $control.find('select').first(); + this.$presetSelect.on('change', this.onPresetChange.bind(this)); + + this.$deleteButton = $control.find('.ame-mc-delete-color-preset').first(); + + const globalVisibilityData = this.$control.data('global-preset-visible'); + if (typeof globalVisibilityData !== 'undefined') { + this.isGlobalPresetVisible = (globalVisibilityData == 1); //Loose comparison is intentional. + } + + //When the user changes any of the colors, deselect the current preset. + const $colorInputs = this.getAssociatedColorInputs(); + $colorInputs.on('adminMenuEditor:colorPickerChange', () => { + if (this.areColorEventsIgnored) { + return; + } + this.deselectPresets(); + }); + + this.$deleteButton.on('click', (event) => { + event.preventDefault(); + + const presetName = this.$presetSelect.val(); + const targetPreset = this.getPresetByName(presetName); + if (!targetPreset || (presetName === '[global]') || (presetName === '[save_preset]')) { + return; + } + + if (!confirm('Are you sure you want to delete the preset "' + presetName + '"?')) { + return false; + } + + this.deletePreset(presetName); + this.deselectPresets(); + }); + + this.$control.on( + 'adminMenuEditor:observableValueChanged adminMenuEditor:observableBindingInit', + (event, data: ColorPresetCollection) => { + this.currentPresets = data || {}; + this.populateSelect(this.currentPresets); + } + ); + + //Initialize the dropdown. + this.populateSelect(this.getPresetCollection()); + } + + onPresetChange() { + if (!this.isActive) { + return; //Ignore changes while inactive, e.g. when the dialog is closed. + } + + const presetName = this.$presetSelect.val(); + + //Show the "Delete" button only if the user selected a custom preset. + //The global preset cannot be deleted. + const metaPresets = ['[save_preset]', '[separator1]', '[global]', '']; + this.$deleteButton.toggle( + (metaPresets.indexOf(presetName) < 0) && (presetName !== null) + ); + + if (presetName === '[save_preset]') { + //Create a new preset. + const colors = this.getCurrentColors(); + if (colors === null) { + this.deselectPresets(); + alert('Error: No colors selected'); + return; + } + + const newPresetName = window.prompt('New preset name:', ''); + if ((newPresetName === null) || ((newPresetName.trim()) === '')) { + this.deselectPresets(); + return; + } + + this.storePreset(newPresetName, colors); + this.selectPresetByName(newPresetName); + + } else if (presetName !== '') { + //Apply the selected preset. + const preset = this.getPresetByName(presetName); + if (preset) { + this.displayPresetColors(preset); + } + } + } + + protected populateSelect(presets: ColorPresetCollection) { + //Remember the current value. + const currentValue = this.$presetSelect.val(); + //Clear the select. + this.$presetSelect.empty(); + + //The first option is a placeholder that just says "Select a preset". + this.$presetSelect.append($(''); + $option.val(presetName); + $option.text(presetName); + this.$presetSelect.append($option); + } + + //Add a separator between the presets and the option that adds a new preset. + this.$presetSelect.append($(''; + $rss->__destruct(); + unset($rss); } } \ No newline at end of file diff --git a/extras/modules/dashboard-widget-editor/ameDashboardWidget.php b/extras/modules/dashboard-widget-editor/ameDashboardWidget.php index 52647c0..53a3d30 100644 --- a/extras/modules/dashboard-widget-editor/ameDashboardWidget.php +++ b/extras/modules/dashboard-widget-editor/ameDashboardWidget.php @@ -114,6 +114,10 @@ public function getLocation() { return $this->location; } + public function getOriginalLocation() { + return $this->getLocation(); + } + public function getPriority() { return $this->priority; } @@ -122,6 +126,10 @@ public function getCallback() { return $this->callback; } + public function getCallbackArgs() { + return $this->callbackArgs; + } + public function getGrantAccess() { return $this->grantAccess; } @@ -213,16 +221,20 @@ public static function userCanAccess($user, $grantAccess, $menuEditor = null) { /** * Register this widget with WordPress. + * + * @param bool $overrideOrder Whether to use the custom widget order settings if available. */ - public function addToDashboard() { + public function addToDashboard($overrideOrder = false) { add_meta_box( $this->getId(), $this->getTitle(), $this->getCallback(), 'dashboard', - $this->getLocation(), - $this->getPriority(), - $this->callbackArgs + $overrideOrder ? $this->getLocation() : $this->getOriginalLocation(), + //To make widgets appear in the order they're added, + //use the same priority for all widgets. + $overrideOrder ? 'default' : $this->getPriority(), + $this->getCallbackArgs() ); } } diff --git a/extras/modules/dashboard-widget-editor/ameStandardWidgetWrapper.php b/extras/modules/dashboard-widget-editor/ameStandardWidgetWrapper.php index 47bffd2..2ca0e46 100644 --- a/extras/modules/dashboard-widget-editor/ameStandardWidgetWrapper.php +++ b/extras/modules/dashboard-widget-editor/ameStandardWidgetWrapper.php @@ -10,6 +10,9 @@ class ameStandardWidgetWrapper extends ameDashboardWidget { */ private $wrappedWidget; + /** + * @var bool + */ private $wasPresent = true; private $callbackFileName = null; @@ -89,7 +92,11 @@ public function copyWrappedWidgetFrom($otherWidget) { } private function updateCallbackFileName() { - $reflection = new AmeReflectionCallable($this->callback); + try { + $reflection = new AmeReflectionCallable($this->callback); + } catch (ReflectionException $e) { + return false; + } $fileName = $reflection->getFileName(); if ($fileName === false) { @@ -115,6 +122,13 @@ public function getLocation() { return $this->getProperty('location'); } + public function getOriginalLocation() { + if ( isset($this->wrappedWidget['location']) ) { + return $this->wrappedWidget['location']; + } + return $this->getLocation(); + } + public function getPriority() { return $this->getProperty('priority'); } @@ -126,6 +140,34 @@ private function getProperty($name) { return $this->wrappedWidget[$name]; } + public function getCallbackArgs() { + /* + * Subtle detail: WordPress stores a copy of the original widget title in the callback + * argument array, under the key "__widget_basename". This is used as the widget name + * in the "Screen Options" tab and in a couple of other UI labels/tooltips. It seems + * that the idea is to have a "nice" name for widgets where the full title also contains + * things like a "Configure" link, etc. + * + * If the user has set a custom title, we need to replace the "__widget_basename" + * value to make the custom title appear in the "Screen Options" tab. + */ + $callbackArgs = $this->callbackArgs; + if ( + //Does the widget have a basename (original title)? + is_array($callbackArgs) + && isset($callbackArgs['__widget_basename']) + //Has the user set a custom title? + && isset($this->wrappedWidget['title']) + && ($this->getTitle() !== $this->wrappedWidget['title']) + ) { + //Also change the basename. + $callbackArgs['__widget_basename'] = $this->getTitle(); + return $callbackArgs; + } + + return parent::getCallbackArgs(); + } + public function isPresent() { return $this->wasPresent || $this->hasValidCallback(); } diff --git a/extras/modules/dashboard-widget-editor/ameWidgetCollection.php b/extras/modules/dashboard-widget-editor/ameWidgetCollection.php index 0e84bce..9527243 100644 --- a/extras/modules/dashboard-widget-editor/ameWidgetCollection.php +++ b/extras/modules/dashboard-widget-editor/ameWidgetCollection.php @@ -2,6 +2,8 @@ /** * An ordered collection of dashboard widgets. + * + * Also contains widget layout configuration, such as widget order and number of columns. */ class ameWidgetCollection { const FORMAT_NAME = 'Admin Menu Editor dashboard widgets'; @@ -11,18 +13,34 @@ class ameWidgetCollection { /** * @var ameDashboardWidget[] */ - private $widgets = array(); + private $widgets = []; /** * @var array Settings for the special "Welcome to WordPress!" panel. */ - private $welcomePanel = array(); + private $welcomePanel = []; /** * @var string */ public $siteComponentHash = ''; + private $lastModified = 0; + + private $defaultOrderOverrideEnabled = false; + private $orderOverridePerActor = []; + + /** + * @var null|int + */ + private $forcedColumnCount = null; + /** + * @var null|int Screen width (in pixels). The forced column count will only + * be used when the screen width is equal to or greater than this value. + */ + private $forcedColumnStrategy = null; + private $forcedColumnsEnabledPerActor = []; + /** * Merge the list of standard / built-in widgets with the collection. * Adds wrappers for new widgets and updates existing wrappers. @@ -37,9 +55,9 @@ public function merge($dashboardMetaBoxes) { //Update existing wrapped widgets, add new ones. $previousWidget = null; - foreach($presentWidgets as $properties) { + foreach ($presentWidgets as $properties) { $wrapper = $this->getWrapper($properties['id']); - if ($wrapper === null) { + if ( $wrapper === null ) { $wrapper = new ameStandardWidgetWrapper($properties); $this->insertAfter($wrapper, $previousWidget); $changesDetected = true; @@ -51,7 +69,7 @@ public function merge($dashboardMetaBoxes) { } //Flag wrappers that are on the list as present and the rest as not present. - foreach($this->getWrappedWidgets() as $widget) { + foreach ($this->getWrappedWidgets() as $widget) { $changed = $widget->setPresence(array_key_exists($widget->getId(), $presentWidgets)); $changesDetected = $changesDetected || $changed; } @@ -67,24 +85,24 @@ public function merge($dashboardMetaBoxes) { * @return array */ private function convertMetaBoxesToProperties($metaBoxes) { - $widgetProperties = array(); + $widgetProperties = []; - foreach($metaBoxes as $location => $priorities) { - foreach($priorities as $priority => $items) { - foreach($items as $standardWidget) { + foreach ($metaBoxes as $location => $priorities) { + foreach ($priorities as $priority => $items) { + foreach ($items as $standardWidget) { //Skip removed widgets. remove_meta_box() replaces widgets that it removes with false. //Also, The Events Calendar somehow creates a widget that's just "true"(?!), so we'll //also skip all entries that are not arrays. - if (empty($standardWidget) || !is_array($standardWidget)) { + if ( empty($standardWidget) || !is_array($standardWidget) ) { continue; } $properties = array_merge( - array( - 'priority' => $priority, - 'location' => $location, + [ + 'priority' => $priority, + 'location' => $location, 'callbackArgs' => isset($standardWidget['args']) ? $standardWidget['args'] : null, - ), + ], $standardWidget ); $widgetProperties[$properties['id']] = $properties; @@ -102,11 +120,11 @@ private function convertMetaBoxesToProperties($metaBoxes) { * @return ameStandardWidgetWrapper|null */ protected function getWrapper($id) { - if (!array_key_exists($id, $this->widgets)) { + if ( !array_key_exists($id, $this->widgets) ) { return null; } $widget = $this->widgets[$id]; - if ($widget instanceof ameStandardWidgetWrapper) { + if ( $widget instanceof ameStandardWidgetWrapper ) { return $widget; } return null; @@ -122,7 +140,7 @@ protected function getWrapper($id) { * @param ameDashboardWidget|null $target */ protected function insertAfter(ameDashboardWidget $widget, ameDashboardWidget $target = null) { - if (($target === null) || !array_key_exists($target->getId(), $this->widgets)) { + if ( ($target === null) || !array_key_exists($target->getId(), $this->widgets) ) { //Just put it at the bottom. $this->widgets[$widget->getId()] = $widget; } else { @@ -130,7 +148,7 @@ protected function insertAfter(ameDashboardWidget $widget, ameDashboardWidget $t $this->widgets = array_merge( array_slice($this->widgets, 0, $offset, true), - array($widget->getId() => $widget), + [$widget->getId() => $widget], array_slice($this->widgets, $offset, null, true) ); } @@ -144,13 +162,13 @@ protected function insertAfter(ameDashboardWidget $widget, ameDashboardWidget $t public function mergeWithWrappersFrom($otherCollection) { $previousWidget = null; - foreach($otherCollection->getWrappedWidgets() as $otherWidget) { - if (!$otherWidget->isPresent()) { + foreach ($otherCollection->getWrappedWidgets() as $otherWidget) { + if ( !$otherWidget->isPresent() ) { continue; } $myWidget = $this->getWrapper($otherWidget->getId()); - if ($myWidget === null) { + if ( $myWidget === null ) { $myWidget = $otherWidget; $this->insertAfter($myWidget, $previousWidget); } else { @@ -167,9 +185,9 @@ public function mergeWithWrappersFrom($otherCollection) { * @return ameStandardWidgetWrapper[] */ protected function getWrappedWidgets() { - $results = array(); - foreach($this->widgets as $widget) { - if ($widget instanceof ameStandardWidgetWrapper) { + $results = []; + foreach ($this->widgets as $widget) { + if ( $widget instanceof ameStandardWidgetWrapper ) { $results[] = $widget; } } @@ -182,9 +200,9 @@ protected function getWrappedWidgets() { * @return ameStandardWidgetWrapper[] */ public function getMissingWrappedWidgets() { - $results = array(); - foreach($this->getWrappedWidgets() as $widget) { - if (!$widget->isPresent()) { + $results = []; + foreach ($this->getWrappedWidgets() as $widget) { + if ( !$widget->isPresent() ) { $results[] = $widget; } } @@ -197,9 +215,9 @@ public function getMissingWrappedWidgets() { * @return ameDashboardWidget[] */ public function getPresentWidgets() { - $results = array(); - foreach($this->widgets as $widget) { - if ($widget->isPresent()) { + $results = []; + foreach ($this->widgets as $widget) { + if ( $widget->isPresent() ) { $results[] = $widget; } } @@ -213,7 +231,7 @@ public function getPresentWidgets() { * @return \ameDashboardWidget|null */ public function getWidgetById($id) { - if (array_key_exists($id, $this->widgets)) { + if ( array_key_exists($id, $this->widgets) ) { return $this->widgets[$id]; } return null; @@ -238,29 +256,35 @@ public function isEmpty() { } public function toArray() { - $widgets = array(); - foreach($this->widgets as $widget) { + $widgets = []; + foreach ($this->widgets as $widget) { $widgets[] = $widget->toArray(); } - $output = array( - 'format' => array( - 'name' => self::FORMAT_NAME, + return [ + 'format' => [ + 'name' => self::FORMAT_NAME, 'version' => self::FORMAT_VERSION, - ), - 'widgets' => $widgets, - 'welcomePanel' => $this->welcomePanel, + ], + 'widgets' => $widgets, + 'welcomePanel' => $this->welcomePanel, 'siteComponentHash' => $this->siteComponentHash, - ); + 'lastModified' => $this->lastModified, - return $output; + 'defaultOrderOverrideEnabled' => $this->defaultOrderOverrideEnabled, + 'orderOverridePerActor' => $this->orderOverridePerActor, + + 'forcedColumnCount' => $this->forcedColumnCount, + 'forcedColumnStrategy' => $this->forcedColumnStrategy, + 'forcedColumnsEnabledPerActor' => $this->forcedColumnsEnabledPerActor, + ]; } /** * @return string */ public function toJSON() { - return json_encode($this->toArray(), JSON_PRETTY_PRINT); + return wp_json_encode($this->toArray(), JSON_PRETTY_PRINT); } /** @@ -269,10 +293,10 @@ public function toJSON() { * @return array [actorId => boolean] */ public function getWelcomePanelVisibility() { - if (isset($this->welcomePanel['grantAccess']) && is_array($this->welcomePanel['grantAccess'])) { + if ( isset($this->welcomePanel['grantAccess']) && is_array($this->welcomePanel['grantAccess']) ) { return $this->welcomePanel['grantAccess']; } - return array(); + return []; } /** @@ -312,18 +336,56 @@ public static function fromArray($input) { } $collection = new self(); - foreach($input['widgets'] as $widgetProperties) { + foreach ($input['widgets'] as $widgetProperties) { $widget = ameDashboardWidget::fromArray($widgetProperties); $collection->widgets[$widget->getId()] = $widget; } if ( isset($input['welcomePanel'], $input['welcomePanel']['grantAccess']) ) { - $collection->welcomePanel = array( + $collection->welcomePanel = [ 'grantAccess' => (array)($input['welcomePanel']['grantAccess']), - ); + ]; } $collection->siteComponentHash = isset($input['siteComponentHash']) ? strval($input['siteComponentHash']) : ''; + + if ( isset($input['lastModified']) && is_numeric($input['lastModified']) ) { + $timestamp = intval($input['lastModified']); + if ( + //The timestamp can only refer to a time *after* the lastModified field was added. + ($timestamp > strtotime('2023-05-23T00:00:00+0000')) + //The timestamp can't be too far the future (small offset is allowed due to time zone differences). + && ($collection->lastModified <= (time() + 86400)) + ) { + $collection->lastModified = $timestamp; + } + } + + if ( isset($input['defaultOrderOverrideEnabled']) ) { + $collection->defaultOrderOverrideEnabled = (bool)$input['defaultOrderOverrideEnabled']; + } + if ( isset($input['orderOverridePerActor']) && is_array($input['orderOverridePerActor']) ) { + $collection->orderOverridePerActor = $input['orderOverridePerActor']; + } + + if ( array_key_exists('forcedColumnCount', $input) ) { + $columnCount = $input['forcedColumnCount']; + if ( $columnCount !== null ) { + $columnCount = max(min(intval($columnCount), 4), 1); + } + $collection->forcedColumnCount = $columnCount; + } + if ( array_key_exists('forcedColumnStrategy', $input) ) { + $strategy = $input['forcedColumnStrategy']; + if ( $strategy !== null ) { + $strategy = max(min(intval($strategy), 3000), 1); + } + $collection->forcedColumnStrategy = $strategy; + } + if ( isset($input['forcedColumnsEnabledPerActor']) && is_array($input['forcedColumnsEnabledPerActor']) ) { + $collection->forcedColumnsEnabledPerActor = $input['forcedColumnsEnabledPerActor']; + } + return $collection; } @@ -334,7 +396,7 @@ public static function fromArray($input) { public static function fromJSON($json) { $input = json_decode($json, true); - if ($input === null) { + if ( $input === null ) { throw new ameInvalidJsonException('Cannot parse widget data. The input is not valid JSON.'); } @@ -346,10 +408,9 @@ public static function fromJSON($json) { */ public function toDbString() { $serializedData = $this->toJSON(); - $tags = array(); + $tags = []; if ( function_exists('gzcompress') ) { - /** @noinspection PhpComposerExtensionStubsInspection */ $compressed = gzcompress($serializedData); if ( is_string($compressed) ) { $serializedData = $compressed; @@ -399,6 +460,91 @@ public static function fromDbString($serializedData) { return self::fromJSON($data); } + + /** + * @return bool + */ + public function isDefaultOrderOverrideEnabled() { + return $this->defaultOrderOverrideEnabled; + } + + /** + * @param \WP_User $user + * @return bool + */ + public function isOrderOverrideEnabledFor($user) { + if ( !$this->isDefaultOrderOverrideEnabled() ) { + return false; + } + return $this->checkOverrideStatus($this->orderOverridePerActor, $user); + } + + /** + * @param \WP_User $user + * @return bool + */ + public function isColumnOverrideEnabledFor($user) { + if ( ($this->forcedColumnCount === null) || empty($this->forcedColumnsEnabledPerActor) ) { + return false; + } + return $this->checkOverrideStatus($this->forcedColumnsEnabledPerActor, $user); + } + + /** + * @param array $actorMap + * @param \WP_User $user + * @return bool + */ + private function checkOverrideStatus($actorMap, $user) { + if ( empty($actorMap) || empty($user) ) { + return false; + } + + $userActor = 'user:' . $user->user_login; + if ( isset($actorMap[$userActor]) ) { + return $actorMap[$userActor]; + } + + if ( is_multisite() && is_super_admin($user->ID) ) { + if ( isset($actorMap['special:super_admin']) ) { + return $actorMap['special:super_admin']; + } + } + + //Enable override if it's enabled for at least one role. + $roles = $user->roles; + if ( is_array($roles) ) { + foreach ($roles as $roleId) { + if ( !empty($actorMap['role:' . $roleId]) ) { + return true; + } + } + } + + return false; + } + + /** + * @return int + */ + public function getLastModified() { + return $this->lastModified; + } + + /** + * @return int|null + */ + public function getForcedColumnCount() { + return $this->forcedColumnCount; + } + + /** + * @return int|null + */ + public function getForcedColumnBreakpoint() { + return $this->forcedColumnStrategy; + } } -class ameInvalidWidgetDataException extends RuntimeException {} \ No newline at end of file +class ameInvalidWidgetDataException extends RuntimeException { +} \ No newline at end of file diff --git a/extras/modules/dashboard-widget-editor/ameWidgetEditor.php b/extras/modules/dashboard-widget-editor/ameWidgetEditor.php index debd78a..f94081b 100644 --- a/extras/modules/dashboard-widget-editor/ameWidgetEditor.php +++ b/extras/modules/dashboard-widget-editor/ameWidgetEditor.php @@ -1,4 +1,8 @@ requiredParam('widgetData') - ->permissionCallback(array($this, 'userCanEditWidgets')) - ->handler(array($this, 'ajaxExportWidgets')) + ->permissionCallback([$this, 'userCanEditWidgets']) + ->handler([$this, 'ajaxExportWidgets']) ->register(); ajaw_v1_CreateAction('ws-ame-import-widgets') - ->permissionCallback(array($this, 'userCanEditWidgets')) - ->handler(array($this, 'ajaxImportWidgets')) + ->permissionCallback([$this, 'userCanEditWidgets']) + ->handler([$this, 'ajaxImportWidgets']) ->register(); add_action( 'admin_menu_editor-register_hideable_items', - array($this, 'registerHideableItems'), - 10, - 1 + [$this, 'registerHideableItems'] ); add_filter( 'admin_menu_editor-save_hideable_items-d-widgets', - array($this, 'saveHideableItems'), + [$this, 'saveHideableItems'], 10, 2 ); + + $this->columnStylesheet = new Stylesheet( + 'ame-dashboard-column-override', + function () { + $settings = $this->loadSettings(); + $columns = $settings->getForcedColumnCount(); + if ( $columns === null ) { + return ''; //No need to override the number of columns. + } + + $templateFile = __DIR__ . '/custom-columns.css'; + if ( !is_file($templateFile) ) { + return '/* CSS template not found. */'; + } + + //This is not a remote file. + //phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown + $css = file_get_contents($templateFile); + if ( empty($css) ) { + return '/* Failed to load the CSS template from a file. */'; + } + + $breakpoint = $settings->getForcedColumnBreakpoint(); + if ( empty($breakpoint) ) { + return $css; + } else { + //Wrap the CSS in a media query that only applies it above + //the configured breakpoint (inclusive). + $breakpoint = min(max(intval($breakpoint), 0), 3000); + return ( + '@media screen and (min-width: ' . $breakpoint . 'px) {' . PHP_EOL . + $css . PHP_EOL + . '}' + ); + } + }, + function () { + $settings = $this->loadSettings(); + return $settings->getLastModified(); + } + ); + + if ( defined('DOING_AJAX') ) { + $this->columnStylesheet->addOutputHook(); + } } public function setupDashboard() { @@ -65,9 +119,10 @@ public function setupDashboard() { //Store new widgets and changed defaults. //We want a complete list of widgets, so we only do this when an administrator is logged in. //Admins usually can see everything. Other roles might be missing specific widgets. + //phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ($changesDetected || !empty($_GET['ame-cache-buster'])) && $this->userCanEditWidgets() ) { //Remove wrapped widgets where the file no longer exists. - foreach($this->dashboardWidgets->getMissingWrappedWidgets() as $widget) { + foreach ($this->dashboardWidgets->getMissingWrappedWidgets() as $widget) { $callbackFileName = $widget->getCallbackFileName(); if ( !empty($callbackFileName) && !is_file($callbackFileName) ) { $this->dashboardWidgets->remove($widget->getId()); @@ -80,16 +135,19 @@ public function setupDashboard() { //Remove all Dashboard widgets. //Important: Using remove_meta_box() would prevent widgets being re-added. Clearing the array does not. - $wp_meta_boxes['dashboard'] = array(); + //phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Required for the plugin to work. + $wp_meta_boxes['dashboard'] = []; //Re-add all widgets, this time with custom settings. $currentUser = wp_get_current_user(); - foreach($this->dashboardWidgets->getPresentWidgets() as $widget) { + foreach ($this->dashboardWidgets->getPresentWidgets() as $widget) { if ( $widget->isVisibleTo($currentUser, $this->menuEditor) ) { - $widget->addToDashboard(); + $widget->addToDashboard( + $this->dashboardWidgets->isDefaultOrderOverrideEnabled() + ); } else { //Technically, this line is not required. It just ensures that other plugins can't recreate the widget. - remove_meta_box($widget->getId(), 'dashboard', $widget->getLocation()); + remove_meta_box($widget->getId(), 'dashboard', $widget->getOriginalLocation()); } } @@ -103,28 +161,83 @@ public function setupDashboard() { if ( $isWelcomePanelHidden ) { remove_action('welcome_panel', 'wp_welcome_panel'); } + + $orderOverrideEnabled = $this->dashboardWidgets->isOrderOverrideEnabledFor($currentUser); + + if ( $orderOverrideEnabled ) { + //Optimization: Enable the user metadata filter only when order override is + //enabled for the current user and when the user is viewing the dashboard. + add_filter('get_user_metadata', [$this, 'filterUserWidgetOrder'], 10, 4); + + //Remove the dashed outline from empty widget containers and hide the "up" + //and "down" buttons. The helper script will also handle some of this, but + //doing it early and in CSS helps prevent FOUC. + add_action( + 'admin_enqueue_scripts', + function ($hookSuffix = null) { + if ( $hookSuffix !== 'index.php' ) { + return; + } + wp_add_inline_style( + 'dashboard', + '#dashboard-widgets .postbox-container .empty-container { outline: none; } + #dashboard-widgets .postbox-container .empty-container:after { content: ""; } + #dashboard-widgets .postbox .handle-order-higher, + #dashboard-widgets .postbox .handle-order-lower { display: none; }' + ); + } + ); + } + + if ( $orderOverrideEnabled ) { + //Enqueue the helper script that overrides the widget order and column count. + ScriptDependency::create( + plugins_url('custom-widget-layout.js', __FILE__), + 'ame-dashboard-layout-override' + ) + ->addDependencies('jquery', 'jquery-ui-sortable') + ->addJsVariable( + 'wsAmeDashboardLayoutSettings', + [ + 'orderOverrideEnabled' => $orderOverrideEnabled, + ] + ) + ->autoEnqueue(); + } + + $columns = $this->dashboardWidgets->getForcedColumnCount(); + if ( !empty($columns) && $this->dashboardWidgets->isColumnOverrideEnabledFor($currentUser) ) { + //It appears that the `wp_dashboard_setup` hook only runs on the "index.php" page, + //so we don't need to worry about checking the hook suffix when adding the stylesheet. + $this->columnStylesheet->addAdminEnqueueHook(); + + add_filter('admin_body_class', function ($classes) use ($columns) { + $classes .= ' ame-de-override-columns-' . $columns . ' '; + return $classes; + }); + } } public function enqueueTabScripts() { - //TODO: Remove this later, it's already registered in register_base_dependencies. - wp_register_auto_versioned_script( - 'knockout', - plugins_url('js/knockout.js', $this->menuEditor->plugin_file) - ); - wp_register_auto_versioned_script( 'ame-dashboard-widget', plugins_url('dashboard-widget.js', __FILE__), - array('knockout', 'ame-lodash', 'ame-actor-manager',) + ['ame-knockout', 'ame-lodash', 'ame-actor-manager', 'ame-pro-common-lib'] ); wp_register_auto_versioned_script( 'ame-dashboard-widget-editor', plugins_url('dashboard-widget-editor.js', __FILE__), - array( - 'ame-lodash', 'ame-dashboard-widget', 'knockout', 'ame-actor-selector', - 'ame-jquery-form', 'jquery-ui-dialog', 'ame-ko-extensions', - ) + [ + 'ame-lodash', + 'ame-dashboard-widget', + 'ame-knockout', + 'ame-actor-selector', + 'ame-jquery-form', + 'jquery-ui-dialog', + 'ame-ko-extensions', + 'ame-knockout-sortable', + ] ); //Automatically refresh the list of available dashboard widgets. @@ -142,20 +255,21 @@ public function enqueueTabScripts() { wp_enqueue_auto_versioned_script( 'ame-refresh-widgets', plugins_url('refresh-widgets.js', __FILE__), - array('jquery') + ['jquery'] ); wp_localize_script( 'ame-refresh-widgets', 'wsWidgetRefresherData', - array( - 'editorUrl' => $this->getEditorUrl(array('ame-widget-refresh-done' => 1)), - 'dashboardUrl' => add_query_arg('ame-cache-buster', time() . '_' . rand(), admin_url('index.php')), - ) + [ + 'editorUrl' => $this->getEditorUrl(['ame-widget-refresh-done' => 1]), + 'dashboardUrl' => add_query_arg('ame-cache-buster', time() . '_' . wp_rand(), admin_url('index.php')), + ] ); return; } + wp_enqueue_script('jquery-qtip'); wp_enqueue_script('ame-dashboard-widget-editor'); $selectedActor = null; @@ -163,14 +277,22 @@ public function enqueueTabScripts() { $selectedActor = strval($query['selected_actor']); } + $previewColumns = get_user_meta(get_current_user_id(), self::PREVIEW_COLUMN_META_KEY, true); + if ( is_numeric($previewColumns) ) { + $previewColumns = max(min(intval($previewColumns), 4), 1); + } else { + $previewColumns = 1; + } + wp_localize_script( 'ame-dashboard-widget-editor', 'wsWidgetEditorData', - array( + [ 'widgetSettings' => $this->dashboardWidgets->toArray(), - 'selectedActor' => $selectedActor, - 'isMultisite' => is_multisite(), - ) + 'selectedActor' => $selectedActor, + 'isMultisite' => is_multisite(), + 'previewColumns' => $previewColumns, + ] ); } @@ -189,7 +311,7 @@ public function displaySettingsPage() { } } - public function handleFormSubmission($action, $post = array()) { + public function handleFormSubmission($action, $post = []) { //Note: We don't need to check user permissions here because plugin core already did. if ( $action === 'save_widgets' ) { check_admin_referer($action); @@ -197,7 +319,13 @@ public function handleFormSubmission($action, $post = array()) { $this->dashboardWidgets = ameWidgetCollection::fromJSON($post['data']); $this->saveSettings(); - $params = array('updated' => 1); + //Remember the preview column count. + if ( isset($post['preview_columns']) && is_scalar($post['preview_columns']) ) { + $columnCount = max(min(intval($post['preview_columns']), 4), 1); + update_user_meta(get_current_user_id(), self::PREVIEW_COLUMN_META_KEY, $columnCount); + } + + $params = ['updated' => 1]; //Re-select the same actor. if ( !empty($post['selected_actor']) ) { @@ -209,12 +337,12 @@ public function handleFormSubmission($action, $post = array()) { } } - private function getEditorUrl($queryParameters = array()) { + private function getEditorUrl($queryParameters = []) { $queryParameters = array_merge( - array( - 'page' => 'menu_editor', - 'sub_section' => 'dashboard-widgets' - ), + [ + 'page' => 'menu_editor', + 'sub_section' => 'dashboard-widgets', + ], $queryParameters ); return add_query_arg($queryParameters, admin_url('options-general.php')); @@ -231,8 +359,8 @@ public function ajaxExportWidgets($params) { $fileName = sprintf( '%1$s dashboard widgets (%2$s).json', - parse_url(get_site_url(), PHP_URL_HOST), - date('Y-m-d') + wp_parse_url(get_site_url(), PHP_URL_HOST), + gmdate('Y-m-d') ); //Force file download. @@ -247,6 +375,7 @@ public function ajaxExportWidgets($params) { header("Pragma: private"); header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- The data is JSON, and is output as a file. echo $exportData; exit(); } @@ -256,24 +385,18 @@ public function ajaxImportWidgets() { return new WP_Error('no_file', 'No file specified'); } + //While this doesn't use wp_handle_upload() since we don't want to keep the file, + //it does perform basic validation and error checking. + //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $importFile = $_FILES['widgetFile']; - if ( filesize($importFile['tmp_name']) > self::MAX_IMPORT_FILE_SIZE ){ - return new WP_Error( - 'file_too_large', - sprintf( - 'Import file too large. Maximum allowed size: %s bytes', - number_format_i18n(self::MAX_IMPORT_FILE_SIZE) - ) - ); - } //Check for general upload errors. if ( $importFile['error'] !== UPLOAD_ERR_OK ) { - $knownErrorCodes = array( + $knownErrorCodes = [ UPLOAD_ERR_INI_SIZE => sprintf( 'The uploaded file exceeds the upload_max_filesize directive in php.ini. Limit: %s', - strval(ini_get('upload_max_filesize')) + ini_get('upload_max_filesize') ), UPLOAD_ERR_FORM_SIZE => "The uploaded file exceeds the internal file size limit. Please contact the developer.", UPLOAD_ERR_PARTIAL => "The file was only partially uploaded", @@ -281,7 +404,7 @@ public function ajaxImportWidgets() { UPLOAD_ERR_NO_TMP_DIR => "Missing a temporary folder", UPLOAD_ERR_CANT_WRITE => "Failed to write file to disk", UPLOAD_ERR_EXTENSION => "File upload stopped by a PHP extension", - ); + ]; if ( array_key_exists($importFile['error'], $knownErrorCodes) ) { $message = $knownErrorCodes[$importFile['error']]; @@ -292,6 +415,20 @@ public function ajaxImportWidgets() { return new WP_Error('internal_upload_error', $message); } + if ( !is_uploaded_file($importFile['tmp_name']) ) { + return new WP_Error('invalid_upload', 'Invalid upload: not an uploaded file'); + } + + if ( filesize($importFile['tmp_name']) > self::MAX_IMPORT_FILE_SIZE ) { + return new WP_Error( + 'file_too_large', + sprintf( + 'Import file too large. Maximum allowed size: %s bytes', + number_format_i18n(self::MAX_IMPORT_FILE_SIZE) + ) + ); + } + $fileContents = file_get_contents($importFile['tmp_name']); //Check if this file could plausibly contain an exported widget collection. @@ -321,7 +458,7 @@ private function loadSettings() { return $this->dashboardWidgets; } - $settings = $this->getScopedOption(self::OPTION_NAME, null); + $settings = $this->getScopedOption(self::OPTION_NAME); if ( empty($settings) ) { $this->dashboardWidgets = new ameWidgetCollection(); } else { @@ -383,7 +520,7 @@ public function userCanEditWidgets() { * @return string */ private function generateCompontentHash() { - $components = array(); + $components = []; //WordPress. $components[] = 'WordPress ' . (isset($GLOBALS['wp_version']) ? $GLOBALS['wp_version'] : 'unknown'); @@ -403,7 +540,7 @@ private function generateCompontentHash() { sort($activePlugins); $components = array_merge($components, $activePlugins); - return md5(implode('|' , $components)); + return md5(implode('|', $components)); } /** @@ -429,7 +566,7 @@ public function registerHideableItems($store) { $store->addItem( self::HIDEABLE_ITEM_PREFIX . $widget->getId(), $this->sanitizeTitleForHiding($widget->getTitle()), - array($cat), + [$cat], null, $widget->getGrantAccess(), 'd-widgets', @@ -441,7 +578,7 @@ public function registerHideableItems($store) { $store->addItem( self::HIDEABLE_WELCOME_ITEM_ID, 'Welcome', - array($cat), + [$cat], null, $collection->getWelcomePanelVisibility(), 'd-widgets' @@ -459,7 +596,7 @@ private function sanitizeTitleForHiding($title) { $title );*/ - return trim(strip_tags($title)); + return trim(wp_strip_all_tags($title)); } public function saveHideableItems($errors, $items) { @@ -470,8 +607,8 @@ public function saveHideableItems($errors, $items) { if ( isset($items[self::HIDEABLE_WELCOME_ITEM_ID]) ) { $welcomePanelEnabled = ameUtils::get( $items, - array(self::HIDEABLE_WELCOME_ITEM_ID, 'enabled'), - array() + [self::HIDEABLE_WELCOME_ITEM_ID, 'enabled'], + [] ); unset($items[self::HIDEABLE_WELCOME_ITEM_ID]); @@ -486,7 +623,7 @@ public function saveHideableItems($errors, $items) { foreach ($items as $id => $item) { $widgetId = substr($id, strlen(self::HIDEABLE_ITEM_PREFIX)); - $enabled = !empty($item['enabled']) ? $item['enabled'] : array(); + $enabled = !empty($item['enabled']) ? $item['enabled'] : []; $widget = $collection->getWidgetById($widgetId); if ( $widget !== null ) { @@ -501,4 +638,40 @@ public function saveHideableItems($errors, $items) { return $errors; } + + public function filterUserWidgetOrder($inputValue, $objectId = null, $metaKey = '') { + if ( + ($metaKey !== 'meta-box-order_dashboard') + || ($objectId !== get_current_user_id()) + ) { + return $inputValue; + } + if ( empty($this->dashboardWidgets) ) { + return $inputValue; + } + $presentWidgets = $this->dashboardWidgets->getPresentWidgets(); + if ( empty($presentWidgets) ) { + return $inputValue; + } + + $columns = [ + 'normal' => [], + 'side' => [], + 'column3' => [], + 'column4' => [], + ]; + foreach ($presentWidgets as $widget) { + $location = $widget->getLocation(); + if ( isset($columns[$location]) ) { + $columns[$location][] = $widget->getId(); + } + } + + $orderedWidgets = []; + foreach ($columns as $location => $widgets) { + $orderedWidgets[$location] = implode(',', $widgets); + } + + return [$orderedWidgets]; + } } \ No newline at end of file diff --git a/extras/modules/dashboard-widget-editor/custom-columns.css b/extras/modules/dashboard-widget-editor/custom-columns.css new file mode 100644 index 0000000..7b89d93 --- /dev/null +++ b/extras/modules/dashboard-widget-editor/custom-columns.css @@ -0,0 +1,118 @@ +.ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-1 { + width: 100%; + float: left; +} +.ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-2, .ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-3, .ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-4 { + width: 100%; +} +.ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-2, +.ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-3, +.ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-4 { + float: left; +} +.ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-1 .empty-container { + outline: 3px dashed #c3c4c7; + height: 250px; + min-height: 100px; +} +.ame-de-override-columns-1.is-dragging-metaboxes #wpbody-content #dashboard-widgets #postbox-container-1 .meta-box-sortables { + outline: 3px dashed #646970; + display: flow-root; +} +.ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-2 .empty-container, .ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-3 .empty-container, .ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-4 .empty-container { + outline: none; + height: 0; + min-height: 0; + margin-bottom: 0; +} +.ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-2 .empty-container:after, .ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-3 .empty-container:after, .ame-de-override-columns-1 #wpbody-content #dashboard-widgets #postbox-container-4 .empty-container:after { + display: none; +} + +.ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-1 { + width: 49.5%; + float: left; +} +.ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-2, .ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-3, .ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-4 { + width: 50.5%; +} +.ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-2, .ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-1 { + float: left; +} +.ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-2, .ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-3, .ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-4 { + float: right; +} +.ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-1 .empty-container, .ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-2 .empty-container { + outline: 3px dashed #c3c4c7; + height: 250px; + min-height: 100px; +} +.ame-de-override-columns-2.is-dragging-metaboxes #wpbody-content #dashboard-widgets #postbox-container-1 .meta-box-sortables, .ame-de-override-columns-2.is-dragging-metaboxes #wpbody-content #dashboard-widgets #postbox-container-2 .meta-box-sortables { + outline: 3px dashed #646970; + display: flow-root; +} +.ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-3 .empty-container, .ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-4 .empty-container { + outline: none; + height: 0; + min-height: 0; + margin-bottom: 0; +} +.ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-3 .empty-container:after, .ame-de-override-columns-2 #wpbody-content #dashboard-widgets #postbox-container-4 .empty-container:after { + display: none; +} + +.ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-1 { + width: 33%; + float: left; +} +.ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-2, .ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-3, .ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-4 { + width: 33.5%; +} +.ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-2 { + float: left; +} +.ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-3, .ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-4 { + float: right; +} +.ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-1 .empty-container, .ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-2 .empty-container, .ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-3 .empty-container { + outline: 3px dashed #c3c4c7; + height: 250px; + min-height: 100px; +} +.ame-de-override-columns-3.is-dragging-metaboxes #wpbody-content #dashboard-widgets #postbox-container-1 .meta-box-sortables, .ame-de-override-columns-3.is-dragging-metaboxes #wpbody-content #dashboard-widgets #postbox-container-2 .meta-box-sortables, .ame-de-override-columns-3.is-dragging-metaboxes #wpbody-content #dashboard-widgets #postbox-container-3 .meta-box-sortables { + outline: 3px dashed #646970; + display: flow-root; +} +.ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-4 .empty-container { + outline: none; + height: 0; + min-height: 0; + margin-bottom: 0; +} +.ame-de-override-columns-3 #wpbody-content #dashboard-widgets #postbox-container-4 .empty-container:after { + display: none; +} + +.ame-de-override-columns-4 #wpbody-content #dashboard-widgets #postbox-container-1 { + width: 25%; + float: left; +} +.ame-de-override-columns-4 #wpbody-content #dashboard-widgets #postbox-container-2, .ame-de-override-columns-4 #wpbody-content #dashboard-widgets #postbox-container-3, .ame-de-override-columns-4 #wpbody-content #dashboard-widgets #postbox-container-4 { + width: 25%; +} +.ame-de-override-columns-4 #wpbody-content #dashboard-widgets #postbox-container-2, +.ame-de-override-columns-4 #wpbody-content #dashboard-widgets #postbox-container-3, +.ame-de-override-columns-4 #wpbody-content #dashboard-widgets #postbox-container-4 { + float: left; +} +.ame-de-override-columns-4 #wpbody-content #dashboard-widgets #postbox-container-1 .empty-container, .ame-de-override-columns-4 #wpbody-content #dashboard-widgets #postbox-container-2 .empty-container, .ame-de-override-columns-4 #wpbody-content #dashboard-widgets #postbox-container-3 .empty-container, .ame-de-override-columns-4 #wpbody-content #dashboard-widgets #postbox-container-4 .empty-container { + outline: 3px dashed #c3c4c7; + height: 250px; + min-height: 100px; +} +.ame-de-override-columns-4.is-dragging-metaboxes #wpbody-content #dashboard-widgets #postbox-container-1 .meta-box-sortables, .ame-de-override-columns-4.is-dragging-metaboxes #wpbody-content #dashboard-widgets #postbox-container-2 .meta-box-sortables, .ame-de-override-columns-4.is-dragging-metaboxes #wpbody-content #dashboard-widgets #postbox-container-3 .meta-box-sortables, .ame-de-override-columns-4.is-dragging-metaboxes #wpbody-content #dashboard-widgets #postbox-container-4 .meta-box-sortables { + outline: 3px dashed #646970; + display: flow-root; +} + +/*# sourceMappingURL=custom-columns.css.map */ diff --git a/extras/modules/dashboard-widget-editor/custom-columns.css.map b/extras/modules/dashboard-widget-editor/custom-columns.css.map new file mode 100644 index 0000000..1adaf47 --- /dev/null +++ b/extras/modules/dashboard-widget-editor/custom-columns.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["custom-columns.scss"],"names":[],"mappings":"AAqBC;EACC;EACA;;AAKD;EACC;;AAKA;AAAA;AAAA;EAGC;;AAkBD;EACC;EACA;EACA;;AAMA;EACC;EACA;;AASD;EACC;EACA;EACA;EACA;;AAID;EACC;;;AA/DH;EACC;EACA;;AAKD;EACC;;AAaA;EACC;;AAID;EACC;;AAOD;EACC;EACA;EACA;;AAMA;EACC;EACA;;AASD;EACC;EACA;EACA;EACA;;AAID;EACC;;;AA/DH;EACC;EACA;;AAKD;EACC;;AAaA;EACC;;AAID;EACC;;AAOD;EACC;EACA;EACA;;AAMA;EACC;EACA;;AASD;EACC;EACA;EACA;EACA;;AAID;EACC;;;AA/DH;EACC;EACA;;AAKD;EACC;;AAKA;AAAA;AAAA;EAGC;;AAkBD;EACC;EACA;EACA;;AAMA;EACC;EACA","file":"custom-columns.css"} \ No newline at end of file diff --git a/extras/modules/dashboard-widget-editor/custom-columns.scss b/extras/modules/dashboard-widget-editor/custom-columns.scss new file mode 100644 index 0000000..1453295 --- /dev/null +++ b/extras/modules/dashboard-widget-editor/custom-columns.scss @@ -0,0 +1,107 @@ +@use 'sass:map'; +@use "sass:list"; + +@function make-column-selectors($from, $to) { + $column-selectors: (); + @for $i from $from through $to { + $selector: "#wpbody-content #dashboard-widgets #postbox-container-#{$i}"; + $column-selectors: list.append($column-selectors, $selector, 'comma'); + } + @return $column-selectors; +} + +@mixin split-columns($n) { + //First column width, other columns width. + $widths: ( + 1: [100%, 100%], + 2: [49.5%, 50.5%], + 3: [33%, 33.5%], + 4: [25%, 25%] + ); + + #wpbody-content #dashboard-widgets #postbox-container-1 { + width: nth(map.get($widths, $n), 1); + float: left; + } + + //Other columns have the same width, second in the list. + $other-columns: make-column-selectors(2, 4); + #{$other-columns} { + width: nth(map.get($widths, $n), 2); + } + + @if ($n == 1) or ($n == 4) { + //All columns are floated left. + #wpbody-content #dashboard-widgets #postbox-container-2, + #wpbody-content #dashboard-widgets #postbox-container-3, + #wpbody-content #dashboard-widgets #postbox-container-4 { + float: left; + } + } @else if $n < 4 { + //Columns up to n-1 are floated left, the rest are floated right. + $left-columns: make-column-selectors(2, $n - 1); + #{$left-columns} { + float: left; + } + + $right-columns: make-column-selectors($n, 4); + #{$right-columns} { + float: right; + } + } + + $fully-visible-columns: make-column-selectors(1, $n); + #{$fully-visible-columns} { + //Restore the dashed outline for empty columns. + .empty-container { + outline: 3px dashed #c3c4c7; + height: 250px; + min-height: 100px; + } + } + //Show a darker outline when dragging widgets. + &.is-dragging-metaboxes { + #{$fully-visible-columns} { + .meta-box-sortables { + outline: 3px dashed #646970; + display: flow-root; + } + } + } + + @if $n < 4 { + $remaining-columns: make-column-selectors($n + 1, 4); + #{$remaining-columns} { + //Remove the dashed outline. + .empty-container { + outline: none; + height: 0; + min-height: 0; + margin-bottom: 0; + } + + //Remove the text. + .empty-container:after { + display: none; + } + } + } +} + + +.ame-de-override-columns-1 { + @include split-columns(1); +} + +.ame-de-override-columns-2 { + @include split-columns(2); +} + +.ame-de-override-columns-3 { + @include split-columns(3); +} + +.ame-de-override-columns-4 { + @include split-columns(4); +} + diff --git a/extras/modules/dashboard-widget-editor/custom-widget-layout.js b/extras/modules/dashboard-widget-editor/custom-widget-layout.js new file mode 100644 index 0000000..5463713 --- /dev/null +++ b/extras/modules/dashboard-widget-editor/custom-widget-layout.js @@ -0,0 +1,67 @@ +"use strict"; +/// +/// +{ + const $ = jQuery; + function disableWidgetSorting() { + //Set the "handle" and "items" options to a non-existent selector to effectively + //disable sorting. Using the "disable" method doesn't work because the sortable + //somehow becomes enabled again when the user opens or closes a widget. I was not + //able to figure out how that happens. + const nonExistentSelector = '.ws-de-non-existent-class-4168408650'; + $('.meta-box-sortables') + .sortable('option', 'handle', nonExistentSelector) + .sortable('option', 'items', nonExistentSelector) + .sortable('refresh'); + //Hide the "up" and "down" buttons. + const $widgetContainer = $('#dashboard-widgets'); + $widgetContainer + .find('.postbox .handle-order-higher, .postbox .handle-order-lower') + .hide(); + //Get rid of the "move" cursor on widget headers. + $('').appendTo('head'); + } + return [this.$styleElement, this.$link]; + } + getImageUrl(imageSetting) { + if (imageSetting === null) { + return null; + } + const externalUrl = (typeof imageSetting.externalUrl === 'string') ? imageSetting.externalUrl.trim() : ''; + if (externalUrl) { + return externalUrl; + } + const attachmentId = imageSetting.attachmentId || 0; + //const attachmentSiteId = imageSetting.attachmentSiteId || 0; + if (attachmentId > 0) { + //Use the cached attachment URL if possible. + if (imageSetting.attachmentUrl) { + return imageSetting.attachmentUrl; + } + //Load the attachment URL from the server. + if ((typeof wp !== 'undefined') && wp.media && wp.media.attachment) { + //Maybe it's already loaded? + let attachmentUrl = wp.media.attachment(attachmentId).get('url'); + if (attachmentUrl) { + return attachmentUrl; + } + const deferredLoader = $.Deferred(); + wp.media.attachment(attachmentId).fetch().then( + //Success + (attachment) => { + if (attachment && attachment.url) { + deferredLoader.resolve(attachment.url); + } + else { + deferredLoader.reject(); + } + }, + //Error + () => deferredLoader.reject()); + return deferredLoader.promise(); + } + } + //No image. + return null; + } + getDefaultVerticalMenuMargins() { + if ((this.defaultMenuMarginTop === null) || (this.defaultMenuMarginBottom === null)) { + //Get the vertical margins of the admin menu. The value includes the "px" suffix, + //but parseInt() will ignore it. + const $adminmenu = $('#adminmenu'); + this.defaultMenuMarginTop = parseInt($adminmenu.css('margin-top'), 10); + this.defaultMenuMarginBottom = parseInt($adminmenu.css('margin-bottom'), 10); + if (isNaN(this.defaultMenuMarginTop)) { + this.defaultMenuMarginTop = 0; + } + if (isNaN(this.defaultMenuMarginBottom)) { + this.defaultMenuMarginBottom = 0; + } + } + return [this.defaultMenuMarginTop, this.defaultMenuMarginBottom]; + } + removeLogo() { + if (this.$container) { + this.$container.remove(); + this.$container = null; + this.$link = null; + } + if (this.$styleElement) { + this.$styleElement.remove(); + this.$styleElement = null; + } + } + getFeatureId() { + return 'MenuLogoFeature'; + } + } + AmeMenuStylerJsFeatures.MenuLogoFeature = MenuLogoFeature; + //Always initialize the features if their config is available. + //They work normally on most admin pages, and are used for preview on the settings page. + AmeMenuStylerJsFeatures.collapseButtonFeature = null; + AmeMenuStylerJsFeatures.menuLogoFeature = null; + const collapseButtonFeatureKey = 'ameMenuStyler_collapseButtonTextFt'; + const menuLogoFeatureKey = 'ameMenuStyler_menuLogoFt'; + let isInitialized = false; + function createFeatureInstances() { + if (isInitialized) { + return; + } + isInitialized = true; + //If the script is loaded multiple times, the features might already exist. + //This can happen because the script is both enqueued normally and imported + //as a module on the settings page. + //We want each feature to be initialized only once, so we'll store them + //in the window object and reuse them. + if (ameMenuStylerFeatureConfig.collapseButtonText) { + if (window[collapseButtonFeatureKey]) { + AmeMenuStylerJsFeatures.collapseButtonFeature = window[collapseButtonFeatureKey]; + } + else { + AmeMenuStylerJsFeatures.collapseButtonFeature = new CollapseButtonTextFeature(ameMenuStylerFeatureConfig.collapseButtonText); + window[collapseButtonFeatureKey] = AmeMenuStylerJsFeatures.collapseButtonFeature; + } + } + if (ameMenuStylerFeatureConfig.menuLogo) { + if (window[menuLogoFeatureKey]) { + AmeMenuStylerJsFeatures.menuLogoFeature = window[menuLogoFeatureKey]; + } + else { + AmeMenuStylerJsFeatures.menuLogoFeature = new MenuLogoFeature(ameMenuStylerFeatureConfig.menuLogo); + window[menuLogoFeatureKey] = AmeMenuStylerJsFeatures.menuLogoFeature; + } + } + /** + * Register the features with the Admin Customizer preview handler, if active. + * + * @param {AmeAdminCustomizerPreview.PreviewHandler} previewHandler + */ + function registerFeaturePreview(previewHandler) { + //Both features should exist in the AC preview, but let's check just in case. + if (!AmeMenuStylerJsFeatures.collapseButtonFeature || !AmeMenuStylerJsFeatures.menuLogoFeature) { + console.warn('Menu Styler: One or more features are not initialized in AC preview.'); + return; + } + previewHandler.registerPreviewUpdater(AmeMenuStylerJsFeatures.collapseButtonFeature.getPreviewableSettingIds(), AmeMenuStylerJsFeatures.collapseButtonFeature); + previewHandler.registerPreviewUpdater(AmeMenuStylerJsFeatures.menuLogoFeature.getPreviewableSettingIds(), AmeMenuStylerJsFeatures.menuLogoFeature); + } + if (typeof window['wsAdminCustomizerPreview'] !== 'undefined') { + registerFeaturePreview(window['wsAdminCustomizerPreview']); + } + else { + $(document).on('adminMenuEditor:acPreviewStart', (event, previewHandler) => { + registerFeaturePreview(previewHandler); + }); + } + } + //The #adminmenu element must be available before initialization. The DOMContentLoaded event + //works, but we can better avoid a visible change/FOUC by using a custom event that the plugin + //triggers immediately after WordPress outputs the admin menu. + $(document).one('adminMenuEditor:menuDomReady', createFeatureInstances); + $(createFeatureInstances); + //Register the features with the menu styler dialog. + $(document).on('adminMenuEditor:menuStylerUiRegister', function (_unused, vm) { + if (!vm) { + return; + } + createFeatureInstances(); + if (AmeMenuStylerJsFeatures.collapseButtonFeature) { + vm.registerPreviewUpdater(AmeMenuStylerJsFeatures.collapseButtonFeature.getPreviewableSettingIds(), AmeMenuStylerJsFeatures.collapseButtonFeature); + } + if (AmeMenuStylerJsFeatures.menuLogoFeature) { + vm.registerPreviewUpdater(AmeMenuStylerJsFeatures.menuLogoFeature.getPreviewableSettingIds(), AmeMenuStylerJsFeatures.menuLogoFeature); + } + }); +})(AmeMenuStylerJsFeatures || (AmeMenuStylerJsFeatures = {})); +//# sourceMappingURL=menu-styler-features.js.map \ No newline at end of file diff --git a/extras/modules/menu-styler/menu-styler-features.js.map b/extras/modules/menu-styler/menu-styler-features.js.map new file mode 100644 index 0000000..3a3dc41 --- /dev/null +++ b/extras/modules/menu-styler/menu-styler-features.js.map @@ -0,0 +1 @@ +{"version":3,"file":"menu-styler-features.js","sourceRoot":"","sources":["menu-styler-features.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/menu-styler/menu-styler-features.ts b/extras/modules/menu-styler/menu-styler-features.ts new file mode 100644 index 0000000..e4c9734 --- /dev/null +++ b/extras/modules/menu-styler/menu-styler-features.ts @@ -0,0 +1,506 @@ +'use strict'; + +import {AmeCustomizable, AmeCustomizableViewModel} from '../../pro-customizables/assets/customizable.js'; + +declare const ameMenuStylerFeatureConfig: AmeMenuStylerJsFeatures.FeatureScriptConfig; + +export namespace AmeMenuStylerJsFeatures { + import PreviewUpdater = AmeCustomizable.PreviewUpdater; + import SimpleVm = AmeCustomizableViewModel.SimpleVm; + const $ = jQuery; + + interface FeatureConfig { + settings: T; + settingMap: SettingMap; + } + + interface FeatureSettings { + } + + interface SettingMap { + [settingId: string]: keyof T; + } + + abstract class StylerJsFeature implements PreviewUpdater { + protected readonly initialSettings: S; + protected readonly settingMap: SettingMap; + + protected activeSettings: S; + + protected constructor(config: FeatureConfig) { + this.initialSettings = config.settings; + this.activeSettings = this.initialSettings; + this.settingMap = config.settingMap; + //Note: Subclasses should call `this.update(this.initialSettings)` in their constructor. + //It is not done here because the subclass constructor may need to do some setup first. + } + + clearPreview(): void { + this.update(this.initialSettings); + }; + + preview(settingId: string, value: any, getSettingValue: AmeCustomizable.SettingValueReader): void { + const localKey = this.settingMap[settingId]; + if (typeof localKey === 'undefined') { + if (console.warn) { + console.warn( + 'Preview failed: The feature "' + this.getFeatureId() + + '" does not use the setting "' + settingId + '".' + ); + } + return; + } + + let newSettings: S = {...this.activeSettings}; + newSettings[localKey] = value; + + //Get any known but missing settings using the callback. + for (const settingId in this.settingMap) { + if (this.settingMap.hasOwnProperty(settingId)) { + const localKey = this.settingMap[settingId]; + if (!newSettings.hasOwnProperty(localKey)) { + newSettings[localKey] = getSettingValue(settingId, null); + } + } + } + + this.update(newSettings); + } + + protected update(settings: S): void { + this.activeSettings = settings; + } + + /** + * Get the setting IDs that this feature uses. The feature can preview any of these settings. + */ + getPreviewableSettingIds(): string[] { + return Object.keys(this.settingMap); + } + + abstract getFeatureId(): string; + } + + interface CollapseButtonTextSettings extends FeatureSettings { + label: string; + } + + export class CollapseButtonTextFeature extends StylerJsFeature { + protected originalLabel: string | null = null; + + constructor(config: FeatureConfig) { + super(config); + this.update(this.initialSettings); + } + + protected update(settings: CollapseButtonTextSettings): void { + super.update(settings); + + const $label = $('#adminmenu #collapse-button .collapse-button-label'); + if (this.originalLabel === null) { + this.originalLabel = $label.text(); + } + + if ((typeof settings['label'] === 'undefined') || (settings.label === '') || (settings.label === null)) { + $label.text(this.originalLabel); + } else { + $label.text(settings.label); + } + } + + getFeatureId(): string { + return 'CollapseButtonTextFeature'; + } + } + + interface ImageSettingValue { + attachmentId: number | null; + attachmentSiteId: number | null; + attachmentUrl: string; + externalUrl: string | null; + width: number | null; + height: number | null; + } + + interface MenuLogoSettings extends FeatureSettings { + baseImage: ImageSettingValue | null; + collapsedImage: ImageSettingValue | null; + linkUrl: string | null; + backgroundColor: string | null; + baseHeight: number | null; + collapsedHeight: number | null; + } + + export class MenuLogoFeature extends StylerJsFeature { + protected $container: JQuery | null = null; + protected $link: JQuery | null = null; + protected $styleElement: JQuery | null = null; + + private readonly logoId = 'ame_ms_admin_menu_logo'; + private readonly linkId = 'ame_ms_menu_logo_link'; + + /* + * Note: The logo container is set up so that the logo image is inside the content + * box (i.e. it does not overlap the padding or the margin), but the logo link covers + * the padding area. This way the user can control the clickable (padding) area and + * the unclickable (margin) area separately. + */ + + private readonly staticLogoStyles = [ + 'background-size: contain;', + 'background-repeat: no-repeat;', + 'background-position: 0;', + 'background-origin: content-box;', + 'min-height: 10px;', + 'position: relative;', + 'display: block;', + 'box-sizing: content-box;', + ]; + + private readonly staticLinkCss = `#adminmenu #${this.linkId} { + display: block; + position: absolute; + top: 0; left: 0; right: 0; bottom: 0; + margin: 0; + padding: 0; + background: transparent; + text-decoration: none; + } + #adminmenu #${this.linkId}:hover, #adminmenu #${this.linkId}:focus { + box-shadow: none; + transition: none; + color: transparent; + }`; + + private defaultMenuMarginTop: number | null = null; + private defaultMenuMarginBottom: number | null = null; + + constructor(config: FeatureConfig) { + super(config); + this.update(this.initialSettings); + } + + protected update(config: MenuLogoSettings) { + super.update(config); + this.updateFromActiveSettings(); + } + + private updateFromActiveSettings(): void { + const config = this.activeSettings; //Local reference in case the config changes while loading images. + + const hasBaseImage = this.settingContainsImage(config.baseImage); + const hasCollapsedImage = this.settingContainsImage(config.collapsedImage); + if (!hasBaseImage && !hasCollapsedImage) { + this.removeLogo(); + return; + } + + this.withLogoImages(config, (baseImageUrl, collapsedImageUrl) => { + const [$styleElement, $link] = this.getOrCreateElements(); + let baseLogoStyles = []; + let collapsedLogoStyles = []; + + let hasBaseLogo = false; + let hasCollapsedLogo = false; + + if (baseImageUrl) { + hasBaseLogo = true; + baseLogoStyles.push(`background-image: url("${baseImageUrl}");`); + + const baseHeight = Math.max(config.baseHeight ? config.baseHeight : 10, 10); + baseLogoStyles.push(`height: ${baseHeight}px;`); + } else { + baseLogoStyles.push('display: none;') + } + if (collapsedImageUrl) { + hasCollapsedLogo = true; + collapsedLogoStyles.push(`background-image: url("${collapsedImageUrl}");`); + collapsedLogoStyles.push('display: block;'); + + const collapsedHeight = Math.max(config.collapsedHeight ? config.collapsedHeight : 10, 10); + collapsedLogoStyles.push(`height: ${collapsedHeight}px;`); + } else { + collapsedLogoStyles.push('display: none;'); + } + + if (config.backgroundColor) { + baseLogoStyles.push(`background-color: ${config.backgroundColor};`); + } + + const linkUrl = (typeof config.linkUrl === 'string') ? config.linkUrl.trim() : ''; + if (linkUrl) { + $link.show().attr('href', linkUrl); + } else { + $link.hide().removeAttr('href'); + } + + baseLogoStyles.unshift(...this.staticLogoStyles); + + const baseStyle = `#adminmenu #${this.logoId} {\n${baseLogoStyles.join('\n')} }`; + const collapsedStyle = `.folded #adminmenu #${this.logoId} {\n${collapsedLogoStyles.join('\n')} }`; + + //Remove the top margin from the admin menu when the logo is visible. + //We also need to let other AME components know that the vertical margin has changed. + //This affects the "Collapse button position" setting. + const [, menuMarginBottom] = this.getDefaultVerticalMenuMargins(); + let wrapperCss = `#adminmenuwrap { --ame-ms-menu-margin-bottom: ${menuMarginBottom}px; }`; + if (hasBaseLogo) { + wrapperCss += `body:not(.folded) #adminmenu { margin-top: 0; }\n`; + wrapperCss += `body:not(.folded) #adminmenuwrap { --ame-ms-menu-margin-top: 0px; }\n`; + } + if (hasCollapsedLogo) { + wrapperCss += `.folded #adminmenu { margin-top: 0; }\n`; + wrapperCss += `.folded #adminmenuwrap { --ame-ms-menu-margin-top: 0px; }\n`; + } + + $styleElement.text(baseStyle + "\n" + collapsedStyle + "\n" + this.staticLinkCss + "\n" + wrapperCss); + }); + } + + private settingContainsImage(setting: ImageSettingValue | null): boolean { + if (setting === null) { + return false; + } + return !!(((setting.attachmentId !== null) && (setting.attachmentId > 0)) || setting.externalUrl); + } + + private withLogoImages( + config: MenuLogoSettings, + callback: (baseImageUrl: string | null, collapsedImageUrl: string | null) => void + ): void { + let imageUrls = [ + this.getImageUrl(config.baseImage), + this.getImageUrl(config.collapsedImage), + ]; + + //Add the logo as quickly as possible to prevent the menu from visibly shifting. + //Promises are usually asynchronous, so let's avoid them when possible and call + //the callback immediately if both URLs are already known or invalid. + if ((typeof imageUrls[0] === 'string') && (typeof imageUrls[1] === 'string')) { + callback(imageUrls[0], imageUrls[1]); + return; + } + + //Assume that ES2020 is not available, so we can't use Promise.allSettled(). + //However, we want to wait for all promises to resolve, even if some fail. + Promise.all( + imageUrls.map((p) => { + //Convert known values to resolved promises. + if ((typeof p === 'string') || (p === null)) { + return Promise.resolve(p); + } + + return p.then( + (value) => (typeof value === 'string') ? value : null, + () => null //Convert errors to null. + ) as JQueryPromise; + }) + ).then((results) => { + //If the active config has changed, don't apply the results. + //The URLs that we just loaded might not be relevant any more. + if (this.activeSettings !== config) { + return; + } + + const [baseImageUrl, collapsedImageUrl] = results; + callback(baseImageUrl ?? null, collapsedImageUrl ?? null); + }); + } + + private getOrCreateElements(): [JQuery, JQuery] { + if (!this.$container) { + this.$container = $(`
  • `); + this.$link = $(``).appendTo(this.$container); + this.$container.prependTo('#adminmenu'); + } + if (!this.$styleElement) { + this.$styleElement = $('').appendTo('head'); + } + return [this.$styleElement, this.$link!]; + } + + private getImageUrl(imageSetting: ImageSettingValue | null): JQueryPromise | string | null { + if (imageSetting === null) { + return null; + } + + const externalUrl = (typeof imageSetting.externalUrl === 'string') ? imageSetting.externalUrl.trim() : ''; + if (externalUrl) { + return externalUrl; + } + + const attachmentId = imageSetting.attachmentId || 0; + //const attachmentSiteId = imageSetting.attachmentSiteId || 0; + if (attachmentId > 0) { + //Use the cached attachment URL if possible. + if (imageSetting.attachmentUrl) { + return imageSetting.attachmentUrl; + } + + //Load the attachment URL from the server. + if ((typeof wp !== 'undefined') && wp.media && wp.media.attachment) { + //Maybe it's already loaded? + let attachmentUrl = wp.media.attachment(attachmentId).get('url'); + if (attachmentUrl) { + return attachmentUrl; + } + + const deferredLoader = $.Deferred(); + wp.media.attachment(attachmentId).fetch().then( + //Success + (attachment: any) => { + if (attachment && attachment.url) { + deferredLoader.resolve(attachment.url); + } else { + deferredLoader.reject(); + } + }, + //Error + () => deferredLoader.reject() + ); + return deferredLoader.promise(); + } + } + + //No image. + return null; + } + + private getDefaultVerticalMenuMargins(): [number, number] { + if ((this.defaultMenuMarginTop === null) || (this.defaultMenuMarginBottom === null)) { + //Get the vertical margins of the admin menu. The value includes the "px" suffix, + //but parseInt() will ignore it. + const $adminmenu = $('#adminmenu'); + this.defaultMenuMarginTop = parseInt($adminmenu.css('margin-top'), 10); + this.defaultMenuMarginBottom = parseInt($adminmenu.css('margin-bottom'), 10); + if (isNaN(this.defaultMenuMarginTop)) { + this.defaultMenuMarginTop = 0; + } + if (isNaN(this.defaultMenuMarginBottom)) { + this.defaultMenuMarginBottom = 0; + } + } + return [this.defaultMenuMarginTop, this.defaultMenuMarginBottom]; + } + + private removeLogo(): void { + if (this.$container) { + this.$container.remove(); + this.$container = null; + this.$link = null; + } + if (this.$styleElement) { + this.$styleElement.remove(); + this.$styleElement = null; + } + } + + getFeatureId(): string { + return 'MenuLogoFeature'; + } + } + + export interface FeatureScriptConfig { + collapseButtonText?: FeatureConfig; + menuLogo?: FeatureConfig; + } + + //Always initialize the features if their config is available. + //They work normally on most admin pages, and are used for preview on the settings page. + export let collapseButtonFeature: CollapseButtonTextFeature | null = null; + export let menuLogoFeature: MenuLogoFeature | null = null; + const collapseButtonFeatureKey = 'ameMenuStyler_collapseButtonTextFt'; + const menuLogoFeatureKey = 'ameMenuStyler_menuLogoFt'; + + let isInitialized = false; + + function createFeatureInstances() { + if (isInitialized) { + return; + } + isInitialized = true; + + //If the script is loaded multiple times, the features might already exist. + //This can happen because the script is both enqueued normally and imported + //as a module on the settings page. + //We want each feature to be initialized only once, so we'll store them + //in the window object and reuse them. + if (ameMenuStylerFeatureConfig.collapseButtonText) { + if (window[collapseButtonFeatureKey]) { + collapseButtonFeature = window[collapseButtonFeatureKey]; + } else { + collapseButtonFeature = new CollapseButtonTextFeature(ameMenuStylerFeatureConfig.collapseButtonText); + window[collapseButtonFeatureKey] = collapseButtonFeature; + } + } + if (ameMenuStylerFeatureConfig.menuLogo) { + if (window[menuLogoFeatureKey]) { + menuLogoFeature = window[menuLogoFeatureKey]; + } else { + menuLogoFeature = new MenuLogoFeature(ameMenuStylerFeatureConfig.menuLogo); + window[menuLogoFeatureKey] = menuLogoFeature; + } + } + + /** + * Register the features with the Admin Customizer preview handler, if active. + * + * @param {AmeAdminCustomizerPreview.PreviewHandler} previewHandler + */ + function registerFeaturePreview(previewHandler: any) { + //Both features should exist in the AC preview, but let's check just in case. + if (!collapseButtonFeature || !menuLogoFeature) { + console.warn('Menu Styler: One or more features are not initialized in AC preview.'); + return; + } + previewHandler.registerPreviewUpdater( + collapseButtonFeature.getPreviewableSettingIds(), + collapseButtonFeature + ); + previewHandler.registerPreviewUpdater( + menuLogoFeature.getPreviewableSettingIds(), + menuLogoFeature + ); + } + + if (typeof window['wsAdminCustomizerPreview'] !== 'undefined') { + registerFeaturePreview(window['wsAdminCustomizerPreview']); + } else { + $(document).on('adminMenuEditor:acPreviewStart', (event, previewHandler) => { + registerFeaturePreview(previewHandler); + }); + } + } + + //The #adminmenu element must be available before initialization. The DOMContentLoaded event + //works, but we can better avoid a visible change/FOUC by using a custom event that the plugin + //triggers immediately after WordPress outputs the admin menu. + + $(document).one('adminMenuEditor:menuDomReady', createFeatureInstances); + $(createFeatureInstances); + + //Register the features with the menu styler dialog. + $(document).on( + 'adminMenuEditor:menuStylerUiRegister', + function (_unused, vm: SimpleVm) { + if (!vm) { + return; + } + createFeatureInstances(); + + if (collapseButtonFeature) { + vm.registerPreviewUpdater(collapseButtonFeature.getPreviewableSettingIds(), collapseButtonFeature); + } + if (menuLogoFeature) { + vm.registerPreviewUpdater(menuLogoFeature.getPreviewableSettingIds(), menuLogoFeature); + } + } + ); +} + +declare global { + interface Window { + ameMenuStyler_menuLogoFt?: AmeMenuStylerJsFeatures.MenuLogoFeature; + ameMenuStyler_collapseButtonTextFt?: AmeMenuStylerJsFeatures.CollapseButtonTextFeature; + } +} \ No newline at end of file diff --git a/extras/modules/menu-styler/menu-styler-template.php b/extras/modules/menu-styler/menu-styler-template.php new file mode 100644 index 0000000..98236d0 --- /dev/null +++ b/extras/modules/menu-styler/menu-styler-template.php @@ -0,0 +1,40 @@ + +
    +
    +
    +
    + renderStructure($structure); + ?> +
    +
    + 'click: onConfirmDialog.bind($data)', + ] + ); + ?> + +
    + +
    + + +
    +
    +
    +
    \ No newline at end of file diff --git a/extras/modules/menu-styler/menu-styler-ui.js b/extras/modules/menu-styler/menu-styler-ui.js new file mode 100644 index 0000000..32016e7 --- /dev/null +++ b/extras/modules/menu-styler/menu-styler-ui.js @@ -0,0 +1,236 @@ +/// +/// +import { AmeStyleGenerator } from '../../style-generator/style-generator.js'; +import { AmeCustomizableViewModel } from '../../pro-customizables/assets/customizable.js'; +jQuery(function ($) { + const _ = wsAmeLodash; + const styleConfigKey = 'menu_styles'; + /** + * Utility class that tells WordPress to pin or unpin the admin menu as needed + * when the menu dimensions or the top margin change. + * + * Uses throttling to avoid excessive updates. + */ + class StickyMenuUpdater { + constructor() { + this.$document = $(document); + this.$adminmenu = $('#adminmenu'); + this.previousHeight = null; + this.previousWidth = null; + this.previousTopMargin = null; + this.updateMenuPinState = _.throttle(() => { + const menuHeight = this.$adminmenu.outerHeight(); + const menuWidth = this.$adminmenu.outerWidth(); + const topMargin = parseInt(this.$adminmenu.css('margin-top'), 10); + if ((menuHeight !== this.previousHeight) + || (menuWidth !== this.previousWidth) + || (topMargin !== this.previousTopMargin)) { + this.previousHeight = menuHeight; + this.previousWidth = menuWidth; + this.previousTopMargin = topMargin; + //In practice, this update doesn't always work if done immediately. + //Not sure why, maybe menu dimensions don't change instantly when, for example, + //the user adds a logo image. Adding a small delay seems to help. + window.requestAnimationFrame(() => { + //The custom "wp-pin-menu" event was added to WP core in 2015. It can be used to update + //the menu "sticky" state. I'm using triggerHandler() instead of trigger() because this + //is what /wp-admin/js/widgets.js does. Hopefully, that will improve compatibility. + this.$document.triggerHandler('wp-pin-menu'); + }); + } + }, 1000, { leading: true, trailing: true }); + } + queueUpdate() { + this.updateMenuPinState(); + } + } + class MenuStylerViewModel extends AmeCustomizableViewModel.SimpleVm { + constructor() { + /** + * This observable is initially stored in a local variable because TypeScript doesn't + * allow accessing `this` in the constructor before calling super(), but we still + * want to establish a dependency on the dialog open state so that preview gets enabled + * when the dialog is open. The observable will get updated later. + */ + const extraPreviewCondition = ko.observable(false); + super(extraPreviewCondition); + this.isFirstOpen = true; + this.$dialog = null; + this.stickyMenuUpdater = new StickyMenuUpdater(); + this.dialogOpenObservable = extraPreviewCondition; + this.previewPreference = new WsAmePreferenceCookie('MsPreviewEnabled', 90, true); + //Read settings from the currently loaded admin menu configuration + //using the aux-data API. Setting ID prefixes should already be registered. + const auxDataSettingReader = (settingId, defaultValue) => { + const path = AmeEditorApi.configDataAdapter.mapSettingIdToPath(settingId); + if (path === null) { + return defaultValue; + } + const value = AmeEditorApi.configDataAdapter.getPath(path, this.notFound); + if (value !== this.notFound) { + return value; + } + else if (ameMenuStylerConfig.defaults.hasOwnProperty(settingId)) { + return ameMenuStylerConfig.defaults[settingId]; + } + else { + throw new Error('Unknown aux config setting ID: ' + settingId); + } + }; + for (const auxPrefix of AmeEditorApi.configDataAdapter.getKnownPrefixes()) { + this.registerSettingReader(auxDataSettingReader, auxPrefix); + } + for (const previewConfig of ameMenuStylerConfig.stylePreviewConfigs) { + const previewInstance = new AmeStyleGenerator.Preview.StyleGeneratorPreview(previewConfig); + this.registerPreviewUpdater(previewInstance.getPreviewableSettingIDs(), previewInstance); + } + $(document).trigger('adminMenuEditor:menuStylerUiRegister', [this]); + } + saveChanges() { + const settingsById = this.getAllSettingValues(); + //Sort by length of the setting ID and then by the ID itself to ensure parent settings + //are updated before their children. For example, this matters for color presets where + //the "activePreset" setting maps to the "[global]" property of the "colorPresets" setting. + const sortedIds = Object.keys(settingsById); + sortedIds.sort((a, b) => { + if (a.length !== b.length) { + return a.length - b.length; + } + return a.localeCompare(b); + }); + //Write all settings into a new object, then save the top-level properties + //of that. This way stale and empty settings will automatically be removed. + const updatedConfig = {}; + for (const settingId of sortedIds) { + const path = AmeEditorApi.configDataAdapter.mapSettingIdToPath(settingId); + if (path === null) { + continue; + } + const value = settingsById[settingId]; + //To save space, don't store null values. This could be extended by using + //the "deleteWhenBlank" property of the setting definition. + if (value === null) { + continue; + } + _.set(updatedConfig, path, value); + } + //Special: Update the last modified timestamp for menu styles. + _.set(updatedConfig, [styleConfigKey, '_lastModified'], (new Date()).toISOString()); + //Special: Remove empty color presets. + const colorPresets = _.get(updatedConfig, ['color_presets'], {}); + for (const presetName of Object.keys(colorPresets)) { + //Remove empty string values (i.e. no color selected). This also + //covers nulls and empty arrays/objects, but that shouldn't happen. + colorPresets[presetName] = _.omit(colorPresets[presetName], _.isEmpty); + //Remove the preset if it's empty. + if (_.isEmpty(colorPresets[presetName])) { + delete colorPresets[presetName]; + } + } + //Finally, write the top-level properties to the menu configuration. + for (const key in updatedConfig) { + if (!updatedConfig.hasOwnProperty(key)) { + continue; + } + const value = updatedConfig[key]; + AmeEditorApi.configDataAdapter.setPath(key, value); + } + } + isDialogOpen(newValue = null) { + if (!this.dialogOpenObservable) { + return false; + } + if (newValue !== null) { + this.dialogOpenObservable(newValue); + return newValue; + } + return this.dialogOpenObservable(); + } + getPreviewActiveState() { + //Disable preview when the dialog is not open. + if (!this.isDialogOpen()) { + return false; + } + return super.getPreviewActiveState(); + } + updatePreview(settingIds) { + super.updatePreview(settingIds); + this.stickyMenuUpdater.queueUpdate(); + } + setDialog($dialog) { + this.$dialog = $dialog; + let $overlay = null; + $dialog.on('dialogopen', () => { + this.isDialogOpen(true); + this.onOpenDialog(); + //Add a custom class to the overlay so that we can style it. + $overlay = $dialog.closest('.ui-dialog').nextAll('.ui-widget-overlay').first(); + $overlay.addClass('ame-ms-dialog-overlay'); + }); + $dialog.on('dialogclose', () => { + this.isDialogOpen(false); + if ($overlay) { + $overlay.removeClass('ame-ms-dialog-overlay'); + $overlay = null; + } + }); + } + onOpenDialog() { + if (!this.isFirstOpen) { + this.reloadAllSettings(); + } + if (this.isFirstOpen) { + this.isFirstOpen = false; + //Load the preview state from a cookie. + this.isPreviewEnabled(this.previewPreference.readAndRefresh(true)); + } + } + // noinspection JSUnusedGlobalSymbols -- Used in the KO template. + onConfirmDialog() { + //Save the preview state in a cookie. + this.previewPreference.write(this.isPreviewEnabled()); + this.saveChanges(); + this.closeDialog(); + } + onCancelDialog() { + this.closeDialog(); + } + closeDialog() { + if (this.$dialog !== null) { + this.$dialog.dialog('close'); + } + } + } + const $styleDialog = $('#ws-ame-menu-style-settings'); + let isDialogInitialized = false; + function initializeDialog() { + $styleDialog.dialog({ + autoOpen: false, + closeText: ' ', + draggable: false, + modal: true, + //Dialog dimensions and position are set in CSS. + minWidth: 300, + height: 400, + classes: { + 'ui-dialog': 'ui-corner-all ws-ame-menu-style-dialog', + } + }); + isDialogInitialized = true; + const vm = new MenuStylerViewModel(); + window['ameMenuStylerVm'] = vm; + ko.applyBindings(vm, $styleDialog[0]); + vm.setDialog($styleDialog); + } + //Open the dialog when the user clicks the style button. + $('#ws_edit_menu_styles').on('click', () => { + //Optimization: Initialize the dialog on the first click. + if (!isDialogInitialized) { + initializeDialog(); + } + //Reset the scroll position of the tab content area. + $styleDialog.find('.ame-tp-content').scrollTop(0); + $styleDialog.dialog('open'); + }); +}); +//# sourceMappingURL=menu-styler-ui.js.map \ No newline at end of file diff --git a/extras/modules/menu-styler/menu-styler-ui.js.map b/extras/modules/menu-styler/menu-styler-ui.js.map new file mode 100644 index 0000000..8328201 --- /dev/null +++ b/extras/modules/menu-styler/menu-styler-ui.js.map @@ -0,0 +1 @@ +{"version":3,"file":"menu-styler-ui.js","sourceRoot":"","sources":["menu-styler-ui.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/menu-styler/menu-styler-ui.ts b/extras/modules/menu-styler/menu-styler-ui.ts new file mode 100644 index 0000000..8158117 --- /dev/null +++ b/extras/modules/menu-styler/menu-styler-ui.ts @@ -0,0 +1,291 @@ +/// +/// + +import {AmeStyleGenerator} from '../../style-generator/style-generator.js'; +import {AmeCustomizableViewModel} from '../../pro-customizables/assets/customizable.js'; + +declare var wsAmeLodash: _.LoDashStatic; + +declare const ameMenuStylerConfig: { + defaults: Record, + stylePreviewConfigs: AmeStyleGenerator.Preview.StyleGeneratorPreviewConfig[] +}; + +jQuery(function ($: JQueryStatic) { + const _ = wsAmeLodash; + + const styleConfigKey = 'menu_styles'; + + /** + * Utility class that tells WordPress to pin or unpin the admin menu as needed + * when the menu dimensions or the top margin change. + * + * Uses throttling to avoid excessive updates. + */ + class StickyMenuUpdater { + private readonly $document = $(document); + private readonly $adminmenu = $('#adminmenu'); + + private previousHeight: number | null = null; + private previousWidth: number | null = null; + private previousTopMargin: number | null = null; + + private readonly updateMenuPinState = _.throttle(() => { + const menuHeight = this.$adminmenu.outerHeight(); + const menuWidth = this.$adminmenu.outerWidth(); + const topMargin = parseInt(this.$adminmenu.css('margin-top'), 10); + + if ( + (menuHeight !== this.previousHeight) + || (menuWidth !== this.previousWidth) + || (topMargin !== this.previousTopMargin) + ) { + this.previousHeight = menuHeight; + this.previousWidth = menuWidth; + this.previousTopMargin = topMargin; + + //In practice, this update doesn't always work if done immediately. + //Not sure why, maybe menu dimensions don't change instantly when, for example, + //the user adds a logo image. Adding a small delay seems to help. + window.requestAnimationFrame(() => { + //The custom "wp-pin-menu" event was added to WP core in 2015. It can be used to update + //the menu "sticky" state. I'm using triggerHandler() instead of trigger() because this + //is what /wp-admin/js/widgets.js does. Hopefully, that will improve compatibility. + this.$document.triggerHandler('wp-pin-menu'); + }); + } + }, 1000, {leading: true, trailing: true}); + + public queueUpdate(): void { + this.updateMenuPinState(); + } + } + + class MenuStylerViewModel extends AmeCustomizableViewModel.SimpleVm { + private readonly dialogOpenObservable: KnockoutObservable; + private isFirstOpen = true; + private $dialog: JQuery | null = null; + + private previewPreference: WsAmePreferenceCookie; + + private stickyMenuUpdater: StickyMenuUpdater = new StickyMenuUpdater(); + + constructor() { + /** + * This observable is initially stored in a local variable because TypeScript doesn't + * allow accessing `this` in the constructor before calling super(), but we still + * want to establish a dependency on the dialog open state so that preview gets enabled + * when the dialog is open. The observable will get updated later. + */ + const extraPreviewCondition = ko.observable(false); + + super(extraPreviewCondition); + + this.dialogOpenObservable = extraPreviewCondition; + this.previewPreference = new WsAmePreferenceCookie('MsPreviewEnabled', 90, true); + + //Read settings from the currently loaded admin menu configuration + //using the aux-data API. Setting ID prefixes should already be registered. + const auxDataSettingReader = (settingId: string, defaultValue: any): any => { + const path = AmeEditorApi.configDataAdapter.mapSettingIdToPath(settingId); + if (path === null) { + return defaultValue; + } + + const value = AmeEditorApi.configDataAdapter.getPath(path, this.notFound); + if (value !== this.notFound) { + return value; + } else if (ameMenuStylerConfig.defaults.hasOwnProperty(settingId)) { + return ameMenuStylerConfig.defaults[settingId]; + } else { + throw new Error('Unknown aux config setting ID: ' + settingId); + } + }; + for (const auxPrefix of AmeEditorApi.configDataAdapter.getKnownPrefixes()) { + this.registerSettingReader(auxDataSettingReader, auxPrefix); + } + + for (const previewConfig of ameMenuStylerConfig.stylePreviewConfigs) { + const previewInstance = new AmeStyleGenerator.Preview.StyleGeneratorPreview(previewConfig); + this.registerPreviewUpdater(previewInstance.getPreviewableSettingIDs(), previewInstance); + } + + $(document).trigger('adminMenuEditor:menuStylerUiRegister', [this]); + } + + saveChanges() { + const settingsById = this.getAllSettingValues(); + + //Sort by length of the setting ID and then by the ID itself to ensure parent settings + //are updated before their children. For example, this matters for color presets where + //the "activePreset" setting maps to the "[global]" property of the "colorPresets" setting. + const sortedIds = Object.keys(settingsById); + sortedIds.sort((a, b) => { + if (a.length !== b.length) { + return a.length - b.length; + } + return a.localeCompare(b); + }); + + //Write all settings into a new object, then save the top-level properties + //of that. This way stale and empty settings will automatically be removed. + const updatedConfig: Record = {}; + for (const settingId of sortedIds) { + const path = AmeEditorApi.configDataAdapter.mapSettingIdToPath(settingId); + if (path === null) { + continue; + } + + const value = settingsById[settingId]; + //To save space, don't store null values. This could be extended by using + //the "deleteWhenBlank" property of the setting definition. + if (value === null) { + continue; + } + _.set(updatedConfig, path, value); + } + + //Special: Update the last modified timestamp for menu styles. + _.set(updatedConfig, [styleConfigKey, '_lastModified'], (new Date()).toISOString()); + + //Special: Remove empty color presets. + const colorPresets: Record = _.get(updatedConfig, ['color_presets'], {}); + for (const presetName of Object.keys(colorPresets)) { + //Remove empty string values (i.e. no color selected). This also + //covers nulls and empty arrays/objects, but that shouldn't happen. + colorPresets[presetName] = _.omit(colorPresets[presetName], _.isEmpty); + //Remove the preset if it's empty. + if (_.isEmpty(colorPresets[presetName])) { + delete colorPresets[presetName]; + } + } + + //Finally, write the top-level properties to the menu configuration. + for (const key in updatedConfig) { + if (!updatedConfig.hasOwnProperty(key)) { + continue; + } + const value = updatedConfig[key]; + AmeEditorApi.configDataAdapter.setPath(key, value); + } + } + + protected isDialogOpen(newValue: boolean | null = null): boolean { + if (!this.dialogOpenObservable) { + return false; + } + + if (newValue !== null) { + this.dialogOpenObservable(newValue); + return newValue; + } + return this.dialogOpenObservable(); + } + + protected getPreviewActiveState(): boolean { + //Disable preview when the dialog is not open. + if (!this.isDialogOpen()) { + return false; + } + return super.getPreviewActiveState(); + } + + updatePreview(settingIds: string[]) { + super.updatePreview(settingIds); + this.stickyMenuUpdater.queueUpdate(); + } + + setDialog($dialog: JQuery) { + this.$dialog = $dialog; + + let $overlay: JQuery | null = null; + + $dialog.on('dialogopen', () => { + this.isDialogOpen(true); + this.onOpenDialog(); + + //Add a custom class to the overlay so that we can style it. + $overlay = $dialog.closest('.ui-dialog').nextAll('.ui-widget-overlay').first(); + $overlay.addClass('ame-ms-dialog-overlay'); + }); + $dialog.on('dialogclose', () => { + this.isDialogOpen(false); + + if ($overlay) { + $overlay.removeClass('ame-ms-dialog-overlay'); + $overlay = null; + } + }); + } + + protected onOpenDialog() { + if (!this.isFirstOpen) { + this.reloadAllSettings(); + } + + if (this.isFirstOpen) { + this.isFirstOpen = false; + //Load the preview state from a cookie. + this.isPreviewEnabled(this.previewPreference.readAndRefresh(true)); + } + } + + // noinspection JSUnusedGlobalSymbols -- Used in the KO template. + onConfirmDialog() { + //Save the preview state in a cookie. + this.previewPreference.write(this.isPreviewEnabled()); + + this.saveChanges(); + this.closeDialog(); + } + + onCancelDialog() { + this.closeDialog(); + } + + protected closeDialog() { + if (this.$dialog !== null) { + this.$dialog.dialog('close'); + } + } + } + + const $styleDialog = $('#ws-ame-menu-style-settings'); + let isDialogInitialized = false; + + function initializeDialog() { + $styleDialog.dialog({ + autoOpen: false, + closeText: ' ', + draggable: false, + modal: true, + //Dialog dimensions and position are set in CSS. + minWidth: 300, + height: 400, + classes: { + 'ui-dialog': 'ui-corner-all ws-ame-menu-style-dialog', + } + }); + + isDialogInitialized = true; + + const vm = new MenuStylerViewModel(); + (window as any)['ameMenuStylerVm'] = vm; + + ko.applyBindings(vm, $styleDialog[0]); + vm.setDialog($styleDialog); + } + + //Open the dialog when the user clicks the style button. + $('#ws_edit_menu_styles').on('click', () => { + //Optimization: Initialize the dialog on the first click. + if (!isDialogInitialized) { + initializeDialog(); + } + + //Reset the scroll position of the tab content area. + $styleDialog.find('.ame-tp-content').scrollTop(0); + + $styleDialog.dialog('open'); + }); +}); \ No newline at end of file diff --git a/extras/modules/menu-styler/menu-styler.css b/extras/modules/menu-styler/menu-styler.css new file mode 100644 index 0000000..37ee75e --- /dev/null +++ b/extras/modules/menu-styler/menu-styler.css @@ -0,0 +1,79 @@ +#ws-ame-menu-style-settings { + padding: 0; +} +#ws-ame-menu-style-settings .ws_dialog_buttons { + flex-shrink: 0; + flex-grow: 0; + padding: 8px; + margin-top: 0; + border-top: 1px solid #dcdcde; + background: #fcfcfc; +} + +#ws-ame-ms-dialog-wrapper { + display: flex; + flex-direction: column; + height: 100%; +} + +#ws-ame-ms-dialog-content { + flex-shrink: 1; + flex-grow: 1; + min-height: 100px; +} + +#ws-ame-ms-preview-box-container { + display: inline-block; + margin: 0 12px; + line-height: 28px; +} + +/* +Position the style dialog relative to the admin menu and the edges of the screen, +overriding the default jQuery UI dialog positioning. + +The "ws-ame-menu-style-dialog" class is added by using the "classes" option when +creating the dialog. + */ +.ws-ame-menu-style-dialog { + --menu-based-offset: calc(var(--ame-ms-menu-width, 160px) + 10px); + --available-width: calc(100vw - var(--menu-based-offset) - 30px); + --dialog-width: max(min(var(--available-width), 810px), 200px); + --centering-based-offset: calc((100vw - var(--dialog-width)) / 2); + --dialog-left: max(var(--centering-based-offset), var(--menu-based-offset), 0px); + position: fixed !important; + bottom: 30px !important; + top: calc(var(--wp-admin--admin-bar--height, 32px) + 10px) !important; + left: var(--dialog-left) !important; + width: var(--dialog-width) !important; + height: auto !important; + display: flex; + flex-direction: column; +} +.ws-ame-menu-style-dialog .ui-dialog-titlebar { + flex-grow: 0; + flex-shrink: 0; +} +.ws-ame-menu-style-dialog .ui-dialog-content { + flex-grow: 1; + flex-shrink: 1; +} + +.ui-widget-overlay.ame-ms-dialog-overlay { + background-color: transparent; + --left-offset: calc(var(--ame-ms-menu-width, 160px) + 20px); + background-image: linear-gradient(to right, transparent 0, transparent var(--left-offset), rgba(0, 0, 0, 0.7) var(--left-offset)); + opacity: 1; +} +.ui-widget-overlay.ame-ms-dialog-overlay:before { + content: ""; + display: block; + position: fixed; + top: 0; + left: 0; + width: var(--left-offset); + height: 100%; + cursor: not-allowed; +} + +/*# sourceMappingURL=menu-styler.css.map */ diff --git a/extras/modules/menu-styler/menu-styler.css.map b/extras/modules/menu-styler/menu-styler.css.map new file mode 100644 index 0000000..b35f9b1 --- /dev/null +++ b/extras/modules/menu-styler/menu-styler.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["menu-styler.scss"],"names":[],"mappings":"AAAA;EACC;;AAEA;EACC;EACA;EAEA;EACA;EACA;EAEA;;;AAIF;EACC;EACA;EACA;;;AAID;EACC;EACA;EACA;;;AAGD;EACC;EACA;EAGA;;;AAGD;AAAA;AAAA;;AAAA;AAAA;AAAA;AAOA;EAOC;EAIA;EAEA;EAEA;EAIA;EAEA;EAEA;EAEA;EAEA;EACA;EACA;EAEA;EACA;;AAEA;EACC;EACA;;AAGD;EACC;EACA;;;AAIF;EAGC;EAEA;EACA;EAOA;;AAIA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA","file":"menu-styler.css"} \ No newline at end of file diff --git a/extras/modules/menu-styler/menu-styler.php b/extras/modules/menu-styler/menu-styler.php new file mode 100644 index 0000000..9cf083b --- /dev/null +++ b/extras/modules/menu-styler/menu-styler.php @@ -0,0 +1,866 @@ +settings !== null ) { + return $this->settings; + } + + if ( ($menuConfigId === null) ) { + $helper = MenuScopedStylesheetHelper::getInstance($this->menuEditor); + $menuConfigId = $helper->getConfigIdFromAjaxRequest(); + } + + $this->settings = new StyleSettings( + new MenuStylerStorage($this->menuEditor, $menuConfigId) + ); + return $this->settings; + } + + protected function getInterfaceStructure() { + $settings = $this->getSettings(); + $b = $settings->elementBuilder(); + $structure = $b->structure( + $b->section( + 'Menu Bar', + $b->number('menuBar.menuWidth')->unitText('px')->params(['step' => 1]), + $b->number('menuBar.submenuPopupWidth')->params(['step' => 1]), + $b->number('menuBar.collapsedMenuWidth')->params(['step' => 1]), + $b->auto('menuBar.layout'), + $b->auto('menuBar.boxShadow') + )->id('ame-ms-menuBar-section'), + $b->section( + 'Menu Items', + $b->autoSection('topLevelItems.font'), + $b->autoSection('topLevelItems.spacing') + ->add($b->html(sprintf( + '

    %s

    ', + esc_html( + 'Tip: Usually, the left padding needs to include the menu icon width, which is 36px by default.' + ) + ))) + ), + $b->section( + 'Submenus', + $b->autoSection('submenu.font'), + $b->autoSection('submenu.openSubmenuItemSpacing'), + $b->autoSection('submenu.popupSubmenuItemSpacing'), + $b->autoSection('submenu.boxShadow') + )->id('ame-ms-Submenus-section'), + $b->section( + 'Logo', + $b->auto('logo.baseImage'), + $b->auto('logo.baseHeight')->params(['step' => 1]), + $b->auto('logo.collapsedImage'), + $b->auto('logo.collapsedHeight')->params(['step' => 1]), + $b->auto('logo.backgroundColor'), + $b->auto('logo.linkUrl'), + $b->auto('logo.spacing') + ), + $b->section( + 'Collapse Button', + $b->checkBox('collapseButton.visible'), + $b->auto('collapseButton.position'), + $b->auto('collapseButton.label') + ) + ); + + //Let other modules add their own settings and UI elements. + do_action('admin_menu_editor-ms_ui_structure', $structure); + + return $structure->build(); + } + + public function outputDialog() { + $structure = $this->getInterfaceStructure(); + $renderer = new TabbedPanelRenderer(['ame-tp-height-100']); + + //The template will call $renderer->renderStructure($structure) for us. + require __DIR__ . '/menu-styler-template.php'; + + $renderer->enqueueDependencies(); + } + + public function addEditorDependencies($dependencies) { + $this->enqueueFeatureScript(true); + + //This script needs to be loaded before menu-editor.js so that it can set + //a "menuConfigurationLoaded" event handler before the editor loads the menu. + //Both scripts use the jQuery(function() { ... }) shortcut and run their + //initialization code when the DOM is ready. + ScriptDependency::create( + plugins_url('menu-styler-ui.js', __FILE__), + self::mainScriptHandle + ) + ->addDependencies( + 'jquery', + 'ame-customizable-settings', + 'ame-style-generator', + 'ame-lodash', + 'ame-jquery-cookie', + self::featureScriptHandle + ) + ->setTypeToModule() + ->register(); + + $dependencies[] = self::mainScriptHandle; + return $dependencies; + } + + /** + * Add JS data for the menu styler UI to the menu styler script. + * + * This can't be done in addEditorDependencies() because wp_add_inline_script() + * only works for scripts that are already enqueued. The script is registered + * as an editor dependency, so it only gets enqueued when menu-editor.js is enqueued. + */ + public function addScriptData() { + //Add "menu_styles" to the registered menu configuration child keys. + add_filter('admin_menu_editor-aux_data_config', [$this, 'addAuxDataConfig']); + + $scriptData = [ + 'defaults' => apply_filters( + 'admin_menu_editor-ms_ui_setting_defaults', + $this->getSettings()->getRecursiveDefaultsForJs() + ), + 'stylePreviewConfigs' => array_map( + function (StyleGenerator $generator) { + return $generator->getJsPreviewConfiguration(); + }, + $this->getAllStyleGenerators() + ), + ]; + + wp_add_inline_script( + self::mainScriptHandle, + sprintf( + 'window.ameMenuStylerConfig = (%s);', + wp_json_encode($scriptData) + ), + 'before' + ); + } + + public function loadMenuStylerSettings($menuConfig, $storedConfig) { + //Copy menu styler settings from the stored menu configuration to the validated menu configuration. + if ( isset($storedConfig['menu_styles']) ) { + $menuConfig['menu_styles'] = $storedConfig['menu_styles']; + } + return $menuConfig; + } + + private function getStyleGenerator(StyleSettings $s, $onlyAdminThemeStyles = false) { + //todo: This should create different instances if the setting argument is different. + if ( ($this->styleGenerator !== null) && !$onlyAdminThemeStyles ) { + return $this->styleGenerator; + } + + $g = new StyleGenerator(); + $g->setStylesheetsToDisableOnPreview(['link#ame-ms-custom-menu-styles-css']); + + //region Menu width + $g->addRuleSet( + ['#adminmenuback', '#adminmenuwrap', '#adminmenu', '#adminmenu .wp-has-current-submenu > .wp-submenu'], + [$s->getSetting('menuBar.menuWidth')] + ); + $g->addRuleSet( + ['#wpcontent', '#wpfooter'], + ['margin-left' => $s->getSetting('menuBar.menuWidth')] + ); + $g->addRuleSet( + ['#adminmenu .wp-submenu'], + ['left' => $s->getSetting('menuBar.menuWidth')] + ); + $g->addRuleSet( + [ + '.folded #adminmenuback', + '.folded #adminmenuwrap', + '.folded #adminmenu', + '.folded #adminmenu li.menu-top', + ], + [$s->getSetting('menuBar.collapsedMenuWidth')] + ); + $g->addRuleSet( + [ + '#adminmenu .wp-not-current-submenu .wp-submenu', + '.folded #adminmenu .wp-has-current-submenu .wp-submenu', + '#adminmenu .ame-has-deep-submenu:not(.ame-has-highlighted-item) > .wp-submenu', + ], + [$s->getSetting('menuBar.submenuPopupWidth')] + ); + //Let other components know the custom menu width. + $g->addRuleSet( + ['body'], + [ + '--ame-ms-menu-width' => $s->getSetting('menuBar.menuWidth'), + '--ame-ms-collapsed-menu-width' => $s->getSetting('menuBar.collapsedMenuWidth'), + ] + ); + //endregion + + //region Menu bar: Full height + $g->addSimpleCondition( + $s->getSetting('menuBar.layout'), + '==', + 'fullHeight', + new CssRuleSet( + [ + //Note: Selector specificity is intentionally increased to override + //the margin-top rule added by the menu logo feature. + '#adminmenu#adminmenu', + ], + ['margin-top' => 'calc(-1 * var(--wp-admin--admin-bar--height, 32px))'] + ), + //The "collapse button position: bottom" setting needs to know + //how far the menu is from the top of the viewport. + new CssRuleSet( + ['#adminmenuwrap'], + ['--ams-ms-menu-vp-top-offset' => '0px'] + ), + //Push the Toolbar/Admin Bar to the right to make room for the full-height menu. + //The default menu width is 160px. + //The width variables are defined in a separate ruleset because the generator will + //automatically remove the ruleset if all of its settings are empty. We want to keep + //the actual margin and width rules even then, just with default values. + new CssRuleSet( + ['#wpadminbar'], + [ + '--ame-ms-fh-menu-width' => $s->getSetting('menuBar.menuWidth'), + '--ame-ms-fh-collapsed-menu-width' => $s->getSetting('menuBar.collapsedMenuWidth'), + ] + ), + new CssRuleSet( + ['#wpadminbar'], + [ + 'margin-left' => 'var(--ame-ms-fh-menu-width, 160px)', + 'width' => 'calc(100% - var(--ame-ms-fh-menu-width, 160px))', + ] + ), + //Same for the collapsed menu. The default collapsed menu width is 32px. + new CssRuleSet( + ['.folded #wpadminbar'], + [ + 'margin-left' => 'var(--ame-ms-fh-collapsed-menu-width, 32px)', + 'width' => 'calc(100% - var(--ame-ms-fh-collapsed-menu-width, 32px))', + ] + ) + ); + //endregion + + //region Menu bar shadow + $g->addRuleSet( + ['#adminmenuback'], + [$s->getSetting('menuBar.boxShadow')] + ); + //endregion + + //region Top level menu items + $g->addRuleSet( + ['#adminmenu a.menu-top', '#adminmenu .wp-submenu-head'], + [$s->getSetting('topLevelItems.font')] + ); + //TODO: Icon alignment needs to be adjusted when the line height changes. + + $g->addRuleSet( + ['#adminmenu div.wp-menu-name'], + [$s->getSetting('topLevelItems.spacing.padding')] + ); + //Adjust menu icon alignment when the vertical padding changes so that the icon + //stays in the same place relative to the text. The default top padding is 8px. + $g->addSimpleCondition( + $s->getSetting('topLevelItems.spacing.padding.top'), + '>', + 0, + new CssRuleSet( + ['#adminmenu .menu-top .wp-menu-image'], + [ + '--ame-ms-item-top-padding' => $s->getSetting('topLevelItems.spacing.padding.top'), + 'padding-top' => 'max(calc(var(--ame-ms-item-top-padding) - 8px), 0px)', + ] + ) + ); + + $g->addRuleSet( + ['#adminmenu > li.menu-top'], + [$s->getSetting('topLevelItems.spacing.margin')] + ); + //endregion + + //region Submenus + $g->addRuleSet( + ['#adminmenu .wp-submenu a'], + [$s->getSetting('submenu.font')] + ); + $g->addRuleSet( + ['#adminmenu .wp-submenu'], + [$s->getSetting('submenu.boxShadow')] + ); + //TODO: Test with submenu icons. Custom CSS variables could help there. + $g->addRuleSet( + [ + '#adminmenu .wp-not-current-submenu li > a', + //Third-level menu popup that's inside the current submenu, + //but does not contain the current menu item. + '#adminmenu .wp-has-current-submenu li.ame-has-deep-submenu.opensub li > a', + ], + [$s->getSetting('submenu.popupSubmenuItemSpacing.padding')] + ); + $g->addRuleSet( + [ + '#adminmenu .wp-not-current-submenu li', + '#adminmenu .wp-has-current-submenu li.ame-has-deep-submenu.opensub li', + ], + [$s->getSetting('submenu.popupSubmenuItemSpacing.margin')] + ); + $g->addRuleSet( + ['#adminmenu .wp-has-current-submenu ul > li > a'], + [$s->getSetting('submenu.openSubmenuItemSpacing.padding')] + ); + $g->addRuleSet( + ['#adminmenu .wp-has-current-submenu ul > li'], + [$s->getSetting('submenu.openSubmenuItemSpacing.margin')] + ); + //endregion + + //The rest of the styles are not related to admin themes, so stop here if + //we're only generating admin theme styles. + if ( $onlyAdminThemeStyles ) { + return $g; + } + + //region Collapse button + $g->addSimpleCondition( + $s->getSetting('collapseButton.visible'), + '==', + false, + new CssRuleSet( + ['#adminmenu #collapse-menu'], + ['display' => 'none'] + ) + ); + $g->addSimpleCondition( + $s->getSetting('collapseButton.position'), + '==', + 'bottom', + new CssRuleSet( + ['#adminmenu'], + [ + //Switch the admin menu to flexbox layout. + 'display' => 'flex', + 'flex-direction' => 'column', + //Menu height must be equal or greater than (viewport height - menu offset from + //the top of the viewport) for the button to be positioned at the bottom of the screen. + //The top offset is usually equal to the height of the Admin Bar, but it can be + //zero if the menu is in "full height" mode. + //We must also account for the menu bar's vertical margins to avoid overflowing the viewport. + 'box-sizing' => 'border-box', + 'min-height' => 'calc( + 100vh + - var(--ams-ms-menu-vp-top-offset, var(--wp-admin--admin-bar--height, 32px)) + - var(--ame-ms-menu-margin-top, 12px) + - var(--ame-ms-menu-margin-bottom, 12px) + )', + ] + ), + //In flexbox layout, "margin-top: auto" works to move the button to the bottom. + new CssRuleSet( + ['#adminmenu #collapse-menu'], + ['margin-top' => 'auto'] + ) + ); + //endregion + + //region Logo + //Most logo settings are handled elsewhere. This is just for the margins and padding. + $g->addRuleSet( + ['#adminmenu #ame_ms_admin_menu_logo'], + [$s->getSetting('logo.spacing')] + ); + //When the menu is collapsed, there is far less space for the logo, so the margins + //and padding must be reduced. Since there are currently no separate settings for that + //state, we just drop the left/right margins and padding to zero. + $g->addRuleSet( + ['.folded #adminmenu #ame_ms_admin_menu_logo'], + [ + 'margin-left' => '0', + 'margin-right' => '0', + 'padding-left' => '0', + 'padding-right' => '0', + ] + ); + //endregion + + $this->styleGenerator = $g; + return $this->styleGenerator; + } + + /** + * Get all style generators associated with the menu styler UI. + * + * This exists because other modules can add their own settings to that UI. + * + * @param bool $onlyAdminThemeStyles + * @return StyleGenerator[] + */ + private function getAllStyleGenerators($onlyAdminThemeStyles = false) { + return apply_filters( + 'admin_menu_editor-ms_ui_style_generators', + [$this->getStyleGenerator($this->getSettings(), $onlyAdminThemeStyles)], + $onlyAdminThemeStyles + ); + } + + public function enqueueFeatureScript($isRequired = false) { + //Do this only once. + static $isScriptEnqueued = false, $featureScript = null; + if ( $isScriptEnqueued ) { + return; + } + + if ( !$featureScript ) { + $featureScript = ScriptDependency::create( + plugins_url('menu-styler-features.js', __FILE__), + self::featureScriptHandle + ) + ->addDependencies('jquery') + ->setTypeToModule() + //Adding the "async" attribute makes a module script execute sooner, + //which is useful to prevent FOUC. + //See https://gist.github.com/jakub-g/385ee6b41085303a53ad92c7c8afd7a6 + ->setAsync(); + } + + if ( !wp_script_is($featureScript->getHandle(), 'registered') ) { + $featureScript->register(); + } + + $settings = $this->getSettings(); + + //Enqueue the script if one of the relevant settings is in use, or if this is + //the AC preview frame, or if this is the settings page, This method handles + //the first two and addEditorDependencies() handles the last one by setting + //the $isRequired parameter to true. + $isRequired = $isRequired + || $this->menuEditor->is_editor_page() + || apply_filters('admin_menu_editor-is_preview_frame', false); + + $buttonRequired = $isRequired; + if ( !$buttonRequired ) { + $collapseButtonLabel = $settings->get('collapseButton.label'); + $buttonRequired = !empty($collapseButtonLabel); + } + + $logoRequired = $isRequired; + if ( !$logoRequired ) { + if ( $settings->get('logo.baseImage.attachmentId', 0) > 0 ) { + $logoRequired = true; + } else if ( (string)$settings->get('logo.baseImage.externalUrl', '') !== '' ) { + $logoRequired = true; + } else if ( (string)$settings->get('logo.collapsedImage.attachmentId', 0) > 0 ) { + $logoRequired = true; + } else if ( (string)$settings->get('logo.collapsedImage.externalUrl', '') !== '' ) { + $logoRequired = true; + } + } + + if ( !($buttonRequired || $logoRequired) ) { + return; + } + + $featureScript->enqueue(); + $isScriptEnqueued = true; + + $scriptData = []; + + $labelSettings = [ + 'collapseButton.label' => 'label', + ]; + $logoSettings = [ + //For attachments, the URL should already be cached in the setting. + 'logo.baseImage' => 'baseImage', + 'logo.collapsedImage' => 'collapsedImage', + 'logo.linkUrl' => 'linkUrl', + 'logo.backgroundColor' => 'backgroundColor', + 'logo.baseHeight' => 'baseHeight', + 'logo.collapsedHeight' => 'collapsedHeight', + ]; + + $scriptDataKeys = []; + if ( $buttonRequired ) { + $scriptDataKeys['collapseButtonText'] = $labelSettings; + } + if ( $logoRequired ) { + $scriptDataKeys['menuLogo'] = $logoSettings; + } + + foreach ($scriptDataKeys as $key => $pathToKeyMap) { + $settingValues = []; + $settingMap = []; + foreach ($pathToKeyMap as $path => $localKey) { + $setting = $settings->getSetting($path); + $settingValues[$localKey] = $setting->getValue(); + $settingMap[$setting->getId()] = $localKey; + } + $scriptData[$key] = [ + 'settings' => $settingValues, + 'settingMap' => $settingMap, + ]; + } + + wp_add_inline_script( + $featureScript->getHandle(), + sprintf( + 'window.ameMenuStylerFeatureConfig = (%s);', + wp_json_encode($scriptData) + ), + 'before' + ); + } + + public function enqueueEditorStyles() { + wp_enqueue_auto_versioned_style( + 'ame-menu-styler-editor-css', + plugins_url('menu-styler.css', __FILE__) + ); + } + + public function registerCustomStyle() { + $bundleName = 'ame-menu-style-bundle'; + $helper = MenuScopedStylesheetHelper::getInstance($this->menuEditor); + + //Disable bundling in preview mode to make things easier for JS-based preview + //updaters. This way each updater only needs to disable their own stylesheet, + //instead of disabling the whole bundle and potentially breaking other features + //that use the same bundle. + $isAdminCustomizerPreview = apply_filters('admin_menu_editor-is_preview_frame', false); + $queryParams = $this->menuEditor->get_query_params(); + $isPreview = ( + $isAdminCustomizerPreview + //The menu editor page also does live preview when the "Style" dialog is open. + //Note that we can't use $this->menuEditor->is_editor_page() here because + //the current tab is not set yet during the "init" action. + || ( + isset($queryParams['page']) + && is_admin() + && (!wp_doing_ajax()) + && ($queryParams['page'] === 'menu_editor') + && ( + empty($queryParams['sub_section']) + || ($queryParams['sub_section'] === 'editor') + || ($queryParams['sub_section'] === 'network-admin-menu') + ) + ) + ); + if ( !$isPreview ) { + $helper->addBundle($bundleName); + } + + $helper->addStylesheet( + 'ame-ms-custom-menu-styles', + function ($menuConfigId) { + $settings = $this->getSettings($menuConfigId); + + $modTimeCallback = function () use ($settings) { + $modificationTime = $settings->getLastModifiedTimestamp(); + return !empty($modificationTime) ? $modificationTime : 0; + }; + + $styleGenerationCallback = function () use ($settings) { + $styleGenerator = $this->getStyleGenerator($settings); + return $styleGenerator->generateCss(); + }; + + return [$modTimeCallback, $styleGenerationCallback]; + }, + $bundleName + ); + } + + public function addAuxDataConfig($config) { + $config['keys'][StyleSettings::CONFIG_KEY] = StyleSettings::SETTING_ID_PREFIX; + return $config; + } + + /** + * @param \YahnisElsts\AdminMenuEditor\AdminCustomizer\AmeAdminCustomizer $customizer + * @return void + */ + public function registerAdminCustomizerItems($customizer) { + //Register settings. + $settings = $this->getSettings(); + $customizer->addSettings($settings->getRegisteredSettings()); + + //Add menu style controls to the "Admin Menu" section. + $menuSectionOpt = $customizer->findSection('ame-admin-menu'); + if ( $menuSectionOpt->isEmpty() ) { + //Add the section if it doesn't exist yet. + $menuSection = new Section('Admin Menu', [], ['id' => 'ame-admin-menu']); + $customizer->addSection($menuSection); + } else { + $menuSection = $menuSectionOpt->get(); + } + + $myStructure = $this->getInterfaceStructure(); + foreach ($myStructure->getAsSections() as $section) { + $menuSection->add($section); + } + } + + /** + * @param \YahnisElsts\AdminMenuEditor\AdminCustomizer\AmeAdminCustomizer $customizer + * @return void + */ + public function registerAdminCustomizerStylePreview($customizer) { + foreach ($this->getAllStyleGenerators() as $generator) { + $customizer->addPreviewStyleGenerator($generator); + } + } + + /** + * + * @param callable $addCss + * @return void + * @internal + */ + public function addAdminThemeCss($addCss) { + $generators = $this->getAllStyleGenerators(true); + foreach ($generators as $g) { + call_user_func($addCss, $g->generateCss()); + } + } +} + +class StyleSettings extends AbstractSettingsDictionary { + const SETTING_ID_PREFIX = 'ws_menu_styler--'; + + const CONFIG_KEY = 'menu_styles'; + + public function __construct(StorageInterface $store) { + parent::__construct($store, self::SETTING_ID_PREFIX, true); + } + + protected function createDefaults() { + return []; + } + + protected function createSettings() { + $f = $this->settingFactory(); + + return [ + $f->boolean( + 'configProducesCss', + '[This internal flag shows if the current configuration generates any CSS when applied]', + [ + 'default' => null, + 'isEditable' => '__return_false', //Never directly editable. + ] + ), + $f->customStruct( + 'menuBar', + function (SettingFactory $cf) { + $cf->enablePostMessageSupport(); + $cf->setTags(AbstractSetting::TAG_ADMIN_THEME); + return [ + $cf->cssLength( + 'menuWidth', + 'Menu width', + 'width', + ['default' => null, 'minValue' => 30, 'maxValue' => 500] + ), + $cf->cssLength( + 'collapsedMenuWidth', + 'Collapsed menu width', + 'width', + ['default' => null, 'minValue' => 10, 'maxValue' => 100] + ), + $cf->cssLength( + 'submenuPopupWidth', + 'Submenu popup width', + 'width', + ['default' => null, 'minValue' => 30, 'maxValue' => 500] + ), + $cf->enum('layout', ['default', 'fullHeight'], 'Layout') + ->describeChoice('default', 'Default') + ->describeChoice('fullHeight', 'Full height menu'), + $cf->cssBoxShadow( + 'boxShadow', + 'Menu bar shadow' + ), + ]; + } + ), + $f->customStruct( + 'topLevelItems', + function (SettingFactory $cf) { + $cf->enablePostMessageSupport(); + $cf->setTags(AbstractSetting::TAG_ADMIN_THEME); + return [ + $cf->cssFont('font', 'Font'), + $cf->cssSpacing('spacing', 'Spacing'), + ]; + } + ), + $f->customStruct( + 'submenu', + function (SettingFactory $cf) { + $cf->enablePostMessageSupport(); + $cf->setTags(AbstractSetting::TAG_ADMIN_THEME); + return [ + $cf->cssFont('font', 'Font'), + $cf->cssSpacing('openSubmenuItemSpacing', 'Spacing: Open submenu items'), + $cf->cssSpacing('popupSubmenuItemSpacing', 'Spacing: Popup submenu items'), + $cf->cssBoxShadow('boxShadow', 'Submenu popup shadow'), + ]; + } + ), + + $f->customStruct( + 'logo', + function (SettingFactory $cf) { + $cf->enablePostMessageSupport(); + return [ + $cf->image('baseImage', 'Expanded menu logo'), + $cf->image('collapsedImage', 'Collapsed menu logo'), + $cf->cssLength( + 'baseHeight', + 'Logo height (expanded)', + 'height', + ['default' => 60, 'defaultUnit' => 'px', 'minValue' => 10, 'maxValue' => 200] + ), + $cf->cssLength( + 'collapsedHeight', + 'Logo height (collapsed)', + 'height', + ['default' => 34, 'defaultUnit' => 'px', 'minValue' => 10, 'maxValue' => 200] + ), + $cf->cssColor('backgroundColor', 'background-color', 'Background color'), + $cf->url('linkUrl', 'Logo link URL'), + $cf->cssSpacing('spacing', 'Logo Spacing'), + ]; + } + ), + + $f->customStruct( + 'collapseButton', + function (SettingFactory $cf) { + $cf->enablePostMessageSupport(); + return [ + $cf->boolean('visible', 'Show the "Collapse menu" button', [ + 'groupTitle' => 'Visibility', + 'default' => true, + ]), + $cf->enum('position', ['default', 'bottom'], 'Position'), + $cf->plainText('label', 'Text'), + ]; + } + ), + ]; + } +} + +class MenuStylerStorage extends LazyArrayStorage implements StorageInterface { + + private $menuEditor; + private $configId; + + public function __construct(\WPMenuEditor $menuEditor, $menuConfigId = null) { + $this->menuEditor = $menuEditor; + $this->configId = $menuConfigId; + parent::__construct(); + } + + private function getMenuConfigId() { + if ( $this->configId === null ) { + $this->configId = $this->menuEditor->get_loaded_menu_config_id(); + } + return $this->configId; + } + + protected function loadData() { + $configId = $this->getMenuConfigId(); + $customMenu = $this->menuEditor->load_custom_menu($configId); + if ( ($customMenu !== null) && !empty($customMenu[StyleSettings::CONFIG_KEY]) ) { + return $customMenu[StyleSettings::CONFIG_KEY]; + } + return []; + } + + protected function storeData($newData) { + $configId = $this->getMenuConfigId(); + + $customMenu = $this->menuEditor->load_custom_menu($configId); + if ( $customMenu === null ) { + //Design problem: Can't save menu styles without a custom menu. + //Note that this will throw an exception if the menu has not been initialized yet. + //For example, it might not work in an AJAX request. + $customMenu = $this->menuEditor->get_active_admin_menu(); + $configId = null; + } + $customMenu[StyleSettings::CONFIG_KEY] = $newData; + $this->menuEditor->set_custom_menu($customMenu, $configId); + } + + protected function deleteStoredData() { + $customMenu = $this->menuEditor->load_custom_menu($this->getMenuConfigId()); + if ( ($customMenu === null) || (empty($customMenu[StyleSettings::CONFIG_KEY])) ) { + return; + } + unset($customMenu[StyleSettings::CONFIG_KEY]); + $this->menuEditor->set_custom_menu($customMenu, $this->getMenuConfigId()); + } +} \ No newline at end of file diff --git a/extras/modules/menu-styler/menu-styler.scss b/extras/modules/menu-styler/menu-styler.scss new file mode 100644 index 0000000..793c54a --- /dev/null +++ b/extras/modules/menu-styler/menu-styler.scss @@ -0,0 +1,116 @@ +#ws-ame-menu-style-settings { + padding: 0; + + .ws_dialog_buttons { + flex-shrink: 0; + flex-grow: 0; + + padding: 8px; + margin-top: 0; + border-top: 1px solid #dcdcde; + + background: #fcfcfc; + } +} + +#ws-ame-ms-dialog-wrapper { + display: flex; + flex-direction: column; + height: 100%; + +} + +#ws-ame-ms-dialog-content { + flex-shrink: 1; + flex-grow: 1; + min-height: 100px; +} + +#ws-ame-ms-preview-box-container { + display: inline-block; + margin: 0 12px; + + //Align vertically with WordPress buttons. + line-height: 28px; +} + +/* +Position the style dialog relative to the admin menu and the edges of the screen, +overriding the default jQuery UI dialog positioning. + +The "ws-ame-menu-style-dialog" class is added by using the "classes" option when +creating the dialog. + */ +.ws-ame-menu-style-dialog { + $edgeOffset: 30px; + $elementOffset: 10px; + $maxDialogWidth: 810px; + + //The left edge of the dialog should be at least $elementOffset pixels away from + //the admin menu. + --menu-based-offset: calc(var(--ame-ms-menu-width, 160px) + #{$elementOffset}); + //The right edge should be at at least $edgeOffset pixels away from the right edge + //of the screen. Given the minimum left and right offsets, we can calculate how much + //horizontal space is available for the dialog. + --available-width: calc(100vw - var(--menu-based-offset) - #{$edgeOffset}); + //For readability, the dialog width should not exceed $maxDialogWidth. + --dialog-width: max(min(var(--available-width), #{$maxDialogWidth}), 200px); + //If the screen is large enough, center the dialog instead of aligning it to the menu. + --centering-based-offset: calc((100vw - var(--dialog-width)) / 2); + + //The final offset is the maximum of the two (centered or aligned to the menu). + //Fall back to 0px in case we end up with negative offsets on small screens. + --dialog-left: max(var(--centering-based-offset), var(--menu-based-offset), 0px); + + position: fixed !important; + + bottom: $edgeOffset !important; + //Show below the Admin Bar. + top: calc(var(--wp-admin--admin-bar--height, 32px) + $elementOffset) !important; + + left: var(--dialog-left) !important; + width: var(--dialog-width) !important; + height: auto !important; + + display: flex; + flex-direction: column; + + .ui-dialog-titlebar { + flex-grow: 0; + flex-shrink: 0; + } + + .ui-dialog-content { + flex-grow: 1; + flex-shrink: 1; + } +} + +.ui-widget-overlay.ame-ms-dialog-overlay { + //Leave the admin menu unobstructed, but cover the rest of the screen. + $menuOverlayColor: transparent; + background-color: transparent; + + --left-offset: calc(var(--ame-ms-menu-width, 160px) + 20px); + background-image: linear-gradient( + to right, + $menuOverlayColor 0, + $menuOverlayColor var(--left-offset), + rgba(0, 0, 0, 0.7) var(--left-offset) + ); + + opacity: 1; //Override the default 0.7 opacity. + + //Change the cursor over the admin menu area to indicate that it's not clickable + //while the style dialog is open. + &:before { + content: ''; + display: block; + position: fixed; + top: 0; + left: 0; + width: var(--left-offset); + height: 100%; + cursor: not-allowed; + } +} \ No newline at end of file diff --git a/extras/modules/metaboxes/ameMetaBoxEditor.php b/extras/modules/metaboxes/ameMetaBoxEditor.php index 66dfd24..975bdd5 100644 --- a/extras/modules/metaboxes/ameMetaBoxEditor.php +++ b/extras/modules/metaboxes/ameMetaBoxEditor.php @@ -399,7 +399,8 @@ public function enqueueTabScripts() { plugins_url('metabox-editor.js', __FILE__), array( 'ame-lodash', - 'knockout', + 'ame-knockout', + 'ame-pro-common-lib', 'ame-actor-selector', 'jquery', 'ame-actor-manager', @@ -535,10 +536,21 @@ public function enqueueGutenbergScripts() { return; } + //Enqueue after wp-edit-post if possible. This is a dependency, but we can't simply + //always add it because the "Appearance -> Widgets" page also loads Gutenberg assets + //(triggering this callback) but specifically disallows the wp-edit-post script. + //AFAICT, there's no general way to detect when wp-edit-post is allowed or not. + $dependencies = array('jquery', 'wp-data'); + /** @noinspection PhpRedundantOptionalArgumentInspection -- What WP core changes the default? */ + if ( wp_script_is('wp-edit-post', 'enqueued') ) { + $dependencies[] = 'wp-edit-post'; + } + wp_enqueue_auto_versioned_script( 'ame-hide-gutenberg-panels', plugins_url('hide-gutenberg-panels.js', __FILE__), - array('wp-data', 'wp-blocks', 'wp-edit-post', 'jquery') + $dependencies, + true ); wp_localize_script( diff --git a/extras/modules/metaboxes/hide-gutenberg-panels.js b/extras/modules/metaboxes/hide-gutenberg-panels.js index 06631b7..29c05d2 100644 --- a/extras/modules/metaboxes/hide-gutenberg-panels.js +++ b/extras/modules/metaboxes/hide-gutenberg-panels.js @@ -7,7 +7,13 @@ * @param {Array} data.selectorsToHide List of jQuery selectors to hide. */ function (data) { - if (typeof data['panelsToRemove'] !== 'undefined') { + if ( + (typeof data['panelsToRemove'] !== 'undefined') + && (typeof wp !== 'undefined') + && (typeof wp.data !== 'undefined') + && (typeof wp.data.dispatch === 'function') + && (wp.data.dispatch('core/edit-post')) + ) { for (var i = 0; i < data.panelsToRemove.length; i++) { // noinspection JSUnresolvedFunction wp.data.dispatch('core/edit-post').removeEditorPanel(data.panelsToRemove[i]); diff --git a/extras/modules/metaboxes/metabox-editor.js b/extras/modules/metaboxes/metabox-editor.js index 1a3d7e3..b35b79b 100644 --- a/extras/modules/metaboxes/metabox-editor.js +++ b/extras/modules/metaboxes/metabox-editor.js @@ -1,25 +1,25 @@ +"use strict"; /// /// /// /// -var AmeMetaBoxEditor = /** @class */ (function () { - function AmeMetaBoxEditor(settings, forceRefreshUrl) { - var _this = this; +class AmeMetaBoxEditor { + constructor(settings, forceRefreshUrl) { this.canAnyBoxesBeDeleted = false; this.actorSelector = new AmeActorSelector(AmeActors, true); //Wrap the selected actor in a computed observable so that it can be used with Knockout. - var _selectedActor = ko.observable(this.actorSelector.selectedActor + let _selectedActor = ko.observable(this.actorSelector.selectedActor ? AmeActors.getActor(this.actorSelector.selectedActor) : null); this.selectedActor = ko.computed({ read: function () { return _selectedActor(); }, - write: function (newActor) { - _this.actorSelector.setSelectedActor(newActor ? newActor.id : null); + write: (newActor) => { + this.actorSelector.setSelectedActor(newActor ? newActor.getId() : null); } }); - this.actorSelector.onChange(function (newSelectedActorId) { + this.actorSelector.onChange((newSelectedActorId) => { if (newSelectedActorId === null) { _selectedActor(null); } @@ -27,20 +27,20 @@ var AmeMetaBoxEditor = /** @class */ (function () { _selectedActor(AmeActors.getActor(newSelectedActorId)); } }); - this.screens = ko.observableArray(AmeMetaBoxEditor._.map(settings.screens, function (screenData, id) { - var metaBoxes = screenData['metaBoxes:']; + this.screens = ko.observableArray(AmeMetaBoxEditor._.map(settings.screens, (screenData, id) => { + let metaBoxes = screenData['metaBoxes:']; if (AmeMetaBoxEditor._.isEmpty(metaBoxes)) { metaBoxes = {}; } if (screenData['postTypeFeatures:'] && !AmeMetaBoxEditor._.isEmpty(screenData['postTypeFeatures:'])) { - var features = screenData['postTypeFeatures:']; - for (var featureName in features) { + const features = screenData['postTypeFeatures:']; + for (let featureName in features) { if (features.hasOwnProperty(featureName)) { metaBoxes['cpt-feature:' + featureName] = features[featureName]; } } } - return new AmeMetaBoxCollection(id, metaBoxes, screenData['isContentTypeMissing:'], _this); + return new AmeMetaBoxCollection(id, metaBoxes, screenData['isContentTypeMissing:'], this); })); this.screens.sort(function (a, b) { return a.formattedTitle.localeCompare(b.formattedTitle); @@ -51,16 +51,16 @@ var AmeMetaBoxEditor = /** @class */ (function () { this.isSlugWarningEnabled = ko.observable(true); } //noinspection JSUnusedGlobalSymbols It's actually used in the KO template, but PhpStorm doesn't realise that. - AmeMetaBoxEditor.prototype.saveChanges = function () { - var settings = this.getCurrentSettings(); + saveChanges() { + let settings = this.getCurrentSettings(); //Set the hidden form fields. this.settingsData(JSON.stringify(settings)); //Submit the form. return true; - }; - AmeMetaBoxEditor.prototype.getCurrentSettings = function () { - var collectionFormatName = 'Admin Menu Editor meta boxes', collectionFormatVersion = '1.0'; - var settings = { + } + getCurrentSettings() { + const collectionFormatName = 'Admin Menu Editor meta boxes', collectionFormatVersion = '1.0'; + let settings = { format: { name: collectionFormatName, version: collectionFormatVersion @@ -68,47 +68,47 @@ var AmeMetaBoxEditor = /** @class */ (function () { screens: {}, isInitialRefreshDone: true }; - var _ = AmeMetaBoxEditor._; + const _ = AmeMetaBoxEditor._; _.forEach(this.screens(), function (collection) { - var thisScreenData = { + let thisScreenData = { 'metaBoxes:': {}, 'postTypeFeatures:': {}, 'isContentTypeMissing:': collection.isContentTypeMissing }; _.forEach(collection.boxes(), function (metaBox) { - var key = metaBox.parentCollectionKey ? metaBox.parentCollectionKey : 'metaBoxes:'; - thisScreenData[key][metaBox.id] = metaBox.toPropertyMap(); + let key = metaBox.parentCollectionKey ? metaBox.parentCollectionKey : 'metaBoxes:'; + if ((key === 'metaBoxes:') || (key === 'postTypeFeatures:')) { + thisScreenData[key][metaBox.id] = metaBox.toPropertyMap(); + } }); settings.screens[collection.screenId] = thisScreenData; }); return settings; - }; + } //noinspection JSUnusedGlobalSymbols It's used in the KO template. - AmeMetaBoxEditor.prototype.promptForRefresh = function () { + promptForRefresh() { if (confirm('Refresh the list of available meta boxes?\n\nWarning: Unsaved changes will be lost.')) { window.location.href = this.forceRefreshUrl; } - }; - AmeMetaBoxEditor.prototype.deleteScreen = function (screen) { + } + deleteScreen(screen) { if (!screen.isContentTypeMissing) { alert('That screen may still exist; it cannot be deleted.'); return; } this.screens.remove(screen); - }; - AmeMetaBoxEditor._ = wsAmeLodash; - return AmeMetaBoxEditor; -}()); -var AmeMetaBox = /** @class */ (function () { - function AmeMetaBox(settings, metaBoxEditor) { - var _this = this; + } +} +AmeMetaBoxEditor._ = wsAmeLodash; +class AmeMetaBox { + constructor(settings, metaBoxEditor) { this.isHiddenByDefault = false; this.canBeDeleted = false; this.isVirtual = false; this.tooltipText = null; AmeMetaBox.counter++; this.uniqueHtmlId = 'ame-mb-item-' + AmeMetaBox.counter; - var _ = AmeMetaBox._; + const _ = AmeMetaBox._; this.metaBoxEditor = metaBoxEditor; this.initialProperties = settings; if (settings['parentCollectionKey']) { @@ -126,87 +126,89 @@ var AmeMetaBox = /** @class */ (function () { this.tooltipText = 'Technically, this is not a meta box, but it\'s included here for convenience.'; } this.isAvailable = ko.computed({ - read: function () { - var actor = metaBoxEditor.selectedActor(); + read: () => { + const actor = metaBoxEditor.selectedActor(); if (actor !== null) { - return AmeMetaBox.actorHasAccess(actor, _this.grantAccess, true, true); + return AmeMetaBox.actorHasAccess(actor, this.grantAccess, true, true); } else { //Check if any actors have this widget enabled. //We only care about visible actors. There might be some users that are loaded but not visible. - var actors = metaBoxEditor.actorSelector.getVisibleActors(); - return _.some(actors, function (anActor) { - return AmeMetaBox.actorHasAccess(anActor, _this.grantAccess, true, true); + const actors = metaBoxEditor.actorSelector.getVisibleActors(); + return _.some(actors, (anActor) => { + return AmeMetaBox.actorHasAccess(anActor, this.grantAccess, true, true); }); } }, - write: function (checked) { - if ((_this.id === 'slugdiv') && !checked && _this.metaBoxEditor.isSlugWarningEnabled()) { - var warningMessage = 'Hiding the "Slug" metabox can prevent the user from changing the post slug.\n' + write: (checked) => { + if ((this.id === 'slugdiv') && !checked && this.metaBoxEditor.isSlugWarningEnabled()) { + const warningMessage = 'Hiding the "Slug" metabox can prevent the user from changing the post slug.\n' + 'This is caused by a known bug in WordPress core.\n' + 'Do you want to hide this metabox anyway?'; if (confirm(warningMessage)) { //Suppress the warning. - _this.metaBoxEditor.isSlugWarningEnabled(false); + this.metaBoxEditor.isSlugWarningEnabled(false); } else { - _this.isAvailable.notifySubscribers(); + this.isAvailable.notifySubscribers(); return; } } - var actor = metaBoxEditor.selectedActor(); + const actor = metaBoxEditor.selectedActor(); if (actor !== null) { - _this.grantAccess.set(actor.getId(), checked); + this.grantAccess.set(actor.getId(), checked); } else { //Enable/disable all. - _.forEach(metaBoxEditor.actorSelector.getVisibleActors(), function (anActor) { _this.grantAccess.set(anActor.getId(), checked); }); + _.forEach(metaBoxEditor.actorSelector.getVisibleActors(), (anActor) => { + this.grantAccess.set(anActor.getId(), checked); + }); } } }); this.isVisibleByDefault = ko.computed({ - read: function () { - var actor = metaBoxEditor.selectedActor(); + read: () => { + const actor = metaBoxEditor.selectedActor(); if (actor !== null) { - return AmeMetaBox.actorHasAccess(actor, _this.defaultVisibility, !_this.isHiddenByDefault, null); + return AmeMetaBox.actorHasAccess(actor, this.defaultVisibility, !this.isHiddenByDefault, null); } else { - var actors = metaBoxEditor.actorSelector.getVisibleActors(); - return _.some(actors, function (anActor) { - return AmeMetaBox.actorHasAccess(anActor, _this.defaultVisibility, !_this.isHiddenByDefault, null); + const actors = metaBoxEditor.actorSelector.getVisibleActors(); + return _.some(actors, (anActor) => { + return AmeMetaBox.actorHasAccess(anActor, this.defaultVisibility, !this.isHiddenByDefault, null); }); } }, - write: function (checked) { - var actor = metaBoxEditor.selectedActor(); + write: (checked) => { + const actor = metaBoxEditor.selectedActor(); if (actor !== null) { - _this.defaultVisibility.set(actor.getId(), checked); + this.defaultVisibility.set(actor.getId(), checked); } else { //Enable/disable all. - _.forEach(metaBoxEditor.actorSelector.getVisibleActors(), function (anActor) { _this.defaultVisibility.set(anActor.getId(), checked); }); + _.forEach(metaBoxEditor.actorSelector.getVisibleActors(), (anActor) => { + this.defaultVisibility.set(anActor.getId(), checked); + }); } } }); - this.canChangeDefaultVisibility = ko.computed(function () { - return _this.isAvailable() && !_this.isVirtual; + this.canChangeDefaultVisibility = ko.computed(() => { + return this.isAvailable() && !this.isVirtual; }); - this.safeTitle = ko.computed(function () { - return AmeMetaBox.stripAllTags(_this.title); + this.safeTitle = ko.computed(() => { + return AmeMetaBox.stripAllTags(this.title); }); } - AmeMetaBox.actorHasAccess = function (actor, grants, roleDefault, superAdminDefault) { - if (roleDefault === void 0) { roleDefault = true; } - if (superAdminDefault === void 0) { superAdminDefault = true; } + static actorHasAccess(actor, grants, roleDefault = true, superAdminDefault = true) { //Is there a setting for this actor specifically? - var hasAccess = grants.get(actor.getId(), null); + let hasAccess = grants.get(actor.getId(), null); if (hasAccess !== null) { return hasAccess; } if (actor instanceof AmeUser) { //The Super Admin has access to everything by default, and it takes priority over roles. if (actor.isSuperAdmin) { - var adminHasAccess = grants.get('special:super_admin', null); + const adminHasAccess = grants.get('special:super_admin', null); if (adminHasAccess !== null) { return adminHasAccess; } @@ -215,17 +217,17 @@ var AmeMetaBox = /** @class */ (function () { } } //Allow access if at least one role has access. - var result = false; - for (var index = 0; index < actor.roles.length; index++) { - var roleActor = 'role:' + actor.roles[index], roleHasAccess = grants.get(roleActor, roleDefault); - result = result || roleHasAccess; + let result = false; + for (let index = 0; index < actor.roles.length; index++) { + let roleActor = 'role:' + actor.roles[index], roleHasAccess = grants.get(roleActor, roleDefault); + result = result || (!!roleHasAccess); } return result; } return roleDefault; - }; - AmeMetaBox.prototype.toPropertyMap = function () { - var properties = { + } + toPropertyMap() { + let properties = { 'id': this.id, 'title': this.title, 'context': this.context, @@ -236,61 +238,17 @@ var AmeMetaBox = /** @class */ (function () { //Preserve unused properties on round-trip. properties = AmeMetaBox._.merge({}, this.initialProperties, properties); return properties; - }; - AmeMetaBox.stripAllTags = function (input) { + } + static stripAllTags(input) { //Based on: http://phpjs.org/functions/strip_tags/ - var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, commentsAndPhpTags = /|<\?(?:php)?[\s\S]*?\?>/gi; + const tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, commentsAndPhpTags = /|<\?(?:php)?[\s\S]*?\?>/gi; return input.replace(commentsAndPhpTags, '').replace(tags, ''); - }; - AmeMetaBox._ = wsAmeLodash; - AmeMetaBox.counter = 0; - return AmeMetaBox; -}()); -var AmeActorAccessDictionary = /** @class */ (function () { - function AmeActorAccessDictionary(initialData) { - this.items = {}; - this.numberOfObservables = ko.observable(0); - if (initialData) { - this.setAll(initialData); - } } - AmeActorAccessDictionary.prototype.get = function (actor, defaultValue) { - if (defaultValue === void 0) { defaultValue = null; } - if (this.items.hasOwnProperty(actor)) { - return this.items[actor](); - } - this.numberOfObservables(); //Establish a dependency. - return defaultValue; - }; - AmeActorAccessDictionary.prototype.set = function (actor, value) { - if (!this.items.hasOwnProperty(actor)) { - this.items[actor] = ko.observable(value); - this.numberOfObservables(this.numberOfObservables() + 1); - } - else { - this.items[actor](value); - } - }; - AmeActorAccessDictionary.prototype.getAll = function () { - var result = {}; - for (var actorId in this.items) { - if (this.items.hasOwnProperty(actorId)) { - result[actorId] = this.items[actorId](); - } - } - return result; - }; - AmeActorAccessDictionary.prototype.setAll = function (values) { - for (var actorId in values) { - if (values.hasOwnProperty(actorId)) { - this.set(actorId, values[actorId]); - } - } - }; - return AmeActorAccessDictionary; -}()); -var AmeMetaBoxCollection = /** @class */ (function () { - function AmeMetaBoxCollection(screenId, metaBoxes, isContentTypeMissing, metaBoxEditor) { +} +AmeMetaBox._ = wsAmeLodash; +AmeMetaBox.counter = 0; +class AmeMetaBoxCollection { + constructor(screenId, metaBoxes, isContentTypeMissing, metaBoxEditor) { this.canAnyBeDeleted = false; this.isContentTypeMissing = false; this.screenId = screenId; @@ -305,21 +263,20 @@ var AmeMetaBoxCollection = /** @class */ (function () { this.canAnyBeDeleted = AmeMetaBoxCollection._.some(this.boxes(), 'canBeDeleted'); } //noinspection JSUnusedGlobalSymbols Use by KO. - AmeMetaBoxCollection.prototype.deleteBox = function (item) { + deleteBox(item) { this.boxes.remove(item); - }; - AmeMetaBoxCollection._ = wsAmeLodash; - return AmeMetaBoxCollection; -}()); + } +} +AmeMetaBoxCollection._ = wsAmeLodash; jQuery(function () { - var metaBoxEditor = new AmeMetaBoxEditor(wsAmeMetaBoxEditorData.settings, wsAmeMetaBoxEditorData.refreshUrl); + let metaBoxEditor = new AmeMetaBoxEditor(wsAmeMetaBoxEditorData.settings, wsAmeMetaBoxEditorData.refreshUrl); ko.applyBindings(metaBoxEditor, document.getElementById('ame-meta-box-editor')); //Make the column widths the same in all tables. - var $ = jQuery; - var tables = $('.ame-meta-box-list'), columnCount = tables.find('thead').first().find('th').length, maxWidths = wsAmeLodash.fill(Array(columnCount), 0); + const $ = jQuery; + let tables = $('.ame-meta-box-list'), columnCount = tables.find('thead').first().find('th').length, maxWidths = wsAmeLodash.fill(Array(columnCount), 0); tables.find('tr').each(function () { $(this).find('td,th').each(function (index) { - var width = $(this).width(); + const width = $(this).width(); if (maxWidths[index]) { maxWidths[index] = Math.max(width, maxWidths[index]); } @@ -334,7 +291,7 @@ jQuery(function () { }); }); //Set up tooltips. - if ($['qtip']) { + if (typeof $['qtip'] !== 'undefined') { $('#ame-meta-box-editor .ws_tooltip_trigger').qtip({ style: { classes: 'qtip qtip-rounded ws_tooltip_node' diff --git a/extras/modules/metaboxes/metabox-editor.js.map b/extras/modules/metaboxes/metabox-editor.js.map index e4f77a2..04b281d 100644 --- a/extras/modules/metaboxes/metabox-editor.js.map +++ b/extras/modules/metaboxes/metabox-editor.js.map @@ -1 +1 @@ -{"version":3,"file":"metabox-editor.js","sourceRoot":"","sources":["metabox-editor.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"metabox-editor.js","sourceRoot":"","sources":["metabox-editor.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/metaboxes/metabox-editor.ts b/extras/modules/metaboxes/metabox-editor.ts index c5c515a..51ab839 100644 --- a/extras/modules/metaboxes/metabox-editor.ts +++ b/extras/modules/metaboxes/metabox-editor.ts @@ -3,14 +3,14 @@ /// /// -declare const wsAmeMetaBoxEditorData; +declare const wsAmeMetaBoxEditorData: any; interface MetaBoxEditorSettings { format: { name: string, version: string }, - screens: {[id: string] : ScreenSettingsData}; + screens: { [id: string]: ScreenSettingsData }; isInitialRefreshDone: boolean; } @@ -26,7 +26,7 @@ class AmeMetaBoxEditor { screens: KnockoutObservableArray; actorSelector: AmeActorSelector; - selectedActor: KnockoutComputed; + selectedActor: KnockoutComputed; settingsData: KnockoutObservable; canAnyBoxesBeDeleted: boolean = false; @@ -39,20 +39,20 @@ class AmeMetaBoxEditor { this.actorSelector = new AmeActorSelector(AmeActors, true); //Wrap the selected actor in a computed observable so that it can be used with Knockout. - let _selectedActor = ko.observable( + let _selectedActor = ko.observable( this.actorSelector.selectedActor ? AmeActors.getActor(this.actorSelector.selectedActor) : null ); - this.selectedActor = ko.computed({ + this.selectedActor = ko.computed({ read: function () { return _selectedActor(); }, - write: (newActor: AmeBaseActor) => { - this.actorSelector.setSelectedActor(newActor ? newActor.id : null); + write: (newActor) => { + this.actorSelector.setSelectedActor(newActor ? newActor.getId() : null); } }); - this.actorSelector.onChange((newSelectedActorId: string|null) => { + this.actorSelector.onChange((newSelectedActorId: string | null) => { if (newSelectedActorId === null) { _selectedActor(null); } else { @@ -78,14 +78,14 @@ class AmeMetaBoxEditor { } return new AmeMetaBoxCollection( - id, + id!, metaBoxes, screenData['isContentTypeMissing:'], this ); }) ); - this.screens.sort(function(a, b) { + this.screens.sort(function (a, b) { return a.formattedTitle.localeCompare(b.formattedTitle); }); @@ -122,14 +122,16 @@ class AmeMetaBoxEditor { const _ = AmeMetaBoxEditor._; _.forEach(this.screens(), function (collection) { - let thisScreenData = { - 'metaBoxes:' : {}, - 'postTypeFeatures:' : {}, - 'isContentTypeMissing:' : collection.isContentTypeMissing + let thisScreenData: ScreenSettingsData = { + 'metaBoxes:': {}, + 'postTypeFeatures:': {}, + 'isContentTypeMissing:': collection.isContentTypeMissing }; - _.forEach(collection.boxes(), function(metaBox) { + _.forEach(collection.boxes(), function (metaBox) { let key = metaBox.parentCollectionKey ? metaBox.parentCollectionKey : 'metaBoxes:'; - thisScreenData[key][metaBox.id] = metaBox.toPropertyMap(); + if ((key === 'metaBoxes:') || (key === 'postTypeFeatures:')) { + thisScreenData[key][metaBox.id] = metaBox.toPropertyMap(); + } }); settings.screens[collection.screenId] = thisScreenData; }); @@ -174,7 +176,7 @@ class AmeMetaBox { canBeDeleted: boolean = false; isVirtual: boolean = false; - tooltipText: string = null; + tooltipText: string|null = null; private readonly initialProperties: MetaBoxPropertyMap; protected metaBoxEditor: AmeMetaBoxEditor; @@ -242,7 +244,9 @@ class AmeMetaBox { //Enable/disable all. _.forEach( metaBoxEditor.actorSelector.getVisibleActors(), - (anActor) => { this.grantAccess.set(anActor.getId(), checked); } + (anActor) => { + this.grantAccess.set(anActor.getId(), checked); + } ); } } @@ -268,7 +272,9 @@ class AmeMetaBox { //Enable/disable all. _.forEach( metaBoxEditor.actorSelector.getVisibleActors(), - (anActor) => { this.defaultVisibility.set(anActor.getId(), checked); } + (anActor) => { + this.defaultVisibility.set(anActor.getId(), checked); + } ); } } @@ -311,7 +317,7 @@ class AmeMetaBox { for (let index = 0; index < actor.roles.length; index++) { let roleActor = 'role:' + actor.roles[index], roleHasAccess = grants.get(roleActor, roleDefault); - result = result || roleHasAccess; + result = result || (!!roleHasAccess); } return result; } @@ -345,54 +351,7 @@ class AmeMetaBox { } interface MetaBoxPropertyMap { - [name: string] : any; -} - -class AmeActorAccessDictionary { - items: { [actorId: string] : KnockoutObservable; } = {}; - private readonly numberOfObservables: KnockoutObservable; - - constructor(initialData?: AmeDictionary) { - this.numberOfObservables = ko.observable(0); - if (initialData) { - this.setAll(initialData); - } - } - - get(actor: string, defaultValue = null): boolean { - if (this.items.hasOwnProperty(actor)) { - return this.items[actor](); - } - this.numberOfObservables(); //Establish a dependency. - return defaultValue; - } - - set(actor: string, value: boolean) { - if (!this.items.hasOwnProperty(actor)) { - this.items[actor] = ko.observable(value); - this.numberOfObservables(this.numberOfObservables() + 1); - } else { - this.items[actor](value); - } - } - - getAll(): AmeDictionary { - let result: AmeDictionary = {}; - for (let actorId in this.items) { - if (this.items.hasOwnProperty(actorId)) { - result[actorId] = this.items[actorId](); - } - } - return result; - } - - setAll(values: AmeDictionary) { - for (let actorId in values) { - if (values.hasOwnProperty(actorId)) { - this.set(actorId, values[actorId]); - } - } - } + [name: string]: any; } class AmeMetaBoxCollection { @@ -407,7 +366,7 @@ class AmeMetaBoxCollection { constructor( screenId: string, - metaBoxes: {[id: string]: MetaBoxPropertyMap}, + metaBoxes: { [id: string]: MetaBoxPropertyMap }, isContentTypeMissing: boolean, metaBoxEditor: AmeMetaBoxEditor ) { @@ -415,11 +374,11 @@ class AmeMetaBoxCollection { this.formattedTitle = screenId.charAt(0).toUpperCase() + screenId.slice(1); this.isContentTypeMissing = isContentTypeMissing; - this.boxes = ko.observableArray(AmeMetaBoxCollection._.map(metaBoxes, function(properties) { + this.boxes = ko.observableArray(AmeMetaBoxCollection._.map(metaBoxes, function (properties) { return new AmeMetaBox(properties, metaBoxEditor); })); - this.boxes.sort(function(a, b) { + this.boxes.sort(function (a, b) { return a.id.localeCompare(b.id); }); @@ -427,13 +386,13 @@ class AmeMetaBoxCollection { } //noinspection JSUnusedGlobalSymbols Use by KO. - deleteBox(item) { + deleteBox(item: AmeMetaBox) { this.boxes.remove(item); } } -jQuery(function() { +jQuery(function () { let metaBoxEditor = new AmeMetaBoxEditor(wsAmeMetaBoxEditorData.settings, wsAmeMetaBoxEditorData.refreshUrl); ko.applyBindings(metaBoxEditor, document.getElementById('ame-meta-box-editor')); @@ -443,8 +402,8 @@ jQuery(function() { columnCount = tables.find('thead').first().find('th').length, maxWidths = wsAmeLodash.fill(Array(columnCount), 0); - tables.find('tr').each(function() { - $(this).find('td,th').each(function(index) { + tables.find('tr').each(function (this: HTMLElement) { + $(this).find('td,th').each(function (this: HTMLElement, index) { const width = $(this).width(); if (maxWidths[index]) { maxWidths[index] = Math.max(width, maxWidths[index]); @@ -454,14 +413,14 @@ jQuery(function() { }) }); - tables.each(function() { - $(this).find('thead th').each(function(index) { + tables.each(function (this: HTMLElement) { + $(this).find('thead th').each(function (this: HTMLElement, index) { $(this).width(maxWidths[index]); }); }); //Set up tooltips. - if ($['qtip']) { + if (typeof ($ as any)['qtip'] !== 'undefined') { $('#ame-meta-box-editor .ws_tooltip_trigger').qtip({ style: { classes: 'qtip qtip-rounded ws_tooltip_node' diff --git a/extras/modules/role-editor/ameRoleEditor.php b/extras/modules/role-editor/ameRoleEditor.php index a4b7d5b..53e844b 100644 --- a/extras/modules/role-editor/ameRoleEditor.php +++ b/extras/modules/role-editor/ameRoleEditor.php @@ -77,6 +77,7 @@ class ameRoleEditor extends amePersistentProModule { private $cachedEnabledRoleCaps = array(); public function __construct($menuEditor) { + $this->settingsWrapperEnabled = true; parent::__construct($menuEditor); add_filter('editable_roles', array($this, 'filterEditableRoles'), 20, 1); @@ -104,7 +105,7 @@ public function enqueueTabScripts() { plugins_url('role-editor.js', __FILE__), array( 'ame-lodash', - 'knockout', + 'ame-knockout', 'jquery', 'jquery-qtip', 'ame-actor-manager', @@ -1860,7 +1861,7 @@ private function getEnabledCoreCapabilitiesForRole($roleId, $roleData = null) { $roleObject = get_role($roleId); $capabilities = isset($roleObject->capabilities) ? $roleObject->capabilities : null; } - if (!isset($capabilities)) { + if (!isset($capabilities) || !is_array($capabilities)) { return array(); } diff --git a/extras/modules/role-editor/role-editor.js b/extras/modules/role-editor/role-editor.js index 521bd0d..600c6bb 100644 --- a/extras/modules/role-editor/role-editor.js +++ b/extras/modules/role-editor/role-editor.js @@ -1,3 +1,4 @@ +"use strict"; /// /// /// @@ -6,29 +7,14 @@ /// /// /// -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var RexPermission = /** @class */ (function () { - function RexPermission(editor, capability) { +class RexPermission { + constructor(editor, capability) { this.readableAction = null; this.mainDescription = ''; this.isRedundant = false; this.editor = editor; this.capability = capability; - var self = this; + const self = this; this.labelHtml = ko.pureComputed({ read: self.getLabelHtml, deferEvaluation: true, @@ -56,44 +42,42 @@ var RexPermission = /** @class */ (function () { deferEvaluation: true }); } - RexPermission.prototype.getLabelHtml = function () { - var text; + getLabelHtml() { + let text; if ((this.readableAction !== null) && this.editor.readableNamesEnabled()) { text = this.readableAction; } else { text = this.capability.displayName(); } - var html = wsAmeLodash.escape(text); + let html = wsAmeLodash.escape(text); if (this.isVisible()) { html = this.editor.highlightSearchKeywords(html); } //Let the browser break words on underscores. html = html.replace(/_/g, '_'); return html; - }; - return RexPermission; -}()); + } +} /** * A basic representation of any component or extension that can add new capabilities. * This includes plugins, themes, and the WordPress core. */ -var RexWordPressComponent = /** @class */ (function () { - function RexWordPressComponent(id, name) { +class RexWordPressComponent { + constructor(id, name) { this.id = id; this.name = name; } - RexWordPressComponent.fromJs = function (id, details) { - var instance = new RexWordPressComponent(id, details.name ? details.name : id); + static fromJs(id, details) { + const instance = new RexWordPressComponent(id, details.name ? details.name : id); if (details.capabilityDocumentationUrl) { instance.capabilityDocumentationUrl = details.capabilityDocumentationUrl; } return instance; - }; - return RexWordPressComponent; -}()); -var RexObservableCapabilityMap = /** @class */ (function () { - function RexObservableCapabilityMap(initialCapabilities) { + } +} +class RexObservableCapabilityMap { + constructor(initialCapabilities) { this.capabilities = {}; if (initialCapabilities) { this.initialCapabilities = wsAmeLodash.clone(initialCapabilities); @@ -102,19 +86,22 @@ var RexObservableCapabilityMap = /** @class */ (function () { this.initialCapabilities = {}; } } - RexObservableCapabilityMap.prototype.getCapabilityState = function (capabilityName) { - var observable = this.getObservable(capabilityName); + getCapabilityState(capabilityName) { + const observable = this.getObservable(capabilityName); return observable(); - }; - RexObservableCapabilityMap.prototype.setCapabilityState = function (capabilityName, state) { - var observable = this.getObservable(capabilityName); + } + setCapabilityState(capabilityName, state) { + const observable = this.getObservable(capabilityName); observable(state); - }; - RexObservableCapabilityMap.prototype.getAllCapabilities = function () { - var _ = wsAmeLodash; - var result = this.initialCapabilities ? _.clone(this.initialCapabilities) : {}; + } + getAllCapabilities() { + const _ = wsAmeLodash; + let result = this.initialCapabilities ? _.clone(this.initialCapabilities) : {}; _.forEach(this.capabilities, function (observable, name) { - var isGranted = observable(); + if (typeof name === 'undefined') { + return; + } + const isGranted = observable(); if (isGranted === null) { delete result[name]; } @@ -123,117 +110,111 @@ var RexObservableCapabilityMap = /** @class */ (function () { } }); return result; - }; - RexObservableCapabilityMap.prototype.getObservable = function (capabilityName) { + } + getObservable(capabilityName) { if (!this.capabilities.hasOwnProperty(capabilityName)) { - var initialValue = null; + let initialValue = null; if (this.initialCapabilities.hasOwnProperty(capabilityName)) { initialValue = this.initialCapabilities[capabilityName]; } this.capabilities[capabilityName] = ko.observable(initialValue); } return this.capabilities[capabilityName]; - }; - return RexObservableCapabilityMap; -}()); -var RexBaseActor = /** @class */ (function () { - function RexBaseActor(id, name, displayName, capabilities) { + } +} +class RexBaseActor { + constructor(id, name, displayName, capabilities) { this.canHaveRoles = false; this.id = ko.observable(id); this.name = ko.observable(name); this.displayName = ko.observable(displayName); this.capabilities = new RexObservableCapabilityMap(capabilities || {}); } - RexBaseActor.prototype.hasCap = function (capability) { + hasCap(capability) { return (this.capabilities.getCapabilityState(capability) === true); - }; - RexBaseActor.prototype.getCapabilityState = function (capability) { + } + getCapabilityState(capability) { return this.getOwnCapabilityState(capability); - }; - RexBaseActor.prototype.getOwnCapabilityState = function (capability) { + } + getOwnCapabilityState(capability) { return this.capabilities.getCapabilityState(capability); - }; - RexBaseActor.prototype.setCap = function (capability, enabled) { + } + setCap(capability, enabled) { this.capabilities.setCapabilityState(capability, enabled); - }; - RexBaseActor.prototype.deleteCap = function (capability) { + } + deleteCap(capability) { this.capabilities.setCapabilityState(capability, null); - }; - RexBaseActor.prototype.getDisplayName = function () { + } + getDisplayName() { return this.displayName(); - }; - RexBaseActor.prototype.getId = function () { + } + getId() { return this.id(); - }; + } /** * Get capabilities that are explicitly assigned/denied to this actor. * Does not include capabilities that a user inherits from their role(s). */ - RexBaseActor.prototype.getOwnCapabilities = function () { + getOwnCapabilities() { return this.capabilities.getAllCapabilities(); - }; - return RexBaseActor; -}()); -var RexRole = /** @class */ (function (_super) { - __extends(RexRole, _super); - function RexRole(name, displayName, capabilities) { - var _this = _super.call(this, 'role:' + name, name, displayName, capabilities) || this; - _this.hasUsers = false; - return _this; - } - RexRole.fromRoleData = function (data) { - var role = new RexRole(data.name, data.displayName, data.capabilities); + } + isUser() { + return false; + } +} +class RexRole extends RexBaseActor { + constructor(name, displayName, capabilities) { + super('role:' + name, name, displayName, capabilities); + this.hasUsers = false; + } + static fromRoleData(data) { + const role = new RexRole(data.name, data.displayName, data.capabilities); role.hasUsers = data.hasUsers; return role; - }; + } /** * Is this one of the default roles that are part of WordPress core? * * Note: I'm calling this property "built-in" instead of "default" to distinguish it * from the default role for new users. */ - RexRole.prototype.isBuiltIn = function () { + isBuiltIn() { return RexRole.builtInRoleNames.indexOf(this.name()) >= 0; - }; - RexRole.prototype.toJs = function () { + } + toJs() { return { name: this.name(), displayName: this.displayName(), capabilities: this.getOwnCapabilities() }; - }; - RexRole.builtInRoleNames = ['administrator', 'editor', 'author', 'subscriber', 'contributor']; - return RexRole; -}(RexBaseActor)); -var RexSuperAdmin = /** @class */ (function (_super) { - __extends(RexSuperAdmin, _super); - function RexSuperAdmin() { - return _super.call(this, 'special:super_admin', 'Super Admin', 'Super Admin') || this; - } - RexSuperAdmin.getInstance = function () { + } +} +RexRole.builtInRoleNames = ['administrator', 'editor', 'author', 'subscriber', 'contributor']; +class RexSuperAdmin extends RexBaseActor { + constructor() { + super('special:super_admin', 'Super Admin', 'Super Admin'); + } + static getInstance() { if (RexSuperAdmin.instance === null) { RexSuperAdmin.instance = new RexSuperAdmin(); } return RexSuperAdmin.instance; - }; - RexSuperAdmin.instance = null; - return RexSuperAdmin; -}(RexBaseActor)); -var RexUser = /** @class */ (function (_super) { - __extends(RexUser, _super); - function RexUser(login, displayName, capabilities, userId) { - var _this = _super.call(this, 'user:' + login, login, displayName, capabilities) || this; - _this.isSuperAdmin = false; - _this.userLogin = login; - _this.canHaveRoles = true; - _this.roles = ko.observableArray([]); - _this.userId = userId; - return _this; - } - RexUser.prototype.hasCap = function (capability, outGrantedBy) { + } +} +RexSuperAdmin.instance = null; +class RexUser extends RexBaseActor { + constructor(login, displayName, capabilities, userId) { + super('user:' + login, login, displayName, capabilities); + this.isSuperAdmin = false; + this.userLogin = login; + this.canHaveRoles = true; + this.roles = ko.observableArray([]); + this.userId = userId || 0; + } + hasCap(capability, outGrantedBy) { return (this.getCapabilityState(capability, outGrantedBy) === true); - }; - RexUser.prototype.getCapabilityState = function (capability, outGrantedBy) { + } + getCapabilityState(capability, outGrantedBy) { if (capability === 'do_not_allow') { return false; } @@ -243,15 +224,15 @@ var RexUser = /** @class */ (function (_super) { } return (capability !== 'do_not_allow'); } - var result = _super.prototype.getCapabilityState.call(this, capability); + let result = super.getCapabilityState(capability); if (result !== null) { if (outGrantedBy) { outGrantedBy.push(this); } return result; } - wsAmeLodash.each(this.roles(), function (role) { - var roleHasCap = role.getCapabilityState(capability); + wsAmeLodash.each(this.roles(), (role) => { + const roleHasCap = role.getCapabilityState(capability); if (roleHasCap !== null) { if (outGrantedBy) { outGrantedBy.push(role); @@ -260,27 +241,27 @@ var RexUser = /** @class */ (function (_super) { } }); return result; - }; + } // noinspection JSUnusedGlobalSymbols Used in KO templates. - RexUser.prototype.getInheritanceDetails = function (capability) { - var _ = wsAmeLodash; - var results = []; + getInheritanceDetails(capability) { + const _ = wsAmeLodash; + let results = []; //Note: Alternative terms include "Assigned", "Granted", "Yes"/"No". if (this.isSuperAdmin) { - var superAdmin = RexSuperAdmin.getInstance(); - var description_1 = 'Allow everything'; + const superAdmin = RexSuperAdmin.getInstance(); + let description = 'Allow everything'; if (capability.name === 'do_not_allow') { - description_1 = 'Deny'; + description = 'Deny'; } results.push({ actor: superAdmin, name: superAdmin.displayName(), - description: description_1 + description: description }); } - _.each(this.roles(), function (role) { - var roleHasCap = role.getCapabilityState(capability.name); - var description; + _.each(this.roles(), (role) => { + const roleHasCap = role.getCapabilityState(capability.name); + let description; if (roleHasCap) { description = 'Allow'; } @@ -296,8 +277,8 @@ var RexUser = /** @class */ (function (_super) { description: description, }); }); - var hasOwnCap = _super.prototype.getCapabilityState.call(this, capability.name); - var description; + let hasOwnCap = super.getCapabilityState(capability.name); + let description; if (hasOwnCap) { description = 'Allow'; } @@ -312,40 +293,43 @@ var RexUser = /** @class */ (function (_super) { name: 'User-specific setting', description: description, }); - var relevantActors = []; + let relevantActors = []; this.getCapabilityState(capability.name, relevantActors); - var decidingActor = _.last(relevantActors); + const decidingActor = _.last(relevantActors); _.each(results, function (item) { item.isDecisive = (item.actor === decidingActor); }); return results; - }; - RexUser.fromAmeUser = function (data, editor) { - var user = new RexUser(data.userLogin, data.displayName, data.capabilities, data.userId); + } + isUser() { + return true; + } + static fromAmeUser(data, editor) { + const user = new RexUser(data.userLogin, data.displayName, data.capabilities, data.userId); wsAmeLodash.forEach(data.roles, function (roleId) { - var role = editor.getRole(roleId); + const role = editor.getRole(roleId); if (role) { user.roles.push(role); } }); return user; - }; - RexUser.fromAmeUserProperties = function (properties, editor) { - var user = new RexUser(properties.user_login, properties.display_name, properties.capabilities); + } + static fromAmeUserProperties(properties, editor) { + const user = new RexUser(properties.user_login, properties.display_name, properties.capabilities); if (properties.id) { user.userId = properties.id; } wsAmeLodash.forEach(properties.roles, function (roleId) { - var role = editor.getRole(roleId); + const role = editor.getRole(roleId); if (role) { user.roles.push(role); } }); return user; - }; - RexUser.prototype.toJs = function () { - var _ = wsAmeLodash; - var roles = _.invoke(this.roles(), 'name'); + } + toJs() { + const _ = wsAmeLodash; + let roles = _.invoke(this.roles(), 'name'); return { userId: this.userId, userLogin: this.userLogin, @@ -353,14 +337,13 @@ var RexUser = /** @class */ (function (_super) { capabilities: this.getOwnCapabilities(), roles: roles }; - }; - return RexUser; -}(RexBaseActor)); -var RexCategory = /** @class */ (function () { - function RexCategory(name, editor, slug, capabilities) { - if (slug === void 0) { slug = null; } - if (capabilities === void 0) { capabilities = []; } - var _this = this; + } + getRoleIds() { + return wsAmeLodash.map(this.roles(), 'name'); + } +} +class RexCategory { + constructor(name, editor, slug = null, capabilities = []) { this.slug = null; this.origin = null; this.subtitle = null; @@ -368,15 +351,15 @@ var RexCategory = /** @class */ (function () { this.parent = null; this.subcategories = []; this.duplicates = []; - var _ = wsAmeLodash; - var self = this; + const _ = wsAmeLodash; + const self = this; this.editor = editor; this.name = name; this.slug = slug; if ((this.slug !== null) && (this.slug !== '')) { editor.categoriesBySlug[this.slug] = this; } - var initialPermissions = _.map(capabilities, function (capabilityName) { + let initialPermissions = _.map(capabilities, (capabilityName) => { return new RexPermission(editor, editor.getCapability(capabilityName)); }); this.permissions = ko.observableArray(initialPermissions); @@ -405,7 +388,7 @@ var RexCategory = /** @class */ (function () { if (!editor.showNumberOfCapsEnabled()) { return false; } - var totalCaps = self.totalCapabilityCount(), enabledCaps = self.enabledCapabilityCount(); + const totalCaps = self.totalCapabilityCount(), enabledCaps = self.enabledCapabilityCount(); if (!editor.showZerosEnabled() && ((totalCaps === 0) || (enabledCaps === 0))) { return false; } @@ -426,14 +409,14 @@ var RexCategory = /** @class */ (function () { }); this.areAllPermissionsEnabled = ko.computed({ read: function () { - var items = self.permissions(); - var len = items.length; - for (var i = 0; i < len; i++) { + const items = self.permissions(); + const len = items.length; + for (let i = 0; i < len; i++) { if (!items[i].capability.isEnabledForSelectedActor() && items[i].capability.isEditable()) { return false; } } - for (var i = 0; i < self.subcategories.length; i++) { + for (let i = 0; i < self.subcategories.length; i++) { if (!self.subcategories[i].areAllPermissionsEnabled()) { return false; } @@ -441,14 +424,14 @@ var RexCategory = /** @class */ (function () { return true; }, write: function (enabled) { - var items = self.permissions(); - for (var i = 0; i < items.length; i++) { - var item = items[i]; + const items = self.permissions(); + for (let i = 0; i < items.length; i++) { + let item = items[i]; if (item.capability.isEditable()) { item.capability.isEnabledForSelectedActor(enabled); } } - for (var i = 0; i < self.subcategories.length; i++) { + for (let i = 0; i < self.subcategories.length; i++) { self.subcategories[i].areAllPermissionsEnabled(enabled); } }, @@ -457,15 +440,15 @@ var RexCategory = /** @class */ (function () { }); this.areAllPermissionsEnabled.extend({ rateLimit: { timeout: 5, method: 'notifyWhenChangesStop' } }); this.areAnyPermissionsEditable = ko.pureComputed({ - read: function () { - var items = self.permissions(); - var len = items.length; - for (var i = 0; i < len; i++) { + read: () => { + const items = self.permissions(); + const len = items.length; + for (let i = 0; i < len; i++) { if (items[i].capability.isEditable()) { return true; } } - for (var i = 0; i < self.subcategories.length; i++) { + for (let i = 0; i < self.subcategories.length; i++) { if (!self.subcategories[i].areAnyPermissionsEditable()) { return true; } @@ -478,8 +461,8 @@ var RexCategory = /** @class */ (function () { this.areAnyPermissionsEditable.extend({ rateLimit: { timeout: 5, method: 'notifyWhenChangesStop' } }); this.isVisible = ko.computed({ read: function () { - var visible = false; - var hasVisibleSubcategories = false; + let visible = false; + let hasVisibleSubcategories = false; _.forEach(self.subcategories, function (category) { if (category.isVisible()) { hasVisibleSubcategories = true; @@ -487,7 +470,7 @@ var RexCategory = /** @class */ (function () { } }); //Hide it if not inside the selected category. - var isInSelectedCategory = false, temp = self; + let isInSelectedCategory = false, temp = self; while (temp !== null) { if (temp.isSelected()) { isInSelectedCategory = true; @@ -500,7 +483,7 @@ var RexCategory = /** @class */ (function () { if (!isInSelectedCategory && (self.duplicates.length > 0) && (editor.categoryViewMode() === RexRoleEditor.singleCategoryView)) { - for (var i = 0; i < self.duplicates.length; i++) { + for (let i = 0; i < self.duplicates.length; i++) { temp = self.duplicates[i]; while (temp !== null) { if (temp.isSelected()) { @@ -538,17 +521,17 @@ var RexCategory = /** @class */ (function () { }); this.desiredColumnCount = ko.computed({ read: function () { - var visiblePermissions = 0; + let visiblePermissions = 0; _.forEach(self.permissions(), function (permission) { if (permission.isVisible()) { visiblePermissions++; } }); - var minItemsPerColumn = 12; + let minItemsPerColumn = 12; if (editor.categoryWidthMode() === 'full') { minItemsPerColumn = 3; } - var desiredColumns = Math.max(Math.ceil(visiblePermissions / minItemsPerColumn), 1); + let desiredColumns = Math.max(Math.ceil(visiblePermissions / minItemsPerColumn), 1); //Avoid situations where the last column has only one item (an orphan). if ((desiredColumns >= 2) && (visiblePermissions % minItemsPerColumn === 1)) { desiredColumns--; @@ -571,8 +554,10 @@ var RexCategory = /** @class */ (function () { }); this.isNavExpanded = ko.observable((this.slug !== null) ? !editor.userPreferences.collapsedCategories.peek(this.slug) : true); if (this.slug) { - this.isNavExpanded.subscribe(function (newValue) { - editor.userPreferences.collapsedCategories.toggle(_this.slug, !newValue); + this.isNavExpanded.subscribe((newValue) => { + //Type node: Because the slug is only assigned in the constructor, we can assume + //it won't become null if it's not null now. + editor.userPreferences.collapsedCategories.toggle(this.slug, !newValue); }); } this.isNavVisible = ko.pureComputed({ @@ -587,7 +572,7 @@ var RexCategory = /** @class */ (function () { }); this.cssClasses = ko.computed({ read: function () { - var classes = []; + let classes = []; if (self.subcategories.length > 0) { classes.push('rex-has-subcategories'); } @@ -608,7 +593,7 @@ var RexCategory = /** @class */ (function () { }); this.navCssClasses = ko.pureComputed({ read: function () { - var classes = []; + let classes = []; if (self.isSelected()) { classes.push('rex-selected-nav-item'); } @@ -625,31 +610,31 @@ var RexCategory = /** @class */ (function () { }); this.subcategoryModificationFlag = ko.observable(this.subcategories.length); this.sortedSubcategories = ko.pureComputed({ - read: function () { + read: () => { //Refresh the sorted list when categories are added or removed. - _this.subcategoryModificationFlag(); - return _this.getSortedSubcategories(); + this.subcategoryModificationFlag(); + return this.getSortedSubcategories(); }, deferEvaluation: true }); this.navSubcategories = ko.pureComputed({ - read: function () { - _this.subcategoryModificationFlag(); - return _this.subcategories; + read: () => { + this.subcategoryModificationFlag(); + return this.subcategories; }, deferEvaluation: true }); this.subheading = ko.pureComputed({ - read: function () { - return _this.getSubheadingItems().join(', '); + read: () => { + return this.getSubheadingItems().join(', '); }, deferEvaluation: true }); } - RexCategory.prototype.addSubcategory = function (category, afterName) { + addSubcategory(category, afterName) { category.parent = this; if (afterName) { - var index = wsAmeLodash.findIndex(this.subcategories, { 'name': afterName }); + const index = wsAmeLodash.findIndex(this.subcategories, { 'name': afterName }); if (index > -1) { this.subcategories.splice(index + 1, 0, category); this.subcategoryModificationFlag(this.subcategories.length); @@ -658,32 +643,33 @@ var RexCategory = /** @class */ (function () { } this.subcategories.push(category); this.subcategoryModificationFlag(this.subcategories.length); - }; + } // noinspection JSUnusedGlobalSymbols Used in KO templates. - RexCategory.prototype.toggleSubcategories = function () { + toggleSubcategories() { this.isNavExpanded(!this.isNavExpanded()); - }; - RexCategory.prototype.getSortedSubcategories = function () { + } + getSortedSubcategories() { //In most cases, the subcategory list is already sorted either alphabetically or in a predefined order //chosen for specific category. Subcategories can override this method to change the sort order. return this.subcategories; - }; + } /** * Sort the permissions in this category. Doesn't affect subcategories. * The default sort is alphabetical, but subclasses can override this method to specify a custom order. */ - RexCategory.prototype.sortPermissions = function () { - this.permissions.sort(function (a, b) { - return a.capability.name.toLowerCase().localeCompare(b.capability.name.toLowerCase()); - }); - }; - RexCategory.prototype.countUniqueCapabilities = function (accumulator, predicate) { - if (accumulator === void 0) { accumulator = {}; } - if (predicate === void 0) { predicate = null; } - var total = 0; - var permissions = this.permissions(); - for (var i = 0; i < permissions.length; i++) { - var capability = permissions[i].capability; + sortPermissions() { + this.permissions.sort((a, b) => { + return this.compareBasicPermissions(a, b); + }); + } + compareBasicPermissions(a, b) { + return a.capability.name.toLowerCase().localeCompare(b.capability.name.toLowerCase()); + } + countUniqueCapabilities(accumulator = {}, predicate = null) { + let total = 0; + const permissions = this.permissions(); + for (let i = 0; i < permissions.length; i++) { + const capability = permissions[i].capability; if (accumulator.hasOwnProperty(capability.name)) { continue; } @@ -696,114 +682,114 @@ var RexCategory = /** @class */ (function () { accumulator[capability.name] = true; total++; } - for (var i = 0; i < this.subcategories.length; i++) { + for (let i = 0; i < this.subcategories.length; i++) { total = total + this.subcategories[i].countUniqueCapabilities(accumulator, predicate); } return total; - }; - RexCategory.prototype.findCategoryBySlug = function (slug) { + } + findCategoryBySlug(slug) { if (this.editor.categoriesBySlug.hasOwnProperty(slug)) { return this.editor.categoriesBySlug[slug]; } return null; - }; - RexCategory.fromJs = function (details, editor) { - var category; - if (details.variant && details.variant === 'post_type') { + } + static fromJs(details, editor) { + var _a; + let category; + if (!details.variant) { + category = new RexCategory(details.name, editor, details.slug, details.capabilities); + } + else if (details.variant && details.variant === 'post_type') { category = new RexPostTypeCategory(details.name, editor, details.contentTypeId, details.slug, details.permissions); } else if (details.variant && details.variant === 'taxonomy') { category = new RexTaxonomyCategory(details.name, editor, details.contentTypeId, details.slug, details.permissions); } else { - category = new RexCategory(details.name, editor, details.slug, details.capabilities); + throw new Error('Unknown category data variant: ' + ((_a = details.variant) !== null && _a !== void 0 ? _a : 'undefined')); } if (details.componentId) { category.origin = editor.getComponent(details.componentId); } if (details.subcategories) { - wsAmeLodash.forEach(details.subcategories, function (childDetails) { - var subcategory = RexCategory.fromJs(childDetails, editor); + wsAmeLodash.forEach(details.subcategories, (childDetails) => { + const subcategory = RexCategory.fromJs(childDetails, editor); category.addSubcategory(subcategory); }); } return category; - }; - RexCategory.prototype.usesBaseCapabilities = function () { + } + usesBaseCapabilities() { return false; - }; - RexCategory.prototype.getDeDuplicationKey = function () { + } + getDeDuplicationKey() { var _a; - var key = (_a = this.slug) !== null && _a !== void 0 ? _a : this.name; + let key = (_a = this.slug) !== null && _a !== void 0 ? _a : this.name; if (this.parent) { key = this.parent.getDeDuplicationKey() + '>' + key; } return key; - }; - RexCategory.prototype.addDuplicate = function (category) { + } + addDuplicate(category) { if (this.duplicates.indexOf(category) === -1) { this.duplicates.push(category); } - }; - RexCategory.prototype.getSubheadingItems = function () { - var items = []; + } + getSubheadingItems() { + let items = []; if (this.parent !== null) { items.push(this.parent.name); } if (this.duplicates.length > 0) { - for (var i = 0; i < this.duplicates.length; i++) { - var category = this.duplicates[i]; + for (let i = 0; i < this.duplicates.length; i++) { + let category = this.duplicates[i]; if (category.parent) { items.push(category.parent.name); } } } return items; - }; - RexCategory.prototype.getAbsoluteName = function () { - var components = [this.name]; - var parent = this.parent; + } + getAbsoluteName() { + let components = [this.name]; + let parent = this.parent; while (parent !== null) { components.unshift(parent.name); parent = parent.parent; } return components.join(' > '); - }; - RexCategory.defaultSubcategoryComparison = function (a, b) { - return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); - }; - return RexCategory; -}()); -var RexContentTypeCategory = /** @class */ (function (_super) { - __extends(RexContentTypeCategory, _super); - function RexContentTypeCategory(name, editor, slug) { - if (slug === void 0) { slug = null; } - var _this = _super.call(this, name, editor, slug) || this; - _this.actions = {}; - _this.baseCategorySlug = null; - _this.isBaseCapNoticeVisible = ko.pureComputed({ - read: function () { + } +} +RexCategory.defaultSubcategoryComparison = function (a, b) { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); +}; +class RexContentTypeCategory extends RexCategory { + constructor(name, editor, slug = null) { + super(name, editor, slug); + this.actions = {}; + this.baseCategorySlug = null; + this.isBaseCapNoticeVisible = ko.pureComputed({ + read: () => { if (editor.showBaseCapsEnabled()) { return false; } - return _this.usesBaseCapabilities(); + return this.usesBaseCapabilities(); }, deferEvaluation: true }); - return _this; } /** * Check if the post type or taxonomy represented by this category uses the same capabilities * as the built-in "post" type or the "category" taxonomy. */ - RexContentTypeCategory.prototype.usesBaseCapabilities = function () { - var baseCategory = this.getBaseCategory(); + usesBaseCapabilities() { + const baseCategory = this.getBaseCategory(); if (baseCategory === null || this === baseCategory) { return false; } - var allCapsMatch = true; + let allCapsMatch = true; wsAmeLodash.forEach(this.actions, function (item) { - var isMatch = item.action + let isMatch = item.action && baseCategory.actions.hasOwnProperty(item.action) && (item.capability === baseCategory.actions[item.action].capability); if (!isMatch) { @@ -812,219 +798,209 @@ var RexContentTypeCategory = /** @class */ (function (_super) { } }); return allCapsMatch; - }; - RexContentTypeCategory.prototype.getBaseCategory = function () { + } + getBaseCategory() { if (this.baseCategorySlug !== null) { - var result = this.findCategoryBySlug(this.baseCategorySlug); + let result = this.findCategoryBySlug(this.baseCategorySlug); if (result instanceof RexContentTypeCategory) { return result; } } return null; - }; - return RexContentTypeCategory; -}(RexCategory)); -var RexPostTypePermission = /** @class */ (function (_super) { - __extends(RexPostTypePermission, _super); - function RexPostTypePermission(editor, capability, action, pluralNoun) { - if (pluralNoun === void 0) { pluralNoun = ''; } - var _this = _super.call(this, editor, capability) || this; - _this.action = action; - _this.readableAction = wsAmeLodash.capitalize(_this.action.replace('_posts', '').replace('_', ' ')); + } +} +class RexPostTypePermission extends RexPermission { + constructor(editor, capability, action, pluralNoun = '') { + super(editor, capability); + this.action = action; + this.readableAction = wsAmeLodash.capitalize(this.action.replace('_posts', '').replace('_', ' ')); if (RexPostTypePermission.actionDescriptions.hasOwnProperty(action) && pluralNoun) { - _this.mainDescription = RexPostTypePermission.actionDescriptions[action].replace('%s', pluralNoun); - } - return _this; - } - RexPostTypePermission.actionDescriptions = { - 'edit_and_create': 'Edit and create %s', - 'edit_posts': 'Edit %s', - 'create_posts': 'Create new %s', - 'edit_published_posts': 'Edit published %s', - 'edit_others_posts': 'Edit %s created by others', - 'edit_private_posts': 'Edit private %s created by others', - 'publish_posts': 'Publish %s', - 'read_private_posts': 'Read private %s', - 'delete_posts': 'Delete %s', - 'delete_published_posts': 'Delete published %s', - 'delete_others_posts': 'Delete %s created by others', - 'delete_private_posts': 'Delete private %s created by others', - }; - return RexPostTypePermission; -}(RexPermission)); -var RexPostTypeCategory = /** @class */ (function (_super) { - __extends(RexPostTypeCategory, _super); - function RexPostTypeCategory(name, editor, postTypeId, slug, permissions, isDefault) { - if (slug === void 0) { slug = null; } - if (isDefault === void 0) { isDefault = false; } - var _this = _super.call(this, name, editor, slug) || this; - _this.pluralLabel = ''; - _this.actions = {}; - var _ = wsAmeLodash; - _this.baseCategorySlug = 'postTypes/post'; - _this.postType = postTypeId; - _this.isDefault = isDefault; - _this.subtitle = _this.postType; + this.mainDescription = RexPostTypePermission.actionDescriptions[action].replace('%s', pluralNoun); + } + } +} +RexPostTypePermission.actionDescriptions = { + 'edit_and_create': 'Edit and create %s', + 'edit_posts': 'Edit %s', + 'create_posts': 'Create new %s', + 'edit_published_posts': 'Edit published %s', + 'edit_others_posts': 'Edit %s created by others', + 'edit_private_posts': 'Edit private %s created by others', + 'publish_posts': 'Publish %s', + 'read_private_posts': 'Read private %s', + 'delete_posts': 'Delete %s', + 'delete_published_posts': 'Delete published %s', + 'delete_others_posts': 'Delete %s created by others', + 'delete_private_posts': 'Delete private %s created by others', +}; +class RexPostTypeCategory extends RexContentTypeCategory { + constructor(name, editor, postTypeId, slug = null, permissions, isDefault = false) { + super(name, editor, slug); + this.pluralLabel = ''; + this.actions = {}; + const _ = wsAmeLodash; + this.baseCategorySlug = 'postTypes/post'; + this.postType = postTypeId; + this.isDefault = isDefault; + this.subtitle = this.postType; if (editor.postTypes[postTypeId].pluralLabel) { - _this.pluralLabel = editor.postTypes[postTypeId].pluralLabel; + this.pluralLabel = editor.postTypes[postTypeId].pluralLabel; } else { - _this.pluralLabel = name.toLowerCase(); + this.pluralLabel = name.toLowerCase(); } - _this.permissions = ko.observableArray(_.map(permissions, function (capability, action) { - var permission = new RexPostTypePermission(editor, editor.getCapability(capability), action, _this.pluralLabel); + this.permissions = ko.observableArray(_.map(permissions, (capability, action) => { + if (typeof action === 'undefined') { + throw new Error('Invalid action name: undefined. This should never happen.'); + } + const permission = new RexPostTypePermission(editor, editor.getCapability(capability), action, this.pluralLabel); //The "read" capability is already shown in the core category and every role has it by default. if (capability === 'read') { permission.isRedundant = true; } - _this.actions[action] = permission; + this.actions[action] = permission; return permission; })); - _this.sortPermissions(); + this.sortPermissions(); //The "create" capability is often the same as the "edit" capability. - var editPerm = _.get(_this.actions, 'edit_posts', null), createPerm = _.get(_this.actions, 'create_posts', null); + const editPerm = _.get(this.actions, 'edit_posts', null), createPerm = _.get(this.actions, 'create_posts', null); if (editPerm && createPerm && (createPerm.capability.name === editPerm.capability.name)) { createPerm.isRedundant = true; } - return _this; } - RexPostTypeCategory.prototype.getDeDuplicationKey = function () { + getDeDuplicationKey() { return 'postType:' + this.postType; - }; - RexPostTypeCategory.prototype.sortPermissions = function () { - this.permissions.sort(function (a, b) { - var priorityA = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexPostTypeCategory.desiredActionOrder[a.action] : 1000; - var priorityB = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexPostTypeCategory.desiredActionOrder[b.action] : 1000; - var delta = priorityA - priorityB; - if (delta !== 0) { - return delta; - } - return a.capability.name.localeCompare(b.capability.name); - }); - }; - RexPostTypeCategory.prototype.getSubheadingItems = function () { - var items = _super.prototype.getSubheadingItems.call(this); + } + sortPermissions() { + this.permissions.sort((a, b) => { + if ((a instanceof RexPostTypePermission) && (b instanceof RexPostTypePermission)) { + const priorityA = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexPostTypeCategory.desiredActionOrder[a.action] : 1000; + const priorityB = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexPostTypeCategory.desiredActionOrder[b.action] : 1000; + let delta = priorityA - priorityB; + if (delta !== 0) { + return delta; + } + return a.capability.name.localeCompare(b.capability.name); + } + return this.compareBasicPermissions(a, b); + }); + } + getSubheadingItems() { + let items = super.getSubheadingItems(); items.push(this.postType); return items; - }; - RexPostTypeCategory.desiredActionOrder = { - 'edit_posts': 1, - 'edit_others_posts': 2, - 'edit_published_posts': 3, - 'edit_private_posts': 4, - 'publish_posts': 5, - 'delete_posts': 6, - 'delete_others_posts': 7, - 'delete_published_posts': 8, - 'delete_private_posts': 9, - 'read_private_posts': 10, - 'create_posts': 11, - }; - return RexPostTypeCategory; -}(RexContentTypeCategory)); -var RexTaxonomyPermission = /** @class */ (function (_super) { - __extends(RexTaxonomyPermission, _super); - function RexTaxonomyPermission(editor, capability, action, pluralNoun) { - if (pluralNoun === void 0) { pluralNoun = ''; } - var _this = _super.call(this, editor, capability) || this; - _this.action = action; - _this.readableAction = wsAmeLodash.capitalize(_this.action.replace('_terms', '').replace('_', ' ')); + } +} +RexPostTypeCategory.desiredActionOrder = { + 'edit_posts': 1, + 'edit_others_posts': 2, + 'edit_published_posts': 3, + 'edit_private_posts': 4, + 'publish_posts': 5, + 'delete_posts': 6, + 'delete_others_posts': 7, + 'delete_published_posts': 8, + 'delete_private_posts': 9, + 'read_private_posts': 10, + 'create_posts': 11, +}; +class RexTaxonomyPermission extends RexPermission { + constructor(editor, capability, action, pluralNoun = '') { + super(editor, capability); + this.action = action; + this.readableAction = wsAmeLodash.capitalize(this.action.replace('_terms', '').replace('_', ' ')); if (RexTaxonomyPermission.actionDescriptions.hasOwnProperty(action) && pluralNoun) { - _this.mainDescription = RexTaxonomyPermission.actionDescriptions[action].replace('%s', pluralNoun); - } - return _this; - } - RexTaxonomyPermission.actionDescriptions = { - 'manage_terms': 'Manage %s', - 'edit_terms': 'Edit %s', - 'delete_terms': 'Delete %s', - 'assign_terms': 'Assign %s', - }; - return RexTaxonomyPermission; -}(RexPermission)); -var RexTaxonomyCategory = /** @class */ (function (_super) { - __extends(RexTaxonomyCategory, _super); - function RexTaxonomyCategory(name, editor, taxonomyId, slug, permissions) { - if (slug === void 0) { slug = null; } - var _this = _super.call(this, name, editor, slug) || this; - _this.actions = {}; - var _ = wsAmeLodash; - _this.baseCategorySlug = 'taxonomies/category'; - _this.taxonomy = taxonomyId; - _this.subtitle = taxonomyId; - var noun = name.toLowerCase(); - _this.permissions = ko.observableArray(_.map(permissions, function (capability, action) { - var permission = new RexTaxonomyPermission(editor, editor.getCapability(capability), action, noun); - _this.actions[action] = permission; + this.mainDescription = RexTaxonomyPermission.actionDescriptions[action].replace('%s', pluralNoun); + } + } +} +RexTaxonomyPermission.actionDescriptions = { + 'manage_terms': 'Manage %s', + 'edit_terms': 'Edit %s', + 'delete_terms': 'Delete %s', + 'assign_terms': 'Assign %s', +}; +class RexTaxonomyCategory extends RexContentTypeCategory { + constructor(name, editor, taxonomyId, slug = null, permissions) { + super(name, editor, slug); + this.actions = {}; + const _ = wsAmeLodash; + this.baseCategorySlug = 'taxonomies/category'; + this.taxonomy = taxonomyId; + this.subtitle = taxonomyId; + const noun = name.toLowerCase(); + this.permissions = ko.observableArray(_.map(permissions, (capability, action) => { + if (typeof action === 'undefined') { + //Can't happen in practice, but TypeScript doesn't know that. + throw new Error('Invalid action name: undefined. This should never happen.'); + } + const permission = new RexTaxonomyPermission(editor, editor.getCapability(capability), action, noun); + this.actions[action] = permission; return permission; })); - _this.sortPermissions(); + this.sortPermissions(); //Permissions that use the same capability as the "manage_terms" permission are redundant. - if (_this.actions.manage_terms) { - var manageCap = _this.actions.manage_terms.capability.name; - for (var action in _this.actions) { - if (!_this.actions.hasOwnProperty(action)) { + if (this.actions.manage_terms) { + const manageCap = this.actions.manage_terms.capability.name; + for (let action in this.actions) { + if (!this.actions.hasOwnProperty(action)) { continue; } - if ((action !== 'manage_terms') && (_this.actions[action].capability.name === manageCap)) { - _this.actions[action].isRedundant = true; + if ((action !== 'manage_terms') && (this.actions[action].capability.name === manageCap)) { + this.actions[action].isRedundant = true; } } } - return _this; } - RexTaxonomyCategory.prototype.getDeDuplicationKey = function () { + getDeDuplicationKey() { return 'taxonomy:' + this.taxonomy; - }; - RexTaxonomyCategory.prototype.sortPermissions = function () { - this.permissions.sort(function (a, b) { - var priorityA = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexTaxonomyCategory.desiredActionOrder[a.action] : 1000; - var priorityB = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexTaxonomyCategory.desiredActionOrder[b.action] : 1000; - var delta = priorityA - priorityB; - if (delta !== 0) { - return delta; - } - return a.capability.name.localeCompare(b.capability.name); - }); - }; - RexTaxonomyCategory.prototype.getSubheadingItems = function () { - var items = _super.prototype.getSubheadingItems.call(this); + } + sortPermissions() { + this.permissions.sort((a, b) => { + if ((a instanceof RexTaxonomyPermission) && (b instanceof RexTaxonomyPermission)) { + const priorityA = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexTaxonomyCategory.desiredActionOrder[a.action] : 1000; + const priorityB = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexTaxonomyCategory.desiredActionOrder[b.action] : 1000; + let delta = priorityA - priorityB; + if (delta !== 0) { + return delta; + } + return a.capability.name.localeCompare(b.capability.name); + } + return this.compareBasicPermissions(a, b); + }); + } + getSubheadingItems() { + let items = super.getSubheadingItems(); items.push(this.taxonomy); return items; - }; - RexTaxonomyCategory.desiredActionOrder = { - 'manage_terms': 1, - 'edit_terms': 2, - 'delete_terms': 3, - 'assign_terms': 4, - }; - return RexTaxonomyCategory; -}(RexContentTypeCategory)); -var RexTableViewCategory = /** @class */ (function (_super) { - __extends(RexTableViewCategory, _super); - function RexTableViewCategory(name, editor, slug) { - if (slug === void 0) { slug = null; } - var _this = _super.call(this, name, editor, slug) || this; - _this.subcategoryComparisonCallback = null; - _this.contentTemplate = ko.pureComputed(function () { + } +} +RexTaxonomyCategory.desiredActionOrder = { + 'manage_terms': 1, + 'edit_terms': 2, + 'delete_terms': 3, + 'assign_terms': 4, +}; +class RexTableViewCategory extends RexCategory { + constructor(name, editor, slug = null) { + super(name, editor, slug); + this.contentTemplate = ko.pureComputed(function () { if (editor.categoryViewMode() === RexRoleEditor.hierarchyView) { return 'rex-permission-table-template'; } return 'rex-default-category-content-template'; }); - _this.subcategoryComparisonCallback = RexCategory.defaultSubcategoryComparison; - return _this; + this.subcategoryComparisonCallback = RexCategory.defaultSubcategoryComparison; } - RexTableViewCategory.prototype.getSortedSubcategories = function () { - var _this = this; + getSortedSubcategories() { if (this.editor.showBaseCapsEnabled()) { - return _super.prototype.getSortedSubcategories.call(this); + return super.getSortedSubcategories(); } - var cats = wsAmeLodash.clone(this.subcategories); - return cats.sort(function (a, b) { + let cats = wsAmeLodash.clone(this.subcategories); + return cats.sort((a, b) => { //Special case: Put categories that use base capabilities at the end. - var aEqualsBase = a.usesBaseCapabilities(); - var bEqualsBase = b.usesBaseCapabilities(); + const aEqualsBase = a.usesBaseCapabilities(); + const bEqualsBase = b.usesBaseCapabilities(); if (aEqualsBase && !bEqualsBase) { return 1; } @@ -1032,28 +1008,25 @@ var RexTableViewCategory = /** @class */ (function (_super) { return -1; } //Otherwise just sort in the default order. - return _this.subcategoryComparisonCallback(a, b); + return this.subcategoryComparisonCallback(a, b); }); - }; + } /** * Sort the underlying category array. */ - RexTableViewCategory.prototype.sortSubcategories = function () { + sortSubcategories() { this.subcategories.sort(this.subcategoryComparisonCallback); - }; - return RexTableViewCategory; -}(RexCategory)); -var RexTaxonomyContainerCategory = /** @class */ (function (_super) { - __extends(RexTaxonomyContainerCategory, _super); - function RexTaxonomyContainerCategory(name, editor, slug) { - if (slug === void 0) { slug = null; } - var _this = _super.call(this, name, editor, slug) || this; - _this.htmlId = 'rex-taxonomy-summary-category'; - _this.tableColumns = ko.pureComputed({ - read: function () { - var _ = wsAmeLodash; - var defaultTaxonomyActions = ['manage_terms', 'assign_terms', 'edit_terms', 'delete_terms']; - var columns = [ + } +} +class RexTaxonomyContainerCategory extends RexTableViewCategory { + constructor(name, editor, slug = null) { + super(name, editor, slug); + this.htmlId = 'rex-taxonomy-summary-category'; + this.tableColumns = ko.pureComputed({ + read: () => { + const _ = wsAmeLodash; + const defaultTaxonomyActions = ['manage_terms', 'assign_terms', 'edit_terms', 'delete_terms']; + let columns = [ { title: 'Manage', actions: ['manage_terms'] @@ -1071,16 +1044,16 @@ var RexTaxonomyContainerCategory = /** @class */ (function (_super) { actions: ['delete_terms'] } ]; - var misColumnExists = false, miscColumn = null; - for (var i = 0; i < _this.subcategories.length; i++) { - var category = _this.subcategories[i]; + let miscColumn = null; + for (let i = 0; i < this.subcategories.length; i++) { + const category = this.subcategories[i]; if (!(category instanceof RexTaxonomyCategory)) { continue; } //Display any unrecognized actions in a "Misc" column. - var customActions = _.omit(category.actions, defaultTaxonomyActions); + const customActions = _.omit(category.actions, defaultTaxonomyActions); if (!_.isEmpty(customActions)) { - if (!misColumnExists) { + if (!miscColumn) { miscColumn = { title: 'Misc', actions: [] }; columns.push(miscColumn); } @@ -1091,42 +1064,41 @@ var RexTaxonomyContainerCategory = /** @class */ (function (_super) { }, deferEvaluation: true, }); - return _this; } - return RexTaxonomyContainerCategory; -}(RexTableViewCategory)); -var RexPostTypeContainerCategory = /** @class */ (function (_super) { - __extends(RexPostTypeContainerCategory, _super); - function RexPostTypeContainerCategory(name, editor, slug) { - if (slug === void 0) { slug = null; } - var _this = _super.call(this, name, editor, slug) || this; +} +class RexPostTypeContainerCategory extends RexTableViewCategory { + constructor(name, editor, slug = null) { + super(name, editor, slug); /* Note: This seems like poor design because the superclass overrides subclass * behaviour (subcategory comparison) in some situations. Unfortunately, I haven't * come up with anything better so far. Might be something to revisit later. */ - _this.subcategoryComparisonCallback = function (a, b) { - //Special case: Put "Posts" at the top. - if (a.postType === 'post') { - return -1; - } - else if (b.postType === 'post') { - return 1; - } - //Put other built-in post types above custom post types. - if (a.isDefault && !b.isDefault) { - return -1; - } - else if (b.isDefault && !a.isDefault) { - return 1; + this.subcategoryComparisonCallback = function (a, b) { + if ((a instanceof RexPostTypeCategory) && (b instanceof RexPostTypeCategory)) { + //Special case: Put "Posts" at the top. + if (a.postType === 'post') { + return -1; + } + else if (b.postType === 'post') { + return 1; + } + //Put other built-in post types above custom post types. + if (a.isDefault && !b.isDefault) { + return -1; + } + else if (b.isDefault && !a.isDefault) { + return 1; + } + let labelA = a.name.toLowerCase(), labelB = b.name.toLowerCase(); + return labelA.localeCompare(labelB); } - var labelA = a.name.toLowerCase(), labelB = b.name.toLowerCase(); - return labelA.localeCompare(labelB); + return RexCategory.defaultSubcategoryComparison(a, b); }; - _this.tableColumns = ko.pureComputed({ - read: function () { - var _ = wsAmeLodash; - var defaultPostTypeActions = _.keys(RexPostTypePermission.actionDescriptions); - var columns = [ + this.tableColumns = ko.pureComputed({ + read: () => { + const _ = wsAmeLodash; + const defaultPostTypeActions = _.keys(RexPostTypePermission.actionDescriptions); + let columns = [ { title: 'Own items', actions: ['create_posts', 'edit_posts', 'delete_posts', 'publish_posts', 'edit_published_posts', 'delete_published_posts'] @@ -1136,18 +1108,18 @@ var RexPostTypeContainerCategory = /** @class */ (function (_super) { actions: ['edit_others_posts', 'delete_others_posts', 'edit_private_posts', 'delete_private_posts', 'read_private_posts'] } ]; - var metaColumn = { + let metaColumn = { title: 'Meta', actions: ['edit_post', 'delete_post', 'read_post'] }; columns.push(metaColumn); - for (var i = 0; i < _this.subcategories.length; i++) { - var category = _this.subcategories[i]; + for (let i = 0; i < this.subcategories.length; i++) { + const category = this.subcategories[i]; if (!(category instanceof RexPostTypeCategory)) { continue; } //Display any unrecognized actions in a "Misc" column. - var customActions = _.omit(category.actions, defaultPostTypeActions); + const customActions = _.omit(category.actions, defaultPostTypeActions); if (!_.isEmpty(customActions)) { metaColumn.actions = _.union(metaColumn.actions, _.keys(customActions)); } @@ -1156,13 +1128,10 @@ var RexPostTypeContainerCategory = /** @class */ (function (_super) { }, deferEvaluation: true, }); - return _this; } - return RexPostTypeContainerCategory; -}(RexTableViewCategory)); -var RexCapability = /** @class */ (function () { - function RexCapability(name, editor) { - var _this = this; +} +class RexCapability { + constructor(name, editor) { this.originComponent = null; this.usedByComponents = []; this.menuItems = []; @@ -1173,7 +1142,7 @@ var RexCapability = /** @class */ (function () { this.notes = null; this.name = String(name); this.editor = editor; - var self = this; + const self = this; this.readableName = wsAmeLodash.capitalize(this.name.replace(/[_\-\s]+/g, ' ')); this.displayName = ko.pureComputed({ read: function () { @@ -1185,7 +1154,7 @@ var RexCapability = /** @class */ (function () { this.isDeleted = ko.observable(false); this.responsibleActors = ko.computed({ read: function () { - var actor = editor.selectedActor(), list = []; + let actor = editor.selectedActor(), list = []; if (actor instanceof RexUser) { actor.hasCap(self.name, list); } @@ -1196,11 +1165,11 @@ var RexCapability = /** @class */ (function () { }); this.isInherited = ko.computed({ read: function () { - var actor = editor.selectedActor(); + const actor = editor.selectedActor(); if (!actor.canHaveRoles) { return false; } - var responsibleActors = self.responsibleActors(); + const responsibleActors = self.responsibleActors(); return responsibleActors && (responsibleActors.length > 0) && (wsAmeLodash.indexOf(responsibleActors, actor) < (responsibleActors.length - 1)); @@ -1211,7 +1180,7 @@ var RexCapability = /** @class */ (function () { this.isPersonalOverride = ko.pureComputed({ read: function () { //This flag applies only to actors that can inherit permissions. - var actor = editor.selectedActor(); + const actor = editor.selectedActor(); if (!actor.canHaveRoles) { return false; } @@ -1234,11 +1203,11 @@ var RexCapability = /** @class */ (function () { return editor.selectedActor().hasCap(self.name); }, write: function (newState) { - var actor = editor.selectedActor(); + const actor = editor.selectedActor(); if (editor.isShiftKeyDown()) { //Hold the shift key while clicking to cycle the capability between 3 states: //Granted -> Denied -> Not granted. - var oldState = actor.getOwnCapabilityState(self.name); + const oldState = actor.getOwnCapabilityState(self.name); if (newState) { if (oldState === false) { actor.deleteCap(self.name); //Denied -> Not granted. @@ -1281,7 +1250,7 @@ var RexCapability = /** @class */ (function () { //this.isEnabledForSelectedActor.extend({rateLimit: {timeout: 10, method: "notifyWhenChangesStop"}}); this.isExplicitlyDenied = ko.pureComputed({ read: function () { - var actor = editor.selectedActor(); + const actor = editor.selectedActor(); if (actor) { return (actor.getCapabilityState(self.name) === false); } @@ -1290,20 +1259,23 @@ var RexCapability = /** @class */ (function () { deferEvaluation: true }); this.grantedPermissions = ko.computed({ - read: function () { - var _ = wsAmeLodash; - var results = []; - if (_this.predefinedPermissions.length > 0) { - results = _this.predefinedPermissions.slice(); + read: () => { + const _ = wsAmeLodash; + let results = []; + if (this.predefinedPermissions.length > 0) { + results = this.predefinedPermissions.slice(); } function localeAwareCompare(a, b) { return a.localeCompare(b); } function actionsToPermissions(actionGroups, labelMap, descriptions) { - return _.map(actionGroups, function (ids, action) { - var labels = _.map(ids, function (id) { return labelMap[id].pluralLabel; }) + return _.map(actionGroups, (ids, action) => { + if (typeof action === 'undefined') { + throw new Error('Undefined action. This should never happen.'); + } + let labels = _.map(ids, (id) => labelMap[id].pluralLabel) .sort(localeAwareCompare); - var template = descriptions[action]; + let template = descriptions[action]; if (!template) { template = action + ': %s'; } @@ -1311,10 +1283,13 @@ var RexCapability = /** @class */ (function () { }).sort(localeAwareCompare); } //Post permissions. - var postActionGroups = _.transform(_this.usedByPostTypeActions, function (accumulator, actions, postType) { - var actionKeys = _.keys(actions); + let postActionGroups = _.transform(this.usedByPostTypeActions, function (accumulator, actions, postType) { + if (typeof postType === 'undefined') { + return; + } + let actionKeys = _.keys(actions); //Combine "edit" and "create" permissions because they usually use the same capability. - var editEqualsCreate = actions.hasOwnProperty('edit_posts') && actions.hasOwnProperty('create_posts'); + const editEqualsCreate = actions.hasOwnProperty('edit_posts') && actions.hasOwnProperty('create_posts'); if (editEqualsCreate) { actionKeys = _.without(actionKeys, 'edit_posts', 'create_posts'); actionKeys.unshift('edit_and_create'); @@ -1326,11 +1301,14 @@ var RexCapability = /** @class */ (function () { accumulator[action].push(postType); }); }, {}); - var postPermissions = actionsToPermissions(postActionGroups, _this.editor.postTypes, RexPostTypePermission.actionDescriptions); + let postPermissions = actionsToPermissions(postActionGroups, this.editor.postTypes, RexPostTypePermission.actionDescriptions); Array.prototype.push.apply(results, postPermissions); //Taxonomy permissions. - var taxonomyActionGroups = _.transform(_this.usedByTaxonomyActions, function (accumulator, actions, taxonomy) { - var actionKeys = _.keys(actions); + let taxonomyActionGroups = _.transform(this.usedByTaxonomyActions, function (accumulator, actions, taxonomy) { + if (typeof taxonomy === 'undefined') { + return; + } + let actionKeys = _.keys(actions); //Most taxonomies use the same capability for manage_terms, edit_terms, and delete_terms. //In those cases, let's show only manage_terms. if (actions.hasOwnProperty('manage_terms')) { @@ -1343,9 +1321,9 @@ var RexCapability = /** @class */ (function () { accumulator[action].push(taxonomy); }); }, {}); - var taxonomyPermissions = actionsToPermissions(taxonomyActionGroups, _this.editor.taxonomies, RexTaxonomyPermission.actionDescriptions); + let taxonomyPermissions = actionsToPermissions(taxonomyActionGroups, this.editor.taxonomies, RexTaxonomyPermission.actionDescriptions); Array.prototype.push.apply(results, taxonomyPermissions); - Array.prototype.push.apply(results, _this.menuItems); + Array.prototype.push.apply(results, this.menuItems); return results; }, deferEvaluation: true, @@ -1353,7 +1331,7 @@ var RexCapability = /** @class */ (function () { }); } // noinspection JSUnusedGlobalSymbols Used in KO templates. - RexCapability.prototype.getDocumentationUrl = function () { + getDocumentationUrl() { if (this.documentationUrl) { return this.documentationUrl; } @@ -1362,9 +1340,9 @@ var RexCapability = /** @class */ (function () { return this.documentationUrl; } return null; - }; - RexCapability.fromJs = function (name, data, editor) { - var capability = new RexCapability(name, editor); + } + static fromJs(name, data, editor) { + const capability = new RexCapability(name, editor); capability.menuItems = data.menuItems.sort(function (a, b) { return a.localeCompare(b); }); @@ -1372,8 +1350,8 @@ var RexCapability = /** @class */ (function () { capability.originComponent = editor.getComponent(data.componentId); } if (data.usedByComponents) { - for (var id in data.usedByComponents) { - var component = editor.getComponent(id); + for (let id in data.usedByComponents) { + const component = editor.getComponent(id); if (component) { capability.usedByComponents.push(component); } @@ -1393,68 +1371,57 @@ var RexCapability = /** @class */ (function () { capability.readableName = data.readableName; } return capability; - }; - RexCapability.formatNounList = function (items) { + } + static formatNounList(items) { if (items.length <= 2) { return items.join(' and '); } return items.slice(0, -1).join(', ') + ', and ' + items[items.length - 1]; - }; - return RexCapability; -}()); -var RexDoNotAllowCapability = /** @class */ (function (_super) { - __extends(RexDoNotAllowCapability, _super); - function RexDoNotAllowCapability(editor) { - var _this = _super.call(this, 'do_not_allow', editor) || this; - _this.notes = '"do_not_allow" is a special capability. ' + } +} +class RexDoNotAllowCapability extends RexCapability { + constructor(editor) { + super('do_not_allow', editor); + this.notes = '"do_not_allow" is a special capability. ' + 'WordPress uses it internally to indicate that access is denied. ' + 'Normally, it should not be assigned to any roles or users.'; //Normally, it's impossible to grant this capability to anyone. Doing so would break things. //However, if it's already granted, you can remove it. - _this.isEditable = ko.computed(function () { - return _this.isEnabledForSelectedActor(); - }); - return _this; - } - return RexDoNotAllowCapability; -}(RexCapability)); -var RexExistCapability = /** @class */ (function (_super) { - __extends(RexExistCapability, _super); - function RexExistCapability(editor) { - var _this = _super.call(this, 'exist', editor) || this; - _this.notes = '"exist" is a special capability. ' + this.isEditable = ko.computed(() => { + return this.isEnabledForSelectedActor(); + }); + } +} +class RexExistCapability extends RexCapability { + constructor(editor) { + super('exist', editor); + this.notes = '"exist" is a special capability. ' + 'WordPress uses it internally to indicate that a role or user exists. ' + 'Normally, everyone has this capability by default, and it is not necessary ' + '(or possible) to assign it directly.'; //Everyone must have this capability. However, if it has somehow become disabled, //we'll let the user enable it. - _this.isEditable = ko.computed(function () { - return !_this.isEnabledForSelectedActor(); - }); - return _this; - } - return RexExistCapability; -}(RexCapability)); -var RexInvalidCapability = /** @class */ (function (_super) { - __extends(RexInvalidCapability, _super); - function RexInvalidCapability(fakeName, value, editor) { - var _this = _super.call(this, fakeName, editor) || this; - var startsWithVowel = /^[aeiou]/i; - var theType = (typeof value); - var nounPhrase = (startsWithVowel.test(theType) ? 'an' : 'a') + ' ' + theType; - _this.notes = 'This is not a valid capability. A capability name must be a string (i.e. text),' + this.isEditable = ko.computed(() => { + return !this.isEnabledForSelectedActor(); + }); + } +} +class RexInvalidCapability extends RexCapability { + constructor(fakeName, value, editor) { + super(fakeName, editor); + const startsWithVowel = /^[aeiou]/i; + let theType = (typeof value); + const nounPhrase = (startsWithVowel.test(theType) ? 'an' : 'a') + ' ' + theType; + this.notes = 'This is not a valid capability. A capability name must be a string (i.e. text),' + ' but this is ' + nounPhrase + '. It was probably created by a bug in another plugin or theme.'; - _this.isEditable = ko.computed(function () { + this.isEditable = ko.computed(() => { return false; }); - return _this; } - return RexInvalidCapability; -}(RexCapability)); -var RexUserPreferences = /** @class */ (function () { - function RexUserPreferences(initialPreferences, ajaxUrl, updateNonce) { - var _this = this; - var _ = wsAmeLodash; +} +class RexUserPreferences { + constructor(initialPreferences, ajaxUrl, updateNonce) { + const _ = wsAmeLodash; initialPreferences = initialPreferences || {}; if (_.isArray(initialPreferences)) { initialPreferences = {}; @@ -1462,22 +1429,22 @@ var RexUserPreferences = /** @class */ (function () { this.preferenceObservables = _.mapValues(initialPreferences, ko.observable, ko); this.preferenceCount = ko.observable(_.size(this.preferenceObservables)); this.collapsedCategories = new RexCollapsedCategorySet(_.get(initialPreferences, 'collapsedCategories', [])); - this.plainPreferences = ko.computed(function () { + this.plainPreferences = ko.computed(() => { //By creating a dependency on the number of preferences, we ensure that the observable will be re-evaluated //whenever a preference is added or removed. - _this.preferenceCount(); + this.preferenceCount(); //This converts preferences to a plain JS object and establishes dependencies on all individual observables. - var result = _.mapValues(_this.preferenceObservables, function (observable) { + let result = _.mapValues(this.preferenceObservables, function (observable) { return observable(); }); - result.collapsedCategories = _this.collapsedCategories.toJs(); + result.collapsedCategories = this.collapsedCategories.toJs(); return result; }); //Avoid excessive AJAX requests. this.plainPreferences.extend({ rateLimit: { timeout: 5000, method: "notifyWhenChangesStop" } }); //Save preferences when they change. if (ajaxUrl && updateNonce) { - this.plainPreferences.subscribe(function (preferences) { + this.plainPreferences.subscribe((preferences) => { //console.info('Saving user preferences', preferences); jQuery.post(ajaxUrl, { action: 'ws_ame_rex_update_user_preferences', @@ -1487,89 +1454,88 @@ var RexUserPreferences = /** @class */ (function () { }); } } - RexUserPreferences.prototype.getObservable = function (name, defaultValue) { - if (defaultValue === void 0) { defaultValue = null; } + getObservable(name, defaultValue) { if (this.preferenceObservables.hasOwnProperty(name)) { return this.preferenceObservables[name]; } - var newPreference = ko.observable(defaultValue || null); + const newPreference = ko.observable(defaultValue); this.preferenceObservables[name] = newPreference; this.preferenceCount(this.preferenceCount() + 1); return newPreference; - }; - return RexUserPreferences; -}()); + } +} /** * An observable collection of unique strings. In this case, they're category slugs. */ -var RexCollapsedCategorySet = /** @class */ (function () { - function RexCollapsedCategorySet(items) { - if (items === void 0) { items = []; } +class RexCollapsedCategorySet { + constructor(items = []) { this.isItemInSet = {}; items = wsAmeLodash.uniq(items); - for (var i = 0; i < items.length; i++) { + for (let i = 0; i < items.length; i++) { this.isItemInSet[items[i]] = ko.observable(true); } this.items = ko.observableArray(items); } - RexCollapsedCategorySet.prototype.getItemObservable = function (item) { + getItemObservable(item) { if (!this.isItemInSet.hasOwnProperty(item)) { this.isItemInSet[item] = ko.observable(false); } return this.isItemInSet[item]; - }; - RexCollapsedCategorySet.prototype.add = function (item) { + } + add(item) { if (!this.contains(item)) { this.getItemObservable(item)(true); this.items.push(item); } - }; - RexCollapsedCategorySet.prototype.remove = function (item) { + } + remove(item) { if (this.contains(item)) { this.isItemInSet[item](false); this.items.remove(item); } - }; - RexCollapsedCategorySet.prototype.toggle = function (item, addToSet) { + } + toggle(item, addToSet) { if (addToSet) { this.add(item); } else { this.remove(item); } - }; - RexCollapsedCategorySet.prototype.contains = function (item) { + } + contains(item) { return this.getItemObservable(item)(); - }; - RexCollapsedCategorySet.prototype.peek = function (item) { + } + peek(item) { if (!this.isItemInSet.hasOwnProperty(item)) { return false; } return this.isItemInSet[item].peek(); - }; - RexCollapsedCategorySet.prototype.toJs = function () { + } + toJs() { return this.items(); - }; - return RexCollapsedCategorySet; -}()); -var RexBaseDialog = /** @class */ (function () { - function RexBaseDialog() { - var _this = this; + } +} +class RexBaseDialog { + constructor() { this.isOpen = ko.observable(false); this.isRendered = ko.observable(false); - this.title = null; + this.jQueryWidget = null; + this.title = ko.observable(null); this.options = { buttons: [] }; - this.isOpen.subscribe(function (isOpenNow) { - if (isOpenNow && !_this.isRendered()) { - _this.isRendered(true); + this.isOpen.subscribe((isOpenNow) => { + if (isOpenNow && !this.isRendered()) { + this.isRendered(true); } }); } - RexBaseDialog.prototype.setupValidationTooltip = function (inputSelector, message) { + setupValidationTooltip(inputSelector, message) { + if (this.jQueryWidget === null) { + return; + } //Display validation messages next to the input field. - var element = this.jQueryWidget.find(inputSelector).qtip({ + const element = this.jQueryWidget.find(inputSelector).qtip({ overwrite: false, content: '(Validation errors will appear here.)', //Show the tooltip when the input is focused. @@ -1591,7 +1557,7 @@ var RexBaseDialog = /** @class */ (function () { classes: 'qtip-bootstrap qtip-shadow rex-tooltip' } }); - message.subscribe(function (newMessage) { + message.subscribe((newMessage) => { if (newMessage == '') { element.qtip('option', 'content.text', 'OK'); element.qtip('option', 'show.event', ''); @@ -1604,57 +1570,55 @@ var RexBaseDialog = /** @class */ (function () { } }); //Hide the tooltip when the dialog is closed and prevent it from automatically re-appearing. - this.isOpen.subscribe(function (isDialogOpen) { + this.isOpen.subscribe((isDialogOpen) => { if (!isDialogOpen) { element.qtip('option', 'show.event', ''); element.qtip('hide'); } }); - }; + } ; - return RexBaseDialog; -}()); -var RexDeleteCapDialog = /** @class */ (function (_super) { - __extends(RexDeleteCapDialog, _super); - function RexDeleteCapDialog(editor) { - var _this = _super.call(this) || this; - _this.options = { +} +class RexDeleteCapDialog extends RexBaseDialog { + constructor(editor) { + super(); + this.options = { buttons: [], minWidth: 380 }; - _this.wasEverOpen = ko.observable(false); - var _ = wsAmeLodash; - _this.options.buttons.push({ + this.wasEverOpen = ko.observable(false); + const _ = wsAmeLodash; + this.options.buttons.push({ text: 'Delete Capability', 'class': 'button button-primary rex-delete-selected-caps', - click: function () { - var selectedCapabilities = _.chain(_this.deletableItems()) + click: () => { + let selectedCapabilities = _.chain(this.deletableItems()) .filter(function (item) { return item.isSelected(); }) .pluck('capability') .value(); //Note: We could remove confirmation if we get an "Undo" feature. - var noun = (selectedCapabilities.length === 1) ? 'capability' : 'capabilities'; - var warning = 'Caution: Deleting capabilities could break plugins that use those capabilities. ' + const noun = (selectedCapabilities.length === 1) ? 'capability' : 'capabilities'; + const warning = 'Caution: Deleting capabilities could break plugins that use those capabilities. ' + 'Delete ' + selectedCapabilities.length + ' ' + noun + '?'; if (!confirm(warning)) { return; } - _this.isOpen(false); + this.isOpen(false); editor.deleteCapabilities(selectedCapabilities); alert(selectedCapabilities.length + ' capabilities deleted'); }, disabled: true }); - _this.isOpen.subscribe(function (open) { - if (open && !_this.wasEverOpen()) { - _this.wasEverOpen(true); + this.isOpen.subscribe((open) => { + if (open && !this.wasEverOpen()) { + this.wasEverOpen(true); } }); - _this.deletableItems = ko.pureComputed({ - read: function () { - var wpCore = editor.getComponent(':wordpress:'); + this.deletableItems = ko.pureComputed({ + read: () => { + const wpCore = editor.getComponent(':wordpress:'); return _.chain(editor.capabilities) .filter(function (capability) { if (capability.originComponent === wpCore) { @@ -1663,7 +1627,7 @@ var RexDeleteCapDialog = /** @class */ (function (_super) { return !capability.isDeleted(); }) //Pre-populate part of the list when the dialog is closed to ensure it has a non-zero height. - .take(_this.wasEverOpen() ? 1000000 : 30) + .take(this.wasEverOpen() ? 1000000 : 30) .sortBy(function (capability) { return capability.name.toLowerCase(); }) @@ -1677,15 +1641,15 @@ var RexDeleteCapDialog = /** @class */ (function (_super) { }, deferEvaluation: true }); - _this.selectedItemCount = ko.pureComputed({ - read: function () { return _.filter(_this.deletableItems(), function (item) { + this.selectedItemCount = ko.pureComputed({ + read: () => _.filter(this.deletableItems(), function (item) { return item.isSelected(); - }).length; }, + }).length, deferEvaluation: true }); - var deleteButtonText = ko.pureComputed({ - read: function () { - var count = _this.selectedItemCount(); + const deleteButtonText = ko.pureComputed({ + read: () => { + const count = this.selectedItemCount(); if (count <= 0) { return 'Delete Capability'; } @@ -1700,63 +1664,63 @@ var RexDeleteCapDialog = /** @class */ (function (_super) { }, deferEvaluation: true }); - deleteButtonText.subscribe(function (newText) { - _this.jQueryWidget + deleteButtonText.subscribe((newText) => { + if (this.jQueryWidget === null) { + return; + } + this.jQueryWidget .closest('.ui-dialog') .find('.ui-dialog-buttonset .button-primary .ui-button-text') .text(newText); }); - _this.isDeleteButtonEnabled = ko.pureComputed({ - read: function () { - return _this.selectedItemCount() > 0; + this.isDeleteButtonEnabled = ko.pureComputed({ + read: () => { + return this.selectedItemCount() > 0; }, deferEvaluation: true }); - return _this; } - RexDeleteCapDialog.prototype.onOpen = function () { + onOpen() { //Deselect all items when the dialog is opened. - var items = this.deletableItems(); - for (var i = 0; i < items.length; i++) { + const items = this.deletableItems(); + for (let i = 0; i < items.length; i++) { if (items[i].isSelected()) { items[i].isSelected(false); } } - }; - return RexDeleteCapDialog; -}(RexBaseDialog)); -var RexAddCapabilityDialog = /** @class */ (function (_super) { - __extends(RexAddCapabilityDialog, _super); - function RexAddCapabilityDialog(editor) { - var _this = _super.call(this) || this; - _this.autoCancelButton = true; - _this.options = { + } +} +class RexAddCapabilityDialog extends RexBaseDialog { + constructor(editor) { + super(); + this.autoCancelButton = true; + this.options = { minWidth: 380 }; - _this.validationState = ko.observable(RexAddCapabilityDialog.states.empty); - _this.validationMessage = ko.observable(''); - var _ = wsAmeLodash; - _this.editor = editor; - var excludedCaps = ['do_not_allow', 'exist', 'customize']; - var newCapabilityName = ko.observable(''); - _this.capabilityName = ko.computed({ + this.validationState = ko.observable(RexAddCapabilityDialog.states.empty); + this.validationMessage = ko.observable(''); + const _ = wsAmeLodash; + this.editor = editor; + const excludedCaps = ['do_not_allow', 'exist', 'customize']; + let newCapabilityName = ko.observable(''); + this.capabilityName = ko.computed({ read: function () { return newCapabilityName(); }, - write: function (value) { + write: (value) => { value = _.trimRight(value); //Validate and sanitize the capability name. - var state = _this.validationState, message = _this.validationMessage; + let state = this.validationState, message = this.validationMessage; //WP API allows completely arbitrary capability names, but this plugin forbids some characters //for sanity's sake and to avoid XSS. - var invalidCharacters = /[><&\r\n\t]/g; + const invalidCharacters = /[><&\r\n\t]/g; //While all other characters are allowed, it's recommended to stick to alphanumerics, //underscores and dashes. Spaces are also OK because some other plugins use them. - var suspiciousCharacters = /[^a-z0-9_ -]/ig; + const suspiciousCharacters = /[^a-z0-9_ -]/ig; //PHP doesn't allow numeric string keys, and there's no conceivable reason to start the name with a space. - var invalidFirstCharacter = /^[\s0-9]/i; - var foundInvalid = value.match(invalidCharacters); - var foundSuspicious = value.match(suspiciousCharacters); + const invalidFirstCharacter = /^[\s0-9]/i; + let foundInvalid = value.match(invalidCharacters); + let foundSuspicious = value.match(suspiciousCharacters); if (foundInvalid !== null) { state(RexAddCapabilityDialog.states.error); message('Sorry, ' + _.escape(_.last(foundInvalid)) + ' is not allowed here.'); @@ -1794,29 +1758,28 @@ var RexAddCapabilityDialog = /** @class */ (function (_super) { newCapabilityName(value); } }); - var acceptableStates = [RexAddCapabilityDialog.states.valid, RexAddCapabilityDialog.states.notice]; - _this.isAddButtonEnabled = ko.pureComputed(function () { - return (acceptableStates.indexOf(_this.validationState()) >= 0); + const acceptableStates = [RexAddCapabilityDialog.states.valid, RexAddCapabilityDialog.states.notice]; + this.isAddButtonEnabled = ko.pureComputed(() => { + return (acceptableStates.indexOf(this.validationState()) >= 0); }); - _this.options.buttons = [{ + this.options.buttons = [{ text: 'Add Capability', 'class': 'button button-primary', - click: function () { - _this.onConfirm(); + click: () => { + this.onConfirm(); }, disabled: true }]; - return _this; } - RexAddCapabilityDialog.prototype.onOpen = function (event, ui) { + onOpen(event, ui) { //Clear the input when the dialog is opened. this.capabilityName(''); - }; - RexAddCapabilityDialog.prototype.onConfirm = function () { + } + onConfirm() { if (!this.isAddButtonEnabled()) { return; } - var category = this.editor.addCapability(this.capabilityName().trim()); + const category = this.editor.addCapability(this.capabilityName().trim()); this.isOpen(false); //Note: Maybe the user doesn't need this alert? Hmm. if (!category || (this.editor.categoryViewMode() === RexRoleEditor.listView)) { @@ -1825,52 +1788,50 @@ var RexAddCapabilityDialog = /** @class */ (function (_super) { else { alert('Capability added to the "' + category.getAbsoluteName() + '" category.'); } - }; - RexAddCapabilityDialog.states = { - valid: 'valid', - empty: 'empty', - notice: 'notice', - error: 'error' - }; - return RexAddCapabilityDialog; -}(RexBaseDialog)); -var RexAddRoleDialog = /** @class */ (function (_super) { - __extends(RexAddRoleDialog, _super); - function RexAddRoleDialog(editor) { - var _this = _super.call(this) || this; - _this.roleName = ko.observable(''); - _this.roleDisplayName = ko.observable(''); - _this.roleToCopyFrom = ko.observable(null); - _this.nameValidationMessage = ko.observable(''); - _this.displayNameValidationMessage = ko.observable(''); - _this.areTooltipsInitialised = false; - var _ = wsAmeLodash; - _this.editor = editor; - _this.options.minWidth = 380; - _this.options.buttons.push({ + } +} +RexAddCapabilityDialog.states = { + valid: 'valid', + empty: 'empty', + notice: 'notice', + error: 'error' +}; +class RexAddRoleDialog extends RexBaseDialog { + constructor(editor) { + super(); + this.roleName = ko.observable(''); + this.roleDisplayName = ko.observable(''); + this.roleToCopyFrom = ko.observable(null); + this.nameValidationMessage = ko.observable(''); + this.displayNameValidationMessage = ko.observable(''); + this.areTooltipsInitialised = false; + const _ = wsAmeLodash; + this.editor = editor; + this.options.minWidth = 380; + this.options.buttons.push({ text: 'Add Role', 'class': 'button button-primary', - click: _this.onConfirm.bind(_this), + click: this.onConfirm.bind(this), disabled: true }); - _this.roleDisplayName.extend({ rateLimit: 10 }); - _this.roleName.extend({ rateLimit: 10 }); + this.roleDisplayName.extend({ rateLimit: 10 }); + this.roleName.extend({ rateLimit: 10 }); //Role names are restricted - you can only use lowercase Latin letters, numbers and underscores. - var roleNameCharacterGroup = 'a-z0-9_'; - var invalidCharacterRegex = new RegExp('[^' + roleNameCharacterGroup + ']', 'g'); - var numbersOnlyRegex = /^[0-9]+$/; - _this.isNameValid = ko.computed(function () { - var name = _this.roleName().trim(); - var message = _this.nameValidationMessage; + const roleNameCharacterGroup = 'a-z0-9_'; + const invalidCharacterRegex = new RegExp('[^' + roleNameCharacterGroup + ']', 'g'); + const numbersOnlyRegex = /^[0-9]+$/; + this.isNameValid = ko.computed(() => { + let name = this.roleName().trim(); + let message = this.nameValidationMessage; //Name must not be empty. if (name === '') { message(''); return false; } //Name can only contain certain characters. - var invalidChars = name.match(invalidCharacterRegex); + const invalidChars = name.match(invalidCharacterRegex); if (invalidChars !== null) { - var lastInvalidChar = _.last(invalidChars); + let lastInvalidChar = _.last(invalidChars); if (lastInvalidChar === ' ') { lastInvalidChar = 'space'; } @@ -1884,7 +1845,7 @@ var RexAddRoleDialog = /** @class */ (function (_super) { return false; } //Name must not be a duplicate. - var existingRole = editor.getRole(name); + let existingRole = editor.getRole(name); if (existingRole) { message('Duplicate role name.'); return false; @@ -1898,32 +1859,31 @@ var RexAddRoleDialog = /** @class */ (function (_super) { message(''); return true; }); - _this.isDisplayNameValid = ko.computed(function () { - var name = _this.roleDisplayName(); - var message = _this.displayNameValidationMessage; + this.isDisplayNameValid = ko.computed(() => { + let name = this.roleDisplayName(); + let message = this.displayNameValidationMessage; return RexAddRoleDialog.validateDisplayName(name, message); }); //Automatically generate a role name from the display name. Basically, turn it into a slug. - var lastAutoRoleName = null; - _this.roleDisplayName.subscribe(function (displayName) { - var slug = _.snakeCase(displayName); + let lastAutoRoleName = null; + this.roleDisplayName.subscribe((displayName) => { + let slug = _.snakeCase(displayName); //Use the auto-generated role name only if the user hasn't entered their own. - var currentValue = _this.roleName(); + let currentValue = this.roleName(); if ((currentValue === '') || (currentValue === lastAutoRoleName)) { - _this.roleName(slug); + this.roleName(slug); } lastAutoRoleName = slug; }); - _this.isAddButtonEnabled = ko.pureComputed({ - read: function () { - return (_this.roleName() !== '') && (_this.roleDisplayName() !== '') - && _this.isNameValid() && _this.isDisplayNameValid(); + this.isAddButtonEnabled = ko.pureComputed({ + read: () => { + return (this.roleName() !== '') && (this.roleDisplayName() !== '') + && this.isNameValid() && this.isDisplayNameValid(); }, deferEvaluation: true }); - return _this; } - RexAddRoleDialog.validateDisplayName = function (name, validationMessage) { + static validateDisplayName(name, validationMessage) { name = name.trim(); if (name === '') { validationMessage(''); @@ -1937,8 +1897,8 @@ var RexAddRoleDialog = /** @class */ (function (_super) { } validationMessage(''); return true; - }; - RexAddRoleDialog.prototype.onOpen = function (event, ui) { + } + onOpen(event, ui) { //Clear dialog fields when it's opened. this.roleName(''); this.roleDisplayName(''); @@ -1948,49 +1908,47 @@ var RexAddRoleDialog = /** @class */ (function (_super) { this.setupValidationTooltip('#rex-new-role-name', this.nameValidationMessage); this.areTooltipsInitialised = true; } - }; - RexAddRoleDialog.prototype.onConfirm = function () { + } + onConfirm() { if (!this.isAddButtonEnabled()) { return; } this.isOpen(false); - var caps = {}; - if (this.roleToCopyFrom()) { - caps = this.roleToCopyFrom().getOwnCapabilities(); + let caps = {}; + const sourceRole = this.roleToCopyFrom(); + if (sourceRole) { + caps = sourceRole.getOwnCapabilities(); } this.editor.addRole(this.roleName(), this.roleDisplayName(), caps); - }; - RexAddRoleDialog.invalidDisplayNameRegex = /[><&\r\n\t]/; - return RexAddRoleDialog; -}(RexBaseDialog)); -var RexDeleteRoleDialog = /** @class */ (function (_super) { - __extends(RexDeleteRoleDialog, _super); - function RexDeleteRoleDialog(editor) { - var _this = _super.call(this) || this; - _this.isRoleSelected = {}; - _this.editor = editor; - _this.options.minWidth = 420; - _this.options.buttons.push({ + } +} +RexAddRoleDialog.invalidDisplayNameRegex = /[><&\r\n\t]/; +class RexDeleteRoleDialog extends RexBaseDialog { + constructor(editor) { + super(); + this.isRoleSelected = {}; + this.editor = editor; + this.options.minWidth = 420; + this.options.buttons.push({ text: 'Delete Role', 'class': 'button button-primary', - click: _this.onConfirm.bind(_this), + click: this.onConfirm.bind(this), disabled: true }); - _this.isDeleteButtonEnabled = ko.pureComputed({ - read: function () { - return _this.getSelectedRoles().length > 0; + this.isDeleteButtonEnabled = ko.pureComputed({ + read: () => { + return this.getSelectedRoles().length > 0; }, deferEvaluation: true }); - return _this; } - RexDeleteRoleDialog.prototype.onConfirm = function () { - var _ = wsAmeLodash; - var rolesToDelete = this.getSelectedRoles(); + onConfirm() { + const _ = wsAmeLodash; + let rolesToDelete = this.getSelectedRoles(); //Warn about the dangers of deleting built-in roles. - var selectedBuiltInRoles = _.filter(rolesToDelete, _.method('isBuiltIn')); + let selectedBuiltInRoles = _.filter(rolesToDelete, _.method('isBuiltIn')); if (selectedBuiltInRoles.length > 0) { - var warning = 'Caution: Deleting default roles like ' + _.first(selectedBuiltInRoles).displayName() + const warning = 'Caution: Deleting default roles like ' + _.first(selectedBuiltInRoles).displayName() + ' can prevent you from using certain plugins. This is because some plugins look for specific' + ' role names to determine if a user is allowed to access the plugin.' + '\nDelete ' + selectedBuiltInRoles.length + ' default role(s)?'; @@ -2000,145 +1958,139 @@ var RexDeleteRoleDialog = /** @class */ (function (_super) { } this.editor.deleteRoles(rolesToDelete); this.isOpen(false); - }; - RexDeleteRoleDialog.prototype.onOpen = function (event, ui) { + } + onOpen(event, ui) { //Deselect all previously selected roles. wsAmeLodash.forEach(this.isRoleSelected, function (isSelected) { isSelected(false); }); - }; - RexDeleteRoleDialog.prototype.getSelectionState = function (roleName) { + } + getSelectionState(roleName) { if (!this.isRoleSelected.hasOwnProperty(roleName)) { this.isRoleSelected[roleName] = ko.observable(false); } return this.isRoleSelected[roleName]; - }; - RexDeleteRoleDialog.prototype.getSelectedRoles = function () { - var _this = this; - var _ = wsAmeLodash; - var rolesToDelete = []; - _.forEach(this.editor.roles(), function (role) { - if (_this.getSelectionState(role.name())()) { + } + getSelectedRoles() { + const _ = wsAmeLodash; + let rolesToDelete = []; + _.forEach(this.editor.roles(), (role) => { + if (this.getSelectionState(role.name())()) { rolesToDelete.push(role); } }); return rolesToDelete; - }; - return RexDeleteRoleDialog; -}(RexBaseDialog)); -var RexRenameRoleDialog = /** @class */ (function (_super) { - __extends(RexRenameRoleDialog, _super); - function RexRenameRoleDialog(editor) { - var _this = _super.call(this) || this; - _this.selectedRole = ko.observable(null); - _this.newDisplayName = ko.observable(''); - _this.displayNameValidationMessage = ko.observable(''); - _this.isTooltipInitialised = false; - _this.editor = editor; - _this.options.minWidth = 380; - _this.options.buttons.push({ + } +} +class RexRenameRoleDialog extends RexBaseDialog { + constructor(editor) { + super(); + this.selectedRole = ko.observable(null); + this.newDisplayName = ko.observable(''); + this.displayNameValidationMessage = ko.observable(''); + this.isTooltipInitialised = false; + this.editor = editor; + this.options.minWidth = 380; + this.options.buttons.push({ text: 'Rename Role', 'class': 'button button-primary', - click: _this.onConfirm.bind(_this), + click: this.onConfirm.bind(this), disabled: true }); - _this.selectedRole.subscribe(function (role) { + this.selectedRole.subscribe((role) => { if (role) { - _this.newDisplayName(role.displayName()); + this.newDisplayName(role.displayName()); } }); - _this.isConfirmButtonEnabled = ko.computed({ - read: function () { - return RexAddRoleDialog.validateDisplayName(_this.newDisplayName(), _this.displayNameValidationMessage); + this.isConfirmButtonEnabled = ko.computed({ + read: () => { + return RexAddRoleDialog.validateDisplayName(this.newDisplayName(), this.displayNameValidationMessage); }, deferEvaluation: true }); - return _this; } - RexRenameRoleDialog.prototype.onOpen = function (event, ui) { - var _ = wsAmeLodash; + onOpen(event, ui) { + const _ = wsAmeLodash; if (!this.isTooltipInitialised) { this.setupValidationTooltip('#rex-edited-role-display-name', this.displayNameValidationMessage); this.isTooltipInitialised = true; } //Select either the currently selected role or the first available role. - var selectedActor = this.editor.selectedActor(); + const selectedActor = this.editor.selectedActor(); if (selectedActor && (selectedActor instanceof RexRole)) { this.selectedRole(selectedActor); } else { this.selectedRole(_.first(this.editor.roles())); } - }; - RexRenameRoleDialog.prototype.onConfirm = function () { + } + onConfirm() { if (!this.isConfirmButtonEnabled()) { return; } - if (this.selectedRole()) { - var name_1 = this.newDisplayName().trim(); - this.selectedRole().displayName(name_1); + const selectedRole = this.selectedRole(); + if (selectedRole) { + const name = this.newDisplayName().trim(); + selectedRole.displayName(name); this.editor.actorSelector.repopulate(); } this.isOpen(false); - }; - return RexRenameRoleDialog; -}(RexBaseDialog)); -var RexEagerObservableStringSet = /** @class */ (function () { - function RexEagerObservableStringSet() { + } +} +class RexEagerObservableStringSet { + constructor() { this.items = {}; } - RexEagerObservableStringSet.prototype.contains = function (item) { + contains(item) { if (!this.items.hasOwnProperty(item)) { this.items[item] = ko.observable(false); return false; } return this.items[item](); - }; - RexEagerObservableStringSet.prototype.add = function (item) { + } + add(item) { if (!this.items.hasOwnProperty(item)) { this.items[item] = ko.observable(true); } else { this.items[item](true); } - }; - RexEagerObservableStringSet.prototype.remove = function (item) { + } + remove(item) { if (this.items.hasOwnProperty(item)) { this.items[item](false); } - }; - RexEagerObservableStringSet.prototype.clear = function () { - var _ = wsAmeLodash; - _.forEach(this.items, function (isInSet) { + } + clear() { + const _ = wsAmeLodash; + _.forEach(this.items, (isInSet) => { isInSet(false); }); - }; - RexEagerObservableStringSet.prototype.getPresenceObservable = function (item) { + } + getPresenceObservable(item) { if (!this.items.hasOwnProperty(item)) { this.items[item] = ko.observable(false); } return this.items[item]; - }; - RexEagerObservableStringSet.prototype.getAsObject = function (fillValue) { - if (fillValue === void 0) { fillValue = true; } - var _ = wsAmeLodash; - var output = {}; - _.forEach(this.items, function (isInSet, item) { - if (isInSet()) { + } + getAsObject(fillValue) { + const _ = wsAmeLodash; + let output = {}; + _.forEach(this.items, (isInSet, item) => { + if (isInSet() && (typeof item !== 'undefined')) { output[item] = fillValue; } }); return output; - }; - return RexEagerObservableStringSet; -}()); -var RexObservableEditableRoleSettings = /** @class */ (function () { - function RexObservableEditableRoleSettings() { + } +} +class RexObservableEditableRoleSettings { + constructor() { this.strategy = ko.observable('auto'); this.userDefinedList = new RexEagerObservableStringSet(); } - RexObservableEditableRoleSettings.prototype.toPlainObject = function () { - var roleList = this.userDefinedList.getAsObject(true); + toPlainObject() { + let roleList = this.userDefinedList.getAsObject(true); if (wsAmeLodash.isEmpty(roleList)) { roleList = null; } @@ -2146,34 +2098,32 @@ var RexObservableEditableRoleSettings = /** @class */ (function () { strategy: this.strategy(), userDefinedList: roleList }; - }; - return RexObservableEditableRoleSettings; -}()); -var RexUserRoleModule = /** @class */ (function () { - function RexUserRoleModule(selectedActor, roles) { - var _this = this; + } +} +class RexUserRoleModule { + constructor(selectedActor, roles) { this.roleObservables = {}; this.selectedActor = selectedActor; - this.sortedRoles = ko.computed(function () { + this.sortedRoles = ko.computed(() => { return roles(); }); this.primaryRole = ko.computed({ - read: function () { - var actor = selectedActor(); + read: () => { + const actor = selectedActor(); if ((actor === null) || !actor.canHaveRoles) { return null; } if (actor instanceof RexUser) { - var roles_1 = actor.roles(); - if (roles_1.length < 1) { + const roles = actor.roles(); + if (roles.length < 1) { return null; } - return roles_1[0]; + return roles[0]; } return null; }, - write: function (newRole) { - var actor = selectedActor(); + write: (newRole) => { + const actor = selectedActor(); if ((actor === null) || !actor.canHaveRoles || !(actor instanceof RexUser)) { return; } @@ -2186,11 +2136,11 @@ var RexUserRoleModule = /** @class */ (function () { if (!(newRole instanceof RexRole)) { return; } - if (!_this.canAssignRoleToActor(newRole)) { + if (!this.canAssignRoleToActor(newRole)) { return; } //Remove the previous primary role. - var oldPrimaryRole = (actor.roles().length > 0) ? actor.roles()[0] : null; + const oldPrimaryRole = (actor.roles().length > 0) ? actor.roles()[0] : null; if (oldPrimaryRole !== null) { actor.roles.remove(oldPrimaryRole); } @@ -2202,21 +2152,20 @@ var RexUserRoleModule = /** @class */ (function () { actor.roles.unshift(newRole); } }); - this.isVisible = ko.pureComputed(function () { - var actor = _this.selectedActor(); + this.isVisible = ko.pureComputed(() => { + const actor = this.selectedActor(); return (actor !== null) && actor.canHaveRoles; }); } // noinspection JSUnusedGlobalSymbols Used in Knockout templates. - RexUserRoleModule.prototype.actorHasRole = function (role) { - var _this = this; - var roleActorId = role.getId(); + actorHasRole(role) { + const roleActorId = role.getId(); if (this.roleObservables.hasOwnProperty(roleActorId) && (this.roleObservables[roleActorId].role === role)) { return this.roleObservables[roleActorId].selectedActorHasRole; } - var selectedActorHasRole = ko.computed({ - read: function () { - var actor = _this.selectedActor(); + let selectedActorHasRole = ko.computed({ + read: () => { + const actor = this.selectedActor(); if ((actor === null) || !actor.canHaveRoles) { return false; } @@ -2225,15 +2174,15 @@ var RexUserRoleModule = /** @class */ (function () { } return false; }, - write: function (shouldHaveRole) { - var actor = _this.selectedActor(); + write: (shouldHaveRole) => { + const actor = this.selectedActor(); if ((actor === null) || !actor.canHaveRoles || !(actor instanceof RexUser)) { return; } - if (!_this.canAssignRoleToActor(role)) { + if (!this.canAssignRoleToActor(role)) { return; } - var alreadyHasRole = (actor.roles.indexOf(role) !== -1); + const alreadyHasRole = (actor.roles.indexOf(role) !== -1); if (shouldHaveRole !== alreadyHasRole) { if (shouldHaveRole) { actor.roles.push(role); @@ -2249,105 +2198,111 @@ var RexUserRoleModule = /** @class */ (function () { selectedActorHasRole: selectedActorHasRole }; return selectedActorHasRole; - }; - RexUserRoleModule.prototype.canAssignRoleToActor = function (role) { + } + canAssignRoleToActor(role) { //This is a stub. The role editor currently doesn't check editable role settings at edit time. - var actor = this.selectedActor(); + const actor = this.selectedActor(); if ((actor === null) || !actor.canHaveRoles) { return false; } return (role instanceof RexRole); - }; - return RexUserRoleModule; -}()); -var RexEditableRolesDialog = /** @class */ (function (_super) { - __extends(RexEditableRolesDialog, _super); - function RexEditableRolesDialog(editor) { - var _this = _super.call(this) || this; - _this.selectedActor = ko.observable(null); - _this.actorSettings = {}; - _this.editor = editor; - _this.visibleActors = ko.observableArray([]); - _this.options.minWidth = 600; - _this.options.buttons.push({ + } +} +class RexEditableRolesDialog extends RexBaseDialog { + constructor(editor) { + super(); + this.selectedActor = ko.observable(null); + this.actorSettings = {}; + this.editor = editor; + this.visibleActors = ko.observableArray([]); + this.options.minWidth = 600; + this.options.buttons.push({ text: 'Save Changes', 'class': 'button button-primary', - click: _this.onConfirm.bind(_this), + click: this.onConfirm.bind(this), disabled: false }); //Super Admin is always set to "leave unchanged" because //they can edit all roles. - var superAdmin = editor.getSuperAdmin(); - var superAdminSettings = new RexObservableEditableRoleSettings(); + const superAdmin = editor.getSuperAdmin(); + const superAdminSettings = new RexObservableEditableRoleSettings(); superAdminSettings.strategy('none'); - var dummySettings = new RexObservableEditableRoleSettings(); - _this.selectedActorSettings = ko.computed(function () { - if (_this.selectedActor() === null) { + const dummySettings = new RexObservableEditableRoleSettings(); + this.selectedActorSettings = ko.computed(() => { + const selectedActor = this.selectedActor(); + if (selectedActor === null) { return dummySettings; } - if (_this.selectedActor() === superAdmin) { + if (selectedActor === superAdmin) { return superAdminSettings; } - var actorId = _this.selectedActor().getId(); - if (!_this.actorSettings.hasOwnProperty(actorId)) { - //This should never happen; the dictionary should be initialised when opening the dialog. - _this.actorSettings[actorId] = new RexObservableEditableRoleSettings(); + const actorId = selectedActor.getId(); + if (!this.actorSettings.hasOwnProperty(actorId)) { + //This should never happen; the dictionary should be initialized when opening the dialog. + this.actorSettings[actorId] = new RexObservableEditableRoleSettings(); } - return _this.actorSettings[actorId]; + return this.actorSettings[actorId]; }); - _this.editableRoleStrategy = ko.computed({ - read: function () { - return _this.selectedActorSettings().strategy(); + this.editableRoleStrategy = ko.computed({ + read: () => { + return this.selectedActorSettings().strategy(); }, - write: function (newValue) { - _this.selectedActorSettings().strategy(newValue); + write: (newValue) => { + this.selectedActorSettings().strategy(newValue); } }); - _this.isAutoStrategyAllowed = ko.computed(function () { - var actor = _this.selectedActor(); + this.isAutoStrategyAllowed = ko.computed(() => { + const actor = this.selectedActor(); if (actor == null) { return true; } return !((actor === superAdmin) || ((actor instanceof RexUser) && actor.isSuperAdmin)); }); - _this.isListStrategyAllowed = _this.isAutoStrategyAllowed; - return _this; + this.isListStrategyAllowed = this.isAutoStrategyAllowed; } - RexEditableRolesDialog.prototype.onOpen = function (event, ui) { - var _this = this; - var _ = wsAmeLodash; + onOpen(event, ui) { + const _ = wsAmeLodash; //Copy editable role settings into observables. - _.forEach(this.editor.actorEditableRoles, function (settings, actorId) { - if (!_this.actorSettings.hasOwnProperty(actorId)) { - _this.actorSettings[actorId] = new RexObservableEditableRoleSettings(); + _.forEach(this.editor.actorEditableRoles, (settings, actorId) => { + if (typeof actorId !== 'string') { + return; } - var observableSettings = _this.actorSettings[actorId]; + if (!this.actorSettings.hasOwnProperty(actorId)) { + this.actorSettings[actorId] = new RexObservableEditableRoleSettings(); + } + const observableSettings = this.actorSettings[actorId]; observableSettings.strategy(settings.strategy); observableSettings.userDefinedList.clear(); if (settings.userDefinedList !== null) { - _.forEach(settings.userDefinedList, function (ignored, roleId) { + _.forEach(settings.userDefinedList, (ignored, roleId) => { + if (typeof roleId !== 'string') { + return; + } observableSettings.userDefinedList.add(roleId); }); } }); this.visibleActors(this.editor.actorSelector.getVisibleActors()); //Select either the currently selected actor or the first role. - var selectedActor = this.editor.selectedActor(); + const selectedActor = this.editor.selectedActor(); if (selectedActor) { this.selectedActor(selectedActor); } else { this.selectedActor(_.first(this.editor.roles())); } - }; - RexEditableRolesDialog.prototype.onConfirm = function () { + } + onConfirm() { //Save editable roles - var _ = wsAmeLodash; - var settings = this.editor.actorEditableRoles; - _.forEach(this.actorSettings, function (observableSettings, actorId) { + const _ = wsAmeLodash; + let settings = this.editor.actorEditableRoles; + _.forEach(this.actorSettings, (observableSettings, actorId) => { + if (typeof actorId === 'undefined') { + throw new Error('Actor ID is undefined. This should never happen.'); + } if (observableSettings.strategy() === 'auto') { - //"auto" is the default so we don't need to store anything. + //"auto" is the default, so we don't need to store anything. delete settings[actorId]; } else { @@ -2355,24 +2310,22 @@ var RexEditableRolesDialog = /** @class */ (function (_super) { } }); this.isOpen(false); - }; - RexEditableRolesDialog.prototype.isRoleSetToEditable = function (role) { + } + isRoleSetToEditable(role) { return this.selectedActorSettings().userDefinedList.getPresenceObservable(role.name()); - }; - RexEditableRolesDialog.prototype.isRoleEnabled = function (role) { + } + isRoleEnabled(role) { return this.editableRoleStrategy() === 'user-defined-list'; - }; - RexEditableRolesDialog.prototype.selectItem = function (actor) { + } + selectItem(actor) { this.selectedActor(actor); - }; - RexEditableRolesDialog.prototype.getItemText = function (actor) { + } + getItemText(actor) { return this.editor.actorSelector.getNiceName(actor); - }; - return RexEditableRolesDialog; -}(RexBaseDialog)); -var RexRoleEditor = /** @class */ (function () { - function RexRoleEditor(data) { - var _this = this; + } +} +class RexRoleEditor { + constructor(data) { // noinspection JSUnusedGlobalSymbols this.categoryViewOptions = [ RexRoleEditor.hierarchyView, @@ -2383,14 +2336,14 @@ var RexRoleEditor = /** @class */ (function () { this.userDefinedCapabilities = {}; this.categoriesBySlug = {}; this.actorLookup = {}; - var self = this; - var _ = wsAmeLodash; + const self = this; + const _ = wsAmeLodash; this.areBindingsApplied = ko.observable(false); - this.isLoaded = ko.computed(function () { - return _this.areBindingsApplied(); + this.isLoaded = ko.computed(() => { + return this.areBindingsApplied(); }); this.userPreferences = new RexUserPreferences(data.userPreferences, data.adminAjaxUrl, data.updatePreferencesNonce); - var preferences = this.userPreferences; + const preferences = this.userPreferences; this.showDeprecatedEnabled = preferences.getObservable('showDeprecatedEnabled', true); this.showRedundantEnabled = preferences.getObservable('showRedundantEnabled', false); this.showBaseCapsEnabled = ko.computed(this.showRedundantEnabled); @@ -2403,8 +2356,8 @@ var RexRoleEditor = /** @class */ (function () { this.showZerosEnabled = preferences.getObservable('showZerosEnabled', false); this.inheritanceOverrideEnabled = preferences.getObservable('inheritanceOverrideEnabled', false); //Remember and restore the selected view mode. - var viewModeId = preferences.getObservable('categoryVewMode', 'hierarchy'); - var initialViewMode = _.find(this.categoryViewOptions, 'id', viewModeId()); + let viewModeId = preferences.getObservable('categoryVewMode', 'hierarchy'); + let initialViewMode = _.find(this.categoryViewOptions, 'id', viewModeId()); if (!initialViewMode) { initialViewMode = RexRoleEditor.hierarchyView; } @@ -2414,16 +2367,16 @@ var RexRoleEditor = /** @class */ (function () { }); this.isShiftKeyDown = ko.observable(false); this.capabilityViewClasses = ko.pureComputed({ - read: function () { - var viewMode = _this.categoryViewMode(); - var classes = ['rex-category-view-mode-' + viewMode.id]; + read: () => { + const viewMode = this.categoryViewMode(); + let classes = ['rex-category-view-mode-' + viewMode.id]; if (viewMode === RexRoleEditor.singleCategoryView) { classes.push('rex-show-category-subheadings'); } - if (_this.readableNamesEnabled()) { + if (this.readableNamesEnabled()) { classes.push('rex-readable-names-enabled'); } - if (_this.categoryWidthMode() === 'full') { + if (this.categoryWidthMode() === 'full') { classes.push('rex-full-width-categories'); } return classes.join(' '); @@ -2432,38 +2385,41 @@ var RexRoleEditor = /** @class */ (function () { }); this.searchQuery = ko.observable('').extend({ rateLimit: { timeout: 100, method: "notifyWhenChangesStop" } }); this.searchKeywords = ko.computed(function () { - var query = self.searchQuery().trim(); + let query = self.searchQuery().trim(); if (query === '') { return []; } return wsAmeLodash(query.split(' ')) - .map(function (keyword) { + .map((keyword) => { return keyword.trim(); }) - .filter(function (keyword) { + .filter((keyword) => { return (keyword !== ''); }) .value(); }); - this.components = _.mapValues(data.knownComponents, function (details, id) { + this.components = _.mapValues(data.knownComponents, (details, id) => { + if (typeof id === 'undefined') { + throw new Error('Undefined component ID. This should never happen.'); + } return RexWordPressComponent.fromJs(id, details); }); this.coreComponent = new RexWordPressComponent(':wordpress:', 'WordPress core'); this.components[':wordpress:'] = this.coreComponent; //Populate roles and users. - var tempRoleList = []; - _.forEach(data.roles, function (roleData) { - var role = new RexRole(roleData.name, roleData.displayName, roleData.capabilities); + const tempRoleList = []; + _.forEach(data.roles, (roleData) => { + const role = new RexRole(roleData.name, roleData.displayName, roleData.capabilities); role.hasUsers = roleData.hasUsers; tempRoleList.push(role); - _this.actorLookup[role.id()] = role; + this.actorLookup[role.id()] = role; }); this.roles = ko.observableArray(tempRoleList); - var tempUserList = []; - _.forEach(AmeActors.getUsers(), function (data) { - var user = RexUser.fromAmeUser(data, self); + const tempUserList = []; + _.forEach(AmeActors.getUsers(), (data) => { + const user = RexUser.fromAmeUser(data, self); tempUserList.push(user); - _this.actorLookup[user.id()] = user; + this.actorLookup[user.id()] = user; }); this.users = ko.observableArray(tempUserList); this.dummyActor = new RexRole('rex-invalid-role', 'Invalid Role'); @@ -2473,24 +2429,30 @@ var RexRoleEditor = /** @class */ (function () { })); this.actorSelector = new AmeActorSelector(this, true, false); //Wrap the selected actor in a computed observable so that it can be used with Knockout. - var _selectedActor = ko.observable(this.getActor(this.actorSelector.selectedActor)); + let _selectedActor = ko.observable((this.actorSelector.selectedActor === null) + ? this.dummyActor + : this.getActor(this.actorSelector.selectedActor)); this.selectedActor = ko.computed({ read: function () { return _selectedActor(); }, - write: function (newActor) { - _this.actorSelector.setSelectedActor(newActor.id()); + write: (newActor) => { + this.actorSelector.setSelectedActor(newActor.id()); } }); - this.actorSelector.onChange(function (newSelectedActor) { - _selectedActor(_this.getActor(newSelectedActor)); + this.actorSelector.onChange((newSelectedActor) => { + if (newSelectedActor === null) { + _selectedActor(this.dummyActor); //This should never happen in practice. + return; + } + _selectedActor(this.getActor(newSelectedActor)); }); //Refresh the actor selector when roles are added or removed. - this.roles.subscribe(function () { - _this.actorSelector.repopulate(); + this.roles.subscribe(() => { + this.actorSelector.repopulate(); }); //Re-select the previously selected actor if possible. - var initialActor = null; + let initialActor = null; if (data.selectedActor) { initialActor = this.getActor(data.selectedActor); } @@ -2502,12 +2464,15 @@ var RexRoleEditor = /** @class */ (function () { this.deprecatedCapabilities = data.deprecatedCapabilities; this.metaCapabilityMap = data.metaCapMap; this.userDefinedCapabilities = data.userDefinedCapabilities; - this.capabilities = _.mapValues(data.capabilities, function (metadata, name) { + this.capabilities = _.mapValues(data.capabilities, (metadata, name) => { + if (typeof name === 'undefined') { + throw new Error('Undefined capability name. This should never happen.'); + } return RexCapability.fromJs(name, metadata, self); }); //Add the special "do_not_allow" capability. Normally, it's impossible to assign it to anyone, //but it can still be used in post type permissions and other places. - var doNotAllow = new RexDoNotAllowCapability(this); + const doNotAllow = new RexDoNotAllowCapability(this); doNotAllow.originComponent = this.components[':wordpress:']; this.capabilities['do_not_allow'] = doNotAllow; //Similarly, "exist" is always enabled for all roles and users. Everyone can exist. @@ -2518,19 +2483,22 @@ var RexRoleEditor = /** @class */ (function () { //Store editable roles. this.actorEditableRoles = (!_.isEmpty(data.editableRoles)) ? data.editableRoles : {}; this.rootCategory = new RexCategory('All', this); - var coreCategory = RexCategory.fromJs(data.coreCategory, this); + const coreCategory = RexCategory.fromJs(data.coreCategory, this); this.rootCategory.addSubcategory(coreCategory); - var postTypeCategory = new RexPostTypeContainerCategory('Post Types', this, 'postTypes'); + const postTypeCategory = new RexPostTypeContainerCategory('Post Types', this, 'postTypes'); this.postTypes = _.indexBy(data.postTypes, 'name'); - _.forEach(this.postTypes, function (details, id) { - var category = new RexPostTypeCategory(details.label, self, id, 'postTypes/' + id, details.permissions, details.isDefault); + _.forEach(this.postTypes, (details, id) => { + if (typeof id === 'undefined') { + throw new Error('Undefined post type ID. This should never happen.'); + } + const category = new RexPostTypeCategory(details.label, self, id, 'postTypes/' + id, details.permissions, details.isDefault); if (details.componentId) { - category.origin = _this.getComponent(details.componentId); + category.origin = this.getComponent(details.componentId); } postTypeCategory.addSubcategory(category); //Record the post type actions associated with each capability. - for (var action in details.permissions) { - var capability = self.getCapability(details.permissions[action]); + for (let action in details.permissions) { + const capability = self.getCapability(details.permissions[action]); _.set(capability.usedByPostTypeActions, [details.name, action], true); } }); @@ -2539,25 +2507,28 @@ var RexRoleEditor = /** @class */ (function () { this.rootCategory.addSubcategory(postTypeCategory); //Taxonomies. this.taxonomies = data.taxonomies; - var taxonomyCategory = new RexTaxonomyContainerCategory('Taxonomies', this, 'taxonomies'); - _.forEach(data.taxonomies, function (details, id) { - var category = new RexTaxonomyCategory(details.label, self, id, 'taxonomies/' + id, details.permissions); + const taxonomyCategory = new RexTaxonomyContainerCategory('Taxonomies', this, 'taxonomies'); + _.forEach(data.taxonomies, (details, id) => { + if (typeof id === 'undefined') { + throw new Error('Undefined taxonomy ID. This should never happen.'); + } + const category = new RexTaxonomyCategory(details.label, self, id, 'taxonomies/' + id, details.permissions); taxonomyCategory.addSubcategory(category); //Record taxonomy type actions associated with each capability. - for (var action in details.permissions) { - var capability = self.getCapability(details.permissions[action]); + for (let action in details.permissions) { + const capability = self.getCapability(details.permissions[action]); _.set(capability.usedByTaxonomyActions, [details.name, action], true); } }); - taxonomyCategory.subcategories.sort(function (a, b) { + taxonomyCategory.subcategories.sort((a, b) => { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); }); this.rootCategory.addSubcategory(taxonomyCategory); - var customParentCategory = new RexCategory('Plugins', this, 'custom'); + const customParentCategory = new RexCategory('Plugins', this, 'custom'); function initCustomCategory(details, parent) { - var category = RexCategory.fromJs(details, self); + let category = RexCategory.fromJs(details, self); //Sort subcategories by title. - category.subcategories.sort(function (a, b) { + category.subcategories.sort((a, b) => { //Keep the "General" category at the top if there is one. if (a.name === b.name) { return 0; @@ -2572,23 +2543,23 @@ var RexRoleEditor = /** @class */ (function () { }); parent.addSubcategory(category); } - _.forEach(data.customCategories, function (details) { + _.forEach(data.customCategories, (details) => { initCustomCategory(details, customParentCategory); }); - customParentCategory.subcategories.sort(function (a, b) { + customParentCategory.subcategories.sort((a, b) => { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); }); this.rootCategory.addSubcategory(customParentCategory); //Make a category for uncategorized capabilities. This one is always at the bottom. - var uncategorizedCategory = new RexCategory('Uncategorized', self, 'custom/uncategorized', data.uncategorizedCapabilities); + const uncategorizedCategory = new RexCategory('Uncategorized', self, 'custom/uncategorized', data.uncategorizedCapabilities); customParentCategory.addSubcategory(uncategorizedCategory); - var _selectedCategory = ko.observable(null); + let _selectedCategory = ko.observable(null); this.selectedCategory = ko.computed({ read: function () { return _selectedCategory(); }, write: function (newSelection) { - var oldSelection = _selectedCategory(); + const oldSelection = _selectedCategory(); if (newSelection === oldSelection) { return; } @@ -2604,11 +2575,11 @@ var RexRoleEditor = /** @class */ (function () { this.selectedCategory(this.rootCategory); this.permissionTipSubject = ko.observable(null); this.allCapabilitiesAsPermissions = ko.pureComputed({ - read: function () { + read: () => { //Create a permission for each unique, non-deleted capability. //Exclude special caps like do_not_allow and exist because they can't be enabled. - var excludedCaps = ['do_not_allow', 'exist']; - return _.chain(_this.capabilities) + const excludedCaps = ['do_not_allow', 'exist']; + const result = _.chain(this.capabilities) .map(function (capability) { if (excludedCaps.indexOf(capability.name) >= 0) { return null; @@ -2619,30 +2590,32 @@ var RexRoleEditor = /** @class */ (function () { return value !== null; }) .value(); + //TypeScript doesn't know that the filter above eliminates nulls. + return result; }, deferEvaluation: true }); this.capsInSelectedCategory = ko.pureComputed({ - read: function () { - var category = _this.selectedCategory(); + read: () => { + const category = this.selectedCategory(); if (!category) { return {}; } - var caps = {}; + let caps = {}; category.countUniqueCapabilities(caps); return caps; }, deferEvaluation: true }); this.leafCategories = ko.computed({ - read: function () { + read: () => { //So what we want here is a depth-first traversal of the category tree. - var results = []; - var addedUniqueCategories = {}; + let results = []; + let addedUniqueCategories = {}; function traverse(category) { if (category.subcategories.length < 1) { //Eliminate duplicates, like CPTs that show up in the post type category and a plugin category. - var key = category.getDeDuplicationKey(); + let key = category.getDeDuplicationKey(); if (!addedUniqueCategories.hasOwnProperty(key)) { results.push(category); addedUniqueCategories[key] = category; @@ -2652,11 +2625,11 @@ var RexRoleEditor = /** @class */ (function () { } return; } - for (var i = 0; i < category.subcategories.length; i++) { + for (let i = 0; i < category.subcategories.length; i++) { traverse(category.subcategories[i]); } } - traverse(_this.rootCategory); + traverse(this.rootCategory); results.sort(function (a, b) { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); }); @@ -2664,7 +2637,7 @@ var RexRoleEditor = /** @class */ (function () { }, deferEvaluation: true }); - var compareRoleDisplayNames = function (a, b) { + const compareRoleDisplayNames = function (a, b) { return a.displayName().toLowerCase().localeCompare(b.displayName().toLowerCase()); }; this.defaultRoles = ko.pureComputed({ @@ -2692,46 +2665,45 @@ var RexRoleEditor = /** @class */ (function () { this.isSaving = ko.observable(false); this.isGlobalSettingsUpdate = ko.observable(false); } - RexRoleEditor.prototype.capabilityMatchesFilters = function (capability) { + capabilityMatchesFilters(capability) { if (!this.showDeprecatedEnabled() && this.isDeprecated(capability.name)) { return false; } if (this.showOnlyCheckedEnabled() && !capability.isEnabledForSelectedActor()) { return false; } - var keywords = this.searchKeywords(), capabilityName = capability.name; + const keywords = this.searchKeywords(), capabilityName = capability.name; if (keywords.length > 0) { - var haystack_1 = capabilityName.toLowerCase(); - var matchesKeywords = wsAmeLodash.all(keywords, function (keyword) { - return haystack_1.indexOf(keyword) >= 0; + const haystack = capabilityName.toLowerCase(); + const matchesKeywords = wsAmeLodash.all(keywords, function (keyword) { + return haystack.indexOf(keyword) >= 0; }); if (!matchesKeywords) { return false; } } return true; - }; - RexRoleEditor.prototype.isDeprecated = function (capability) { + } + isDeprecated(capability) { return this.deprecatedCapabilities.hasOwnProperty(capability); - }; - RexRoleEditor.prototype.getComponent = function (componentId) { + } + getComponent(componentId) { if (this.components.hasOwnProperty(componentId)) { return this.components[componentId]; } return null; - }; + } /** * Get or create a capability instance. */ - RexRoleEditor.prototype.getCapability = function (capabilityName, recursionDepth) { - if (recursionDepth === void 0) { recursionDepth = 0; } + getCapability(capabilityName, recursionDepth = 0) { //Un-map meta capabilities where possible. if (this.metaCapabilityMap.hasOwnProperty(capabilityName) && (recursionDepth < 10)) { return this.getCapability(this.metaCapabilityMap[capabilityName], recursionDepth + 1); } if (!this.capabilities.hasOwnProperty(capabilityName)) { - var _1 = wsAmeLodash; - if (!_1.isString(capabilityName) && !_1.isFinite(capabilityName)) { + const _ = wsAmeLodash; + if (!_.isString(capabilityName) && !_.isFinite(capabilityName)) { return this.getInvalidCapability(capabilityName); } if (console && console.info) { @@ -2741,9 +2713,9 @@ var RexRoleEditor = /** @class */ (function () { this.capabilities[capabilityName] = new RexCapability(capabilityName, this); } return this.capabilities[capabilityName]; - }; - RexRoleEditor.prototype.getInvalidCapability = function (invalidName) { - var capabilityName = '[Invalid capability: ' + String(invalidName) + ']'; + } + getInvalidCapability(invalidName) { + const capabilityName = '[Invalid capability: ' + String(invalidName) + ']'; if (!this.capabilities.hasOwnProperty(capabilityName)) { if (console && console.error) { console.error('Invalid capability detected - expected a string but got this: ', invalidName); @@ -2751,99 +2723,98 @@ var RexRoleEditor = /** @class */ (function () { this.capabilities[capabilityName] = new RexInvalidCapability(capabilityName, invalidName, this); } return this.capabilities[capabilityName]; - }; - RexRoleEditor.prototype.getActor = function (actorId) { + } + getActor(actorId) { if (this.actorLookup.hasOwnProperty(actorId)) { return this.actorLookup[actorId]; } return this.dummyActor; - }; - RexRoleEditor.prototype.getRole = function (name) { - var actorId = 'role:' + name; + } + getRole(name) { + const actorId = 'role:' + name; if (this.actorLookup.hasOwnProperty(actorId)) { - var role = this.actorLookup[actorId]; + const role = this.actorLookup[actorId]; if (role instanceof RexRole) { return role; } } return null; - }; + } // noinspection JSUnusedGlobalSymbols Testing method used in KO templates. - RexRoleEditor.prototype.setSubjectPermission = function (permission) { + setSubjectPermission(permission) { this.permissionTipSubject(permission); - }; + } /** * Search a string for the current search keywords and add the "rex-search-highlight" CSS class to each match. * * @param inputString */ - RexRoleEditor.prototype.highlightSearchKeywords = function (inputString) { - var _ = wsAmeLodash; - var keywordList = this.searchKeywords(); + highlightSearchKeywords(inputString) { + const _ = wsAmeLodash; + const keywordList = this.searchKeywords(); if (keywordList.length === 0) { return inputString; } - var keywordGroup = _.map(keywordList, _.escapeRegExp).join('|'); - var regex = new RegExp('((?:' + keywordGroup + ')(?:\\s*))+', 'gi'); + let keywordGroup = _.map(keywordList, _.escapeRegExp).join('|'); + let regex = new RegExp('((?:' + keywordGroup + ')(?:\\s*))+', 'gi'); return inputString.replace(regex, function (foundKeywords) { //Don't highlight the trailing space after the keyword(s). - var trailingSpace = ''; - var parts = foundKeywords.match(/^(.+?)(\s+)$/); + let trailingSpace = ''; + let parts = foundKeywords.match(/^(.+?)(\s+)$/); if (parts) { foundKeywords = parts[1]; trailingSpace = parts[2]; } return '' + foundKeywords + '' + trailingSpace; }); - }; - RexRoleEditor.prototype.actorExists = function (actorId) { + } + actorExists(actorId) { return this.actorLookup.hasOwnProperty(actorId); - }; - RexRoleEditor.prototype.addUsers = function (newUsers) { - var _this = this; - wsAmeLodash.forEach(newUsers, function (user) { + } + addUsers(newUsers) { + wsAmeLodash.forEach(newUsers, (user) => { if (!(user instanceof RexUser)) { if (console.error) { console.error('Cannot add a user. Expected an instance of RexUser, got this:', user); } return; } - if (!_this.actorLookup.hasOwnProperty(user.getId())) { - _this.users.push(user); - _this.actorLookup[user.getId()] = user; + if (!this.actorLookup.hasOwnProperty(user.getId())) { + this.users.push(user); + this.actorLookup[user.getId()] = user; } }); - }; - RexRoleEditor.prototype.createUserFromProperties = function (properties) { + } + createUserFromProperties(properties) { return RexUser.fromAmeUserProperties(properties, this); - }; - RexRoleEditor.prototype.getRoles = function () { + } + getRoles() { return wsAmeLodash.indexBy(this.roles(), function (role) { return role.name(); }); - }; - RexRoleEditor.prototype.getSuperAdmin = function () { + } + getSuperAdmin() { return RexSuperAdmin.getInstance(); - }; - RexRoleEditor.prototype.getUser = function (login) { - var actorId = 'user:' + login; + } + getUser(login) { + const actorId = 'user:' + login; if (this.actorLookup.hasOwnProperty(actorId)) { - var user = this.actorLookup[actorId]; + const user = this.actorLookup[actorId]; if (user instanceof RexUser) { return user; } } return null; - }; - RexRoleEditor.prototype.getUsers = function () { + } + getUsers() { return wsAmeLodash.indexBy(this.users(), 'userLogin'); - }; - RexRoleEditor.prototype.isInSelectedCategory = function (capabilityName) { - var caps = this.capsInSelectedCategory(); + } + isInSelectedCategory(capabilityName) { + let caps = this.capsInSelectedCategory(); return caps.hasOwnProperty(capabilityName); - }; - RexRoleEditor.prototype.addCapability = function (capabilityName) { - var capability; + } + addCapability(capabilityName) { + let capability; if (this.capabilities.hasOwnProperty(capabilityName)) { capability = this.capabilities[capabilityName]; if (!capability.isDeleted()) { @@ -2858,17 +2829,17 @@ var RexRoleEditor = /** @class */ (function () { capability.notes = 'This capability has not been saved yet. Click the "Save Changes" button to save it.'; this.capabilities[capabilityName] = capability; //Add the new capability to the "Other" or "Uncategorized" category. - var category = this.categoriesBySlug['custom/uncategorized']; - var permission = new RexPermission(this, capability); + const category = this.categoriesBySlug['custom/uncategorized']; + const permission = new RexPermission(this, capability); category.permissions.push(permission); category.sortPermissions(); this.userDefinedCapabilities[capabilityName] = true; return category; } - }; - RexRoleEditor.prototype.deleteCapabilities = function (selectedCapabilities) { - var self = this, _ = wsAmeLodash; - var targetActors = _.union(this.roles(), this.users()); + } + deleteCapabilities(selectedCapabilities) { + const self = this, _ = wsAmeLodash; + const targetActors = _.union(this.roles(), this.users()); _.forEach(selectedCapabilities, function (capability) { //Remove it from all roles and visible users. _.forEach(targetActors, function (actor) { @@ -2877,54 +2848,52 @@ var RexRoleEditor = /** @class */ (function () { capability.isDeleted(true); delete self.userDefinedCapabilities[capability.name]; }); - }; - RexRoleEditor.prototype.capabilityExists = function (capabilityName) { + } + capabilityExists(capabilityName) { return this.capabilities.hasOwnProperty(capabilityName) && !this.capabilities[capabilityName].isDeleted(); - }; - RexRoleEditor.prototype.addRole = function (name, displayName, capabilities) { - if (capabilities === void 0) { capabilities = {}; } - var role = new RexRole(name, displayName, capabilities); + } + addRole(name, displayName, capabilities = {}) { + let role = new RexRole(name, displayName, capabilities); this.actorLookup[role.id()] = role; this.roles.push(role); //Select the new role. this.selectedActor(role); return role; - }; - RexRoleEditor.prototype.deleteRoles = function (roles) { - var _this = this; - var _ = wsAmeLodash; - _.forEach(roles, function (role) { - if (!_this.canDeleteRole(role)) { + } + deleteRoles(roles) { + const _ = wsAmeLodash; + _.forEach(roles, (role) => { + if (!this.canDeleteRole(role)) { throw 'Cannot delete role "' + role.name() + '"'; } }); this.roles.removeAll(roles); this.trashedRoles.push.apply(this.trashedRoles, roles); //TODO: Later, add an option to restore deleted roles. - }; - RexRoleEditor.prototype.canDeleteRole = function (role) { + } + canDeleteRole(role) { //Was the role already assigned to any users when the editor was opened? if (role.hasUsers) { return false; } //We also need to take into account any unsaved user role changes. //Is the role assigned to any of the users currently loaded in the editor? - var _ = wsAmeLodash; + const _ = wsAmeLodash; if (_.some(this.users(), function (user) { return (user.roles.indexOf(role) !== -1); })) { return false; } return !this.isDefaultRoleForNewUsers(role); - }; - RexRoleEditor.prototype.isDefaultRoleForNewUsers = function (role) { + } + isDefaultRoleForNewUsers(role) { return (role.name() === this.defaultNewUserRoleName); - }; + } // noinspection JSUnusedGlobalSymbols Used in KO templates. - RexRoleEditor.prototype.saveChanges = function () { + saveChanges() { this.isSaving(true); - var _ = wsAmeLodash; - var data = { + const _ = wsAmeLodash; + let data = { 'roles': _.invoke(this.roles(), 'toJs'), 'users': _.invoke(this.users(), 'toJs'), 'trashedRoles': _.invoke(this.trashedRoles(), 'toJs'), @@ -2933,32 +2902,34 @@ var RexRoleEditor = /** @class */ (function () { }; this.settingsFieldData(ko.toJSON(data)); jQuery('#rex-save-settings-form').submit(); - }; - RexRoleEditor.prototype.updateAllSites = function () { + } + updateAllSites() { if (!confirm('Apply these role settings to ALL sites? Any changes that you\'ve made to individual sites will be lost.')) { return false; } this.isGlobalSettingsUpdate(true); this.saveChanges(); - }; - RexRoleEditor.hierarchyView = { - label: 'Hierarchy view', - id: 'hierarchy', - templateName: 'rex-hierarchy-view-template' - }; - RexRoleEditor.singleCategoryView = { - label: 'Category view', - id: 'category', - templateName: 'rex-single-category-view-template' - }; - RexRoleEditor.listView = { label: 'List view', id: 'list', templateName: 'rex-list-view-template' }; - return RexRoleEditor; -}()); + } +} +RexRoleEditor.hierarchyView = { + label: 'Hierarchy view', + id: 'hierarchy', + templateName: 'rex-hierarchy-view-template' +}; +RexRoleEditor.singleCategoryView = { + label: 'Category view', + id: 'category', + templateName: 'rex-single-category-view-template' +}; +RexRoleEditor.listView = { label: 'List view', id: 'list', templateName: 'rex-list-view-template' }; (function () { jQuery(function ($) { - var rootElement = jQuery('#ame-role-editor-root'); + if (wsRexRoleEditorData === null) { + throw 'wsRexRoleEditorData is null. This should never happen.'; + } + const rootElement = jQuery('#ame-role-editor-root'); //Initialize the application. - var app = new RexRoleEditor(wsRexRoleEditorData); + const app = new RexRoleEditor(wsRexRoleEditorData); //The input data can be quite large, so let's give the browser a chance to free up that memory. wsRexRoleEditorData = null; window['ameRoleEditor'] = app; @@ -2968,9 +2939,9 @@ var RexRoleEditor = /** @class */ (function () { app.areBindingsApplied(true); //console.timeEnd('Apply Knockout bindings'); //Track the state of the Shift key. - var isShiftKeyDown = false; + let isShiftKeyDown = false; function handleKeyboardEvent(event) { - var newState = !!(event.shiftKey); + const newState = !!(event.shiftKey); if (newState !== isShiftKeyDown) { isShiftKeyDown = newState; app.isShiftKeyDown(isShiftKeyDown); @@ -2978,7 +2949,7 @@ var RexRoleEditor = /** @class */ (function () { } $(document).on('keydown.adminMenuEditorRex keyup.adminMenuEditorRex mousedown.adminMenuEditorRex', handleKeyboardEvent); //Initialize permission tooltips. - var visiblePermissionTooltips = []; + let visiblePermissionTooltips = []; rootElement.find('#rex-capability-view').on('mouseenter click', '.rex-permission-tip-trigger', function (event) { $(this).qtip({ overwrite: false, @@ -3016,22 +2987,22 @@ var RexRoleEditor = /** @class */ (function () { events: { show: function (event, api) { //Immediately hide all other permission tooltips. - for (var i = visiblePermissionTooltips.length - 1; i >= 0; i--) { + for (let i = visiblePermissionTooltips.length - 1; i >= 0; i--) { visiblePermissionTooltips[i].hide(); } - var permission = ko.dataFor(api.target.get(0)); + let permission = ko.dataFor(api.target.get(0)); if (permission && (permission instanceof RexPermission)) { app.permissionTipSubject(permission); } //Move the content container to the current tooltip. - var tipContent = $('#rex-permission-tip'); + const tipContent = $('#rex-permission-tip'); if (!$.contains(api.elements.content.get(0), tipContent.get(0))) { api.elements.content.empty().append(tipContent); } visiblePermissionTooltips.push(api); }, hide: function (event, api) { - var index = visiblePermissionTooltips.indexOf(api); + const index = visiblePermissionTooltips.indexOf(api); if (index >= 0) { visiblePermissionTooltips.splice(index, 1); } @@ -3043,13 +3014,13 @@ var RexRoleEditor = /** @class */ (function () { jQuery.fn.qtip.zindex = 100101 + 5000; //Set up dropdown menus. $('.rex-dropdown-trigger').on('click', function (event) { - var $trigger = $(this); - var $dropdown = $('#' + $trigger.data('target-dropdown-id')); + const $trigger = $(this); + const $dropdown = $('#' + $trigger.data('target-dropdown-id')); event.stopPropagation(); event.preventDefault(); function hideThisDropdown(event) { //Only do it if the user clicked something outside the dropdown. - var $clickedDropdown = $(event.target).closest($dropdown.get(0)); + const $clickedDropdown = $(event.target).closest($dropdown.get(0)); if ($clickedDropdown.length < 1) { $dropdown.hide(); $(document).off('click', hideThisDropdown); diff --git a/extras/modules/role-editor/role-editor.js.map b/extras/modules/role-editor/role-editor.js.map index 17de9a2..f2eedeb 100644 --- a/extras/modules/role-editor/role-editor.js.map +++ b/extras/modules/role-editor/role-editor.js.map @@ -1 +1 @@ -{"version":3,"file":"role-editor.js","sourceRoot":"","sources":["role-editor.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"role-editor.js","sourceRoot":"","sources":["role-editor.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/role-editor/role-editor.ts b/extras/modules/role-editor/role-editor.ts index 8ae7a13..1dbb1d2 100644 --- a/extras/modules/role-editor/role-editor.ts +++ b/extras/modules/role-editor/role-editor.ts @@ -11,7 +11,7 @@ class RexPermission { public readonly capability: RexCapability; labelHtml: KnockoutComputed; - protected readableAction: string = null; + protected readableAction: string | null = null; mainDescription: string = ''; @@ -122,7 +122,7 @@ class RexObservableCapabilityMap { } } - getCapabilityState(capabilityName: string): boolean { + getCapabilityState(capabilityName: string): boolean | null { const observable = this.getObservable(capabilityName); return observable(); } @@ -137,6 +137,10 @@ class RexObservableCapabilityMap { let result = this.initialCapabilities ? _.clone(this.initialCapabilities) : {}; _.forEach(this.capabilities, function (observable, name) { + if (typeof name === 'undefined') { + return; + } + const isGranted = observable(); if (isGranted === null) { delete result[name]; @@ -221,6 +225,10 @@ abstract class RexBaseActor implements IAmeActor { getOwnCapabilities(): CapabilityMap { return this.capabilities.getAllCapabilities(); } + + isUser(): this is IAmeUser { + return false; + } } class RexRole extends RexBaseActor { @@ -258,7 +266,7 @@ class RexRole extends RexBaseActor { } class RexSuperAdmin extends RexBaseActor { - private static instance: RexSuperAdmin = null; + private static instance: RexSuperAdmin | null = null; protected constructor() { super('special:super_admin', 'Super Admin', 'Super Admin'); @@ -282,15 +290,15 @@ class RexUser extends RexBaseActor implements IAmeUser { super('user:' + login, login, displayName, capabilities); this.userLogin = login; this.canHaveRoles = true; - this.roles = ko.observableArray([]); - this.userId = userId; + this.roles = ko.observableArray([] as RexRole[]); + this.userId = userId || 0; } hasCap(capability: string, outGrantedBy?: RexBaseActor[]): boolean { return (this.getCapabilityState(capability, outGrantedBy) === true); } - getCapabilityState(capability: string, outGrantedBy?: RexBaseActor[]): boolean { + getCapabilityState(capability: string, outGrantedBy?: RexBaseActor[]): boolean | null { if (capability === 'do_not_allow') { return false; } @@ -326,7 +334,12 @@ class RexUser extends RexBaseActor implements IAmeUser { // noinspection JSUnusedGlobalSymbols Used in KO templates. getInheritanceDetails(capability: RexCapability): any[] { const _ = wsAmeLodash; - let results = []; + let results: { + actor: IAmeActor, + name: string, + description: string, + isDecisive?: boolean + }[] = []; //Note: Alternative terms include "Assigned", "Granted", "Yes"/"No". if (this.isSuperAdmin) { @@ -374,7 +387,7 @@ class RexUser extends RexBaseActor implements IAmeUser { description: description, }); - let relevantActors = []; + let relevantActors: RexBaseActor[] = []; this.getCapabilityState(capability.name, relevantActors); const decidingActor = _.last(relevantActors); _.each(results, function (item) { @@ -384,6 +397,10 @@ class RexUser extends RexBaseActor implements IAmeUser { return results; } + isUser(): this is IAmeUser { + return true; + } + static fromAmeUser(data: AmeUser, editor: RexRoleEditor) { const user = new RexUser(data.userLogin, data.displayName, data.capabilities, data.userId); wsAmeLodash.forEach(data.roles, function (roleId) { @@ -420,6 +437,10 @@ class RexUser extends RexBaseActor implements IAmeUser { roles: roles }; } + + getRoleIds(): string[] { + return wsAmeLodash.map(this.roles(), 'name'); + } } interface RexStorableRoleData { @@ -447,15 +468,15 @@ interface RexUserData extends RexStorableUserData { type RexCategoryComparisonCallback = (a: RexCategory, b: RexCategory) => number; class RexCategory { - slug: string = null; + readonly slug: string | null = null; name: string; permissions: KnockoutObservableArray; - origin: RexWordPressComponent = null; - subtitle: string = null; - htmlId: string = null; + origin: RexWordPressComponent | null = null; + subtitle: string | null = null; + htmlId: string | null = null; - parent: RexCategory = null; + parent: RexCategory | null = null; subheading: KnockoutObservable; subcategories: RexCategory[] = []; @@ -498,7 +519,7 @@ class RexCategory { protected duplicates: RexCategory[] = []; - constructor(name: string, editor: RexRoleEditor, slug: string = null, capabilities: string[] = []) { + constructor(name: string, editor: RexRoleEditor, slug: string | null = null, capabilities: string[] = []) { const _ = wsAmeLodash; const self = this; this.editor = editor; @@ -640,7 +661,7 @@ class RexCategory { //Hide it if not inside the selected category. let isInSelectedCategory = false, - temp: RexCategory = self; + temp: RexCategory | null = self; while (temp !== null) { if (temp.isSelected()) { isInSelectedCategory = true; @@ -740,7 +761,9 @@ class RexCategory { if (this.slug) { this.isNavExpanded.subscribe((newValue: boolean) => { - editor.userPreferences.collapsedCategories.toggle(this.slug, !newValue); + //Type node: Because the slug is only assigned in the constructor, we can assume + //it won't become null if it's not null now. + editor.userPreferences.collapsedCategories.toggle(this.slug!, !newValue); }); } @@ -851,12 +874,16 @@ class RexCategory { * The default sort is alphabetical, but subclasses can override this method to specify a custom order. */ sortPermissions() { - this.permissions.sort(function (a, b) { - return a.capability.name.toLowerCase().localeCompare(b.capability.name.toLowerCase()); + this.permissions.sort((a, b) => { + return this.compareBasicPermissions(a, b); }); } - countUniqueCapabilities(accumulator: AmeDictionary = {}, predicate: Function = null): number { + protected compareBasicPermissions(a: RexPermission, b: RexPermission): number { + return a.capability.name.toLowerCase().localeCompare(b.capability.name.toLowerCase()); + } + + countUniqueCapabilities(accumulator: AmeDictionary = {}, predicate: Function | null = null): number { let total = 0; const permissions = this.permissions(); @@ -883,21 +910,23 @@ class RexCategory { return total; } - protected findCategoryBySlug(slug: string): RexCategory { + protected findCategoryBySlug(slug: string): RexCategory | null { if (this.editor.categoriesBySlug.hasOwnProperty(slug)) { return this.editor.categoriesBySlug[slug]; } return null; } - static fromJs(details: RexCategoryData, editor: RexRoleEditor): RexCategory { - let category; - if (details.variant && details.variant === 'post_type') { + static fromJs(details: RexAnyCategoryData, editor: RexRoleEditor): RexCategory { + let category: RexCategory; + if (!details.variant) { + category = new RexCategory(details.name, editor, details.slug, details.capabilities); + } else if (details.variant && details.variant === 'post_type') { category = new RexPostTypeCategory(details.name, editor, details.contentTypeId, details.slug, details.permissions); } else if (details.variant && details.variant === 'taxonomy') { category = new RexTaxonomyCategory(details.name, editor, details.contentTypeId, details.slug, details.permissions); } else { - category = new RexCategory(details.name, editor, details.slug, details.capabilities); + throw new Error('Unknown category data variant: ' + ((details as any).variant ?? 'undefined')); } if (details.componentId) { @@ -965,10 +994,10 @@ interface RexContentTypePermission extends RexPermission { abstract class RexContentTypeCategory extends RexCategory { public actions: { [action: string]: RexContentTypePermission } = {}; - protected baseCategorySlug: string = null; + protected baseCategorySlug: string | null = null; isBaseCapNoticeVisible: KnockoutComputed; - protected constructor(name: string, editor: RexRoleEditor, slug: string = null) { + protected constructor(name: string, editor: RexRoleEditor, slug: string | null = null) { super(name, editor, slug); this.isBaseCapNoticeVisible = ko.pureComputed({ @@ -1006,7 +1035,7 @@ abstract class RexContentTypeCategory extends RexCategory { return allCapsMatch; } - protected getBaseCategory(): RexContentTypeCategory { + protected getBaseCategory(): RexContentTypeCategory | null { if (this.baseCategorySlug !== null) { let result = this.findCategoryBySlug(this.baseCategorySlug); if (result instanceof RexContentTypeCategory) { @@ -1072,7 +1101,7 @@ class RexPostTypeCategory extends RexContentTypeCategory { name: string, editor: RexRoleEditor, postTypeId: string, - slug: string = null, + slug: string | null = null, permissions: { [action: string]: string }, isDefault: boolean = false ) { @@ -1090,6 +1119,10 @@ class RexPostTypeCategory extends RexContentTypeCategory { } this.permissions = ko.observableArray(_.map(permissions, (capability, action) => { + if (typeof action === 'undefined') { + throw new Error('Invalid action name: undefined. This should never happen.'); + } + const permission = new RexPostTypePermission(editor, editor.getCapability(capability), action, this.pluralLabel); //The "read" capability is already shown in the core category and every role has it by default. @@ -1098,14 +1131,15 @@ class RexPostTypeCategory extends RexContentTypeCategory { } this.actions[action] = permission; - return permission; + return permission as RexPermission; })); this.sortPermissions(); + type ActionMapItem = (typeof this.actions[string]); //The "create" capability is often the same as the "edit" capability. - const editPerm = _.get(this.actions, 'edit_posts', null), - createPerm = _.get(this.actions, 'create_posts', null); + const editPerm = _.get(this.actions, 'edit_posts', null), + createPerm = _.get(this.actions, 'create_posts', null); if (editPerm && createPerm && (createPerm.capability.name === editPerm.capability.name)) { createPerm.isRedundant = true; } @@ -1117,16 +1151,19 @@ class RexPostTypeCategory extends RexContentTypeCategory { } sortPermissions() { - this.permissions.sort(function (a: RexPostTypePermission, b: RexPostTypePermission) { - const priorityA = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexPostTypeCategory.desiredActionOrder[a.action] : 1000; - const priorityB = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexPostTypeCategory.desiredActionOrder[b.action] : 1000; + this.permissions.sort((a, b)=> { + if ((a instanceof RexPostTypePermission) && (b instanceof RexPostTypePermission)) { + const priorityA = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexPostTypeCategory.desiredActionOrder[a.action] : 1000; + const priorityB = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexPostTypeCategory.desiredActionOrder[b.action] : 1000; - let delta = priorityA - priorityB; - if (delta !== 0) { - return delta; - } + let delta = priorityA - priorityB; + if (delta !== 0) { + return delta; + } - return a.capability.name.localeCompare(b.capability.name); + return a.capability.name.localeCompare(b.capability.name); + } + return this.compareBasicPermissions(a, b); }); } @@ -1174,7 +1211,7 @@ class RexTaxonomyCategory extends RexContentTypeCategory { name: string, editor: RexRoleEditor, taxonomyId: string, - slug: string = null, + slug: string | null = null, permissions: { [action: string]: string } ) { super(name, editor, slug); @@ -1185,7 +1222,12 @@ class RexTaxonomyCategory extends RexContentTypeCategory { this.subtitle = taxonomyId; const noun = name.toLowerCase(); - this.permissions = ko.observableArray(_.map(permissions, (capability, action) => { + this.permissions = ko.observableArray(_.map(permissions, (capability, action) => { + if (typeof action === 'undefined') { + //Can't happen in practice, but TypeScript doesn't know that. + throw new Error('Invalid action name: undefined. This should never happen.'); + } + const permission = new RexTaxonomyPermission(editor, editor.getCapability(capability), action, noun); this.actions[action] = permission; return permission; @@ -1212,16 +1254,19 @@ class RexTaxonomyCategory extends RexContentTypeCategory { } sortPermissions(): void { - this.permissions.sort(function (a: RexTaxonomyPermission, b: RexTaxonomyPermission) { - const priorityA = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexTaxonomyCategory.desiredActionOrder[a.action] : 1000; - const priorityB = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexTaxonomyCategory.desiredActionOrder[b.action] : 1000; + this.permissions.sort((a: RexPermission, b: RexPermission) => { + if ((a instanceof RexTaxonomyPermission) && (b instanceof RexTaxonomyPermission)) { + const priorityA = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexTaxonomyCategory.desiredActionOrder[a.action] : 1000; + const priorityB = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexTaxonomyCategory.desiredActionOrder[b.action] : 1000; - let delta = priorityA - priorityB; - if (delta !== 0) { - return delta; - } + let delta = priorityA - priorityB; + if (delta !== 0) { + return delta; + } - return a.capability.name.localeCompare(b.capability.name); + return a.capability.name.localeCompare(b.capability.name); + } + return this.compareBasicPermissions(a, b); }); } @@ -1237,11 +1282,11 @@ interface RexPermissionTableColumn { actions: string[]; } -class RexTableViewCategory extends RexCategory { - tableColumns: KnockoutComputed; - subcategoryComparisonCallback: RexCategoryComparisonCallback = null; +abstract class RexTableViewCategory extends RexCategory { + abstract tableColumns: KnockoutComputed; + subcategoryComparisonCallback: RexCategoryComparisonCallback; - constructor(name: string, editor: RexRoleEditor, slug: string = null) { + protected constructor(name: string, editor: RexRoleEditor, slug: string | null = null) { super(name, editor, slug); this.contentTemplate = ko.pureComputed(function () { @@ -1284,7 +1329,9 @@ class RexTableViewCategory extends RexCategory { } class RexTaxonomyContainerCategory extends RexTableViewCategory { - constructor(name: string, editor: RexRoleEditor, slug: string = null) { + tableColumns: KnockoutComputed; + + constructor(name: string, editor: RexRoleEditor, slug: string | null = null) { super(name, editor, slug); this.htmlId = 'rex-taxonomy-summary-category'; @@ -1312,7 +1359,7 @@ class RexTaxonomyContainerCategory extends RexTableViewCategory { actions: ['delete_terms'] } ]; - let misColumnExists = false, miscColumn: RexPermissionTableColumn = null; + let miscColumn: RexPermissionTableColumn | null = null; for (let i = 0; i < this.subcategories.length; i++) { const category = this.subcategories[i]; @@ -1323,7 +1370,7 @@ class RexTaxonomyContainerCategory extends RexTableViewCategory { //Display any unrecognized actions in a "Misc" column. const customActions = _.omit(category.actions, defaultTaxonomyActions); if (!_.isEmpty(customActions)) { - if (!misColumnExists) { + if (!miscColumn) { miscColumn = {title: 'Misc', actions: []}; columns.push(miscColumn); } @@ -1339,7 +1386,9 @@ class RexTaxonomyContainerCategory extends RexTableViewCategory { } class RexPostTypeContainerCategory extends RexTableViewCategory { - constructor(name: string, editor: RexRoleEditor, slug: string = null) { + tableColumns: KnockoutComputed; + + constructor(name: string, editor: RexRoleEditor, slug: string | null = null) { super(name, editor, slug); /* Note: This seems like poor design because the superclass overrides subclass @@ -1347,23 +1396,27 @@ class RexPostTypeContainerCategory extends RexTableViewCategory { * come up with anything better so far. Might be something to revisit later. */ - this.subcategoryComparisonCallback = function (a: RexPostTypeCategory, b: RexPostTypeCategory) { - //Special case: Put "Posts" at the top. - if (a.postType === 'post') { - return -1; - } else if (b.postType === 'post') { - return 1; - } + this.subcategoryComparisonCallback = function (a, b) { + if ((a instanceof RexPostTypeCategory) && (b instanceof RexPostTypeCategory)) { + //Special case: Put "Posts" at the top. + if (a.postType === 'post') { + return -1; + } else if (b.postType === 'post') { + return 1; + } - //Put other built-in post types above custom post types. - if (a.isDefault && !b.isDefault) { - return -1; - } else if (b.isDefault && !a.isDefault) { - return 1; + //Put other built-in post types above custom post types. + if (a.isDefault && !b.isDefault) { + return -1; + } else if (b.isDefault && !a.isDefault) { + return 1; + } + + let labelA = a.name.toLowerCase(), labelB = b.name.toLowerCase(); + return labelA.localeCompare(labelB); } - let labelA = a.name.toLowerCase(), labelB = b.name.toLowerCase(); - return labelA.localeCompare(labelB); + return RexCategory.defaultSubcategoryComparison(a, b); }; this.tableColumns = ko.pureComputed({ @@ -1434,7 +1487,7 @@ class RexCapability { readonly isPersonalOverride: KnockoutComputed; readonly isExplicitlyDenied: KnockoutComputed; - originComponent: RexWordPressComponent = null; + originComponent: RexWordPressComponent | null = null; usedByComponents: RexWordPressComponent[] = []; menuItems: string[] = []; @@ -1443,8 +1496,8 @@ class RexCapability { predefinedPermissions: string[] = []; grantedPermissions: KnockoutComputed; - protected documentationUrl: string = null; - notes: string = null; + protected documentationUrl: string | null = null; + notes: string | null = null; readonly isDeleted: KnockoutObservable; @@ -1466,7 +1519,7 @@ class RexCapability { this.responsibleActors = ko.computed({ read: function () { - let actor = editor.selectedActor(), list = []; + let actor = editor.selectedActor(), list: RexBaseActor[] = []; if (actor instanceof RexUser) { actor.hasCap(self.name, list); } @@ -1579,7 +1632,7 @@ class RexCapability { this.grantedPermissions = ko.computed({ read: () => { const _ = wsAmeLodash; - let results = []; + let results: string[] = []; if (this.predefinedPermissions.length > 0) { results = this.predefinedPermissions.slice(); @@ -1595,6 +1648,10 @@ class RexCapability { descriptions: AmeDictionary ): string[] { return _.map(actionGroups, (ids, action) => { + if (typeof action === 'undefined') { + throw new Error('Undefined action. This should never happen.'); + } + let labels = _.map(ids, (id) => labelMap[id].pluralLabel) .sort(localeAwareCompare); let template = descriptions[action]; @@ -1609,6 +1666,10 @@ class RexCapability { let postActionGroups = _.transform( this.usedByPostTypeActions, function (accumulator: { [action: string]: string[] }, actions, postType) { + if (typeof postType === 'undefined') { + return; + } + let actionKeys = _.keys(actions); //Combine "edit" and "create" permissions because they usually use the same capability. @@ -1638,6 +1699,10 @@ class RexCapability { let taxonomyActionGroups = _.transform( this.usedByTaxonomyActions, function (accumulator: { [action: string]: string[] }, actions, taxonomy) { + if (typeof taxonomy === 'undefined') { + return; + } + let actionKeys = _.keys(actions); //Most taxonomies use the same capability for manage_terms, edit_terms, and delete_terms. @@ -1830,12 +1895,12 @@ class RexUserPreferences { } } - getObservable(name: string, defaultValue: T = null): KnockoutObservable { + getObservable(name: string, defaultValue: T): KnockoutObservable { if (this.preferenceObservables.hasOwnProperty(name)) { return this.preferenceObservables[name]; } - const newPreference = ko.observable(defaultValue || null); + const newPreference = ko.observable(defaultValue); this.preferenceObservables[name] = newPreference; this.preferenceCount(this.preferenceCount() + 1); @@ -1909,13 +1974,28 @@ interface RexCategoryData { slug?: string; capabilities?: string[]; - permissions?: { [action: string]: string }; - subcategories?: RexCategoryData[]; + subcategories?: RexAnyCategoryData[]; +} - variant?: string; - contentTypeId?: string; //Post type or taxonomy name (internal ID, not display name). +interface RexBasicCategoryData extends RexCategoryData { + variant?: null; } +interface RexExtendedCategoryData extends RexCategoryData { + permissions: { [action: string]: string }; + contentTypeId: string; +} + +interface RexPostTypeCategoryData extends RexExtendedCategoryData { + variant: 'post_type'; +} + +interface RexTaxonomyCategoryData extends RexExtendedCategoryData { + variant: 'taxonomy'; +} + +type RexAnyCategoryData = RexBasicCategoryData | RexPostTypeCategoryData | RexTaxonomyCategoryData; + interface RexBaseContentData { name: string; label: string; @@ -1952,8 +2032,8 @@ interface RexAppData { defaultRoleName: string; trashedRoles: RexRoleData[]; - coreCategory: RexCategoryData; - customCategories: RexCategoryData[]; + coreCategory: RexAnyCategoryData; + customCategories: RexAnyCategoryData[]; uncategorizedCapabilities: string[]; userDefinedCapabilities: CapabilityMap; @@ -1978,8 +2058,8 @@ interface RexCategoryViewOption { class RexBaseDialog implements AmeKnockoutDialog { isOpen: KnockoutObservable = ko.observable(false); isRendered: KnockoutObservable = ko.observable(false); - jQueryWidget: JQuery; - title: KnockoutObservable = null; + jQueryWidget: JQuery | null = null; + title: KnockoutObservable = ko.observable(null); options: AmeDictionary = { buttons: [] }; @@ -1993,6 +2073,10 @@ class RexBaseDialog implements AmeKnockoutDialog { } setupValidationTooltip(inputSelector: string, message: KnockoutObservable) { + if (this.jQueryWidget === null) { + return; + } + //Display validation messages next to the input field. const element = this.jQueryWidget.find(inputSelector).qtip({ overwrite: false, @@ -2048,7 +2132,7 @@ interface RexDeletableCapItem { class RexDeleteCapDialog extends RexBaseDialog { options = { - buttons: [], + buttons: [] as any[], minWidth: 380 }; @@ -2100,7 +2184,7 @@ class RexDeleteCapDialog extends RexBaseDialog { read: () => { const wpCore = editor.getComponent(':wordpress:'); return _.chain(editor.capabilities) - .filter(function (capability) { + .filter(function (capability: RexCapability) { if (capability.originComponent === wpCore) { return false; } @@ -2146,6 +2230,10 @@ class RexDeleteCapDialog extends RexBaseDialog { }); deleteButtonText.subscribe((newText) => { + if (this.jQueryWidget === null) { + return; + } + this.jQueryWidget .closest('.ui-dialog') .find('.ui-dialog-buttonset .button-primary .ui-button-text') @@ -2276,7 +2364,7 @@ class RexAddCapabilityDialog extends RexBaseDialog { }]; } - onOpen(event, ui) { + onOpen(event: JQueryEventObject, ui: unknown) { //Clear the input when the dialog is opened. this.capabilityName(''); } @@ -2300,7 +2388,7 @@ class RexAddCapabilityDialog extends RexBaseDialog { class RexAddRoleDialog extends RexBaseDialog { roleName: KnockoutObservable = ko.observable(''); roleDisplayName: KnockoutObservable = ko.observable(''); - roleToCopyFrom: KnockoutObservable = ko.observable(null); + roleToCopyFrom: KnockoutObservable = ko.observable(null); isAddButtonEnabled: KnockoutComputed; @@ -2390,7 +2478,7 @@ class RexAddRoleDialog extends RexBaseDialog { }); //Automatically generate a role name from the display name. Basically, turn it into a slug. - let lastAutoRoleName = null; + let lastAutoRoleName: string | null = null; this.roleDisplayName.subscribe((displayName) => { let slug = _.snakeCase(displayName); @@ -2430,7 +2518,7 @@ class RexAddRoleDialog extends RexBaseDialog { return true; } - onOpen(event, ui) { + onOpen(event: JQueryEventObject, ui: any) { //Clear dialog fields when it's opened. this.roleName(''); this.roleDisplayName(''); @@ -2451,8 +2539,9 @@ class RexAddRoleDialog extends RexBaseDialog { this.isOpen(false); let caps = {}; - if (this.roleToCopyFrom()) { - caps = this.roleToCopyFrom().getOwnCapabilities(); + const sourceRole = this.roleToCopyFrom(); + if (sourceRole) { + caps = sourceRole.getOwnCapabilities(); } this.editor.addRole(this.roleName(), this.roleDisplayName(), caps); @@ -2504,7 +2593,7 @@ class RexDeleteRoleDialog extends RexBaseDialog { this.isOpen(false); } - onOpen(event, ui) { + onOpen(event: JQueryEventObject, ui: any) { //Deselect all previously selected roles. wsAmeLodash.forEach(this.isRoleSelected, function (isSelected) { isSelected(false); @@ -2520,7 +2609,7 @@ class RexDeleteRoleDialog extends RexBaseDialog { private getSelectedRoles(): RexRole[] { const _ = wsAmeLodash; - let rolesToDelete = []; + let rolesToDelete: RexRole[] = []; _.forEach(this.editor.roles(), (role) => { if (this.getSelectionState(role.name())()) { rolesToDelete.push(role); @@ -2533,7 +2622,7 @@ class RexDeleteRoleDialog extends RexBaseDialog { class RexRenameRoleDialog extends RexBaseDialog { private editor: RexRoleEditor; isConfirmButtonEnabled: KnockoutComputed; - selectedRole: KnockoutObservable = ko.observable(null); + selectedRole: KnockoutObservable = ko.observable(null); newDisplayName: KnockoutObservable = ko.observable(''); displayNameValidationMessage: KnockoutObservable = ko.observable(''); @@ -2565,7 +2654,7 @@ class RexRenameRoleDialog extends RexBaseDialog { }); } - onOpen(event, ui) { + onOpen(event: JQueryEventObject, ui: any) { const _ = wsAmeLodash; if (!this.isTooltipInitialised) { @@ -2586,9 +2675,10 @@ class RexRenameRoleDialog extends RexBaseDialog { if (!this.isConfirmButtonEnabled()) { return; } - if (this.selectedRole()) { + const selectedRole = this.selectedRole(); + if (selectedRole) { const name = this.newDisplayName().trim(); - this.selectedRole().displayName(name); + selectedRole.displayName(name); this.editor.actorSelector.repopulate(); } this.isOpen(false); @@ -2634,11 +2724,11 @@ class RexEagerObservableStringSet { return this.items[item]; } - public getAsObject(fillValue: T | boolean = true): Record { + public getAsObject(fillValue: T): Record { const _ = wsAmeLodash; - let output = {}; + let output: Record = {}; _.forEach(this.items, (isInSet, item) => { - if (isInSet()) { + if (isInSet() && (typeof item !== 'undefined')) { output[item] = fillValue; } }); @@ -2651,12 +2741,12 @@ class RexObservableEditableRoleSettings { userDefinedList: RexEagerObservableStringSet; constructor() { - this.strategy = ko.observable('auto'); + this.strategy = ko.observable('auto'); this.userDefinedList = new RexEagerObservableStringSet(); } toPlainObject(): RexEditableRoleSettings { - let roleList = this.userDefinedList.getAsObject(true); + let roleList: Record | null = this.userDefinedList.getAsObject(true); if (wsAmeLodash.isEmpty(roleList)) { roleList = null; } @@ -2802,7 +2892,7 @@ class RexUserRoleModule { class RexEditableRolesDialog extends RexBaseDialog { private editor: RexRoleEditor; private readonly visibleActors: KnockoutObservableArray; - selectedActor: KnockoutObservable = ko.observable(null); + selectedActor: KnockoutObservable = ko.observable(null); private actorSettings: { [actorId: string]: RexObservableEditableRoleSettings; } = {}; private selectedActorSettings: KnockoutObservable; @@ -2814,7 +2904,7 @@ class RexEditableRolesDialog extends RexBaseDialog { constructor(editor: RexRoleEditor) { super(); this.editor = editor; - this.visibleActors = ko.observableArray([]); + this.visibleActors = ko.observableArray([] as IAmeActor[]); this.options.minWidth = 600; this.options.buttons.push({ @@ -2832,15 +2922,16 @@ class RexEditableRolesDialog extends RexBaseDialog { const dummySettings = new RexObservableEditableRoleSettings(); this.selectedActorSettings = ko.computed(() => { - if (this.selectedActor() === null) { + const selectedActor = this.selectedActor(); + if (selectedActor === null) { return dummySettings; } - if (this.selectedActor() === superAdmin) { + if (selectedActor === superAdmin) { return superAdminSettings; } - const actorId = this.selectedActor().getId(); + const actorId = selectedActor.getId(); if (!this.actorSettings.hasOwnProperty(actorId)) { - //This should never happen; the dictionary should be initialised when opening the dialog. + //This should never happen; the dictionary should be initialized when opening the dialog. this.actorSettings[actorId] = new RexObservableEditableRoleSettings(); } return this.actorSettings[actorId]; @@ -2868,11 +2959,14 @@ class RexEditableRolesDialog extends RexBaseDialog { this.isListStrategyAllowed = this.isAutoStrategyAllowed; } - onOpen(event, ui) { + onOpen(event: JQueryEventObject, ui: any) { const _ = wsAmeLodash; //Copy editable role settings into observables. - _.forEach(this.editor.actorEditableRoles, (settings: RexEditableRoleSettings, actorId: string) => { + _.forEach(this.editor.actorEditableRoles, (settings: RexEditableRoleSettings, actorId) => { + if (typeof actorId !== 'string') { + return; + } if (!this.actorSettings.hasOwnProperty(actorId)) { this.actorSettings[actorId] = new RexObservableEditableRoleSettings(); } @@ -2880,7 +2974,10 @@ class RexEditableRolesDialog extends RexBaseDialog { observableSettings.strategy(settings.strategy); observableSettings.userDefinedList.clear(); if (settings.userDefinedList !== null) { - _.forEach(settings.userDefinedList, (ignored, roleId: string) => { + _.forEach(settings.userDefinedList, (ignored, roleId) => { + if (typeof roleId !== 'string') { + return; + } observableSettings.userDefinedList.add(roleId); }); } @@ -2902,8 +2999,12 @@ class RexEditableRolesDialog extends RexBaseDialog { const _ = wsAmeLodash; let settings = this.editor.actorEditableRoles; _.forEach(this.actorSettings, (observableSettings, actorId) => { + if (typeof actorId === 'undefined') { + throw new Error('Actor ID is undefined. This should never happen.'); + } + if (observableSettings.strategy() === 'auto') { - //"auto" is the default so we don't need to store anything. + //"auto" is the default, so we don't need to store anything. delete settings[actorId]; } else { settings[actorId] = observableSettings.toPlainObject(); @@ -2968,7 +3069,7 @@ class RexRoleEditor implements AmeActorManagerInterface { readonly coreComponent: RexWordPressComponent; rootCategory: RexCategory; - selectedCategory: KnockoutComputed; + selectedCategory: KnockoutComputed; categoriesBySlug: AmeDictionary = {}; categoryViewMode: KnockoutObservable; capabilityViewClasses: KnockoutObservable; @@ -2987,7 +3088,7 @@ class RexRoleEditor implements AmeActorManagerInterface { actorSelector: AmeActorSelector; private actorLookup: AmeDictionary = {}; private readonly dummyActor: RexRole; - permissionTipSubject: KnockoutObservable; + permissionTipSubject: KnockoutObservable; searchQuery: KnockoutObservable; searchKeywords: KnockoutComputed; @@ -3116,13 +3217,16 @@ class RexRoleEditor implements AmeActorManagerInterface { }); this.components = _.mapValues(data.knownComponents, (details, id) => { + if (typeof id === 'undefined') { + throw new Error('Undefined component ID. This should never happen.'); + } return RexWordPressComponent.fromJs(id, details); }); this.coreComponent = new RexWordPressComponent(':wordpress:', 'WordPress core'); this.components[':wordpress:'] = this.coreComponent; //Populate roles and users. - const tempRoleList = []; + const tempRoleList: RexRole[] = []; _.forEach(data.roles, (roleData) => { const role = new RexRole(roleData.name, roleData.displayName, roleData.capabilities); role.hasUsers = roleData.hasUsers; @@ -3131,7 +3235,7 @@ class RexRoleEditor implements AmeActorManagerInterface { }); this.roles = ko.observableArray(tempRoleList); - const tempUserList = []; + const tempUserList: RexUser[] = []; _.forEach(AmeActors.getUsers(), (data) => { const user = RexUser.fromAmeUser(data, self); tempUserList.push(user); @@ -3147,7 +3251,11 @@ class RexRoleEditor implements AmeActorManagerInterface { this.actorSelector = new AmeActorSelector(this, true, false); //Wrap the selected actor in a computed observable so that it can be used with Knockout. - let _selectedActor = ko.observable(this.getActor(this.actorSelector.selectedActor)); + let _selectedActor = ko.observable( + (this.actorSelector.selectedActor === null) + ? this.dummyActor + : this.getActor(this.actorSelector.selectedActor) + ); this.selectedActor = ko.computed({ read: function () { return _selectedActor(); @@ -3156,7 +3264,11 @@ class RexRoleEditor implements AmeActorManagerInterface { this.actorSelector.setSelectedActor(newActor.id()); } }); - this.actorSelector.onChange((newSelectedActor: string) => { + this.actorSelector.onChange((newSelectedActor: string | null) => { + if (newSelectedActor === null) { + _selectedActor(this.dummyActor); //This should never happen in practice. + return; + } _selectedActor(this.getActor(newSelectedActor)); }); @@ -3166,7 +3278,7 @@ class RexRoleEditor implements AmeActorManagerInterface { }); //Re-select the previously selected actor if possible. - let initialActor: RexBaseActor = null; + let initialActor: RexBaseActor | null = null; if (data.selectedActor) { initialActor = this.getActor(data.selectedActor); } @@ -3180,7 +3292,10 @@ class RexRoleEditor implements AmeActorManagerInterface { this.metaCapabilityMap = data.metaCapMap; this.userDefinedCapabilities = data.userDefinedCapabilities; - this.capabilities = _.mapValues(data.capabilities, (metadata: RexCapabilityData, name: string) => { + this.capabilities = _.mapValues(data.capabilities, (metadata: RexCapabilityData, name) => { + if (typeof name === 'undefined') { + throw new Error('Undefined capability name. This should never happen.'); + } return RexCapability.fromJs(name, metadata, self); }); @@ -3207,6 +3322,10 @@ class RexRoleEditor implements AmeActorManagerInterface { const postTypeCategory = new RexPostTypeContainerCategory('Post Types', this, 'postTypes'); this.postTypes = _.indexBy(data.postTypes, 'name'); _.forEach(this.postTypes, (details: RexPostTypeData, id) => { + if (typeof id === 'undefined') { + throw new Error('Undefined post type ID. This should never happen.'); + } + const category = new RexPostTypeCategory(details.label, self, id, 'postTypes/' + id, details.permissions, details.isDefault); if (details.componentId) { category.origin = this.getComponent(details.componentId); @@ -3227,6 +3346,10 @@ class RexRoleEditor implements AmeActorManagerInterface { this.taxonomies = data.taxonomies; const taxonomyCategory = new RexTaxonomyContainerCategory('Taxonomies', this, 'taxonomies'); _.forEach(data.taxonomies, (details, id) => { + if (typeof id === 'undefined') { + throw new Error('Undefined taxonomy ID. This should never happen.'); + } + const category = new RexTaxonomyCategory(details.label, self, id, 'taxonomies/' + id, details.permissions); taxonomyCategory.addSubcategory(category); @@ -3244,7 +3367,7 @@ class RexRoleEditor implements AmeActorManagerInterface { const customParentCategory = new RexCategory('Plugins', this, 'custom'); - function initCustomCategory(details: RexCategoryData, parent: RexCategory) { + function initCustomCategory(details: RexAnyCategoryData, parent: RexCategory) { let category = RexCategory.fromJs(details, self); //Sort subcategories by title. @@ -3280,7 +3403,7 @@ class RexRoleEditor implements AmeActorManagerInterface { ); customParentCategory.addSubcategory(uncategorizedCategory); - let _selectedCategory: KnockoutObservable = ko.observable(null); + let _selectedCategory: KnockoutObservable = ko.observable(null); this.selectedCategory = ko.computed({ read: function () { return _selectedCategory(); @@ -3309,8 +3432,8 @@ class RexRoleEditor implements AmeActorManagerInterface { //Create a permission for each unique, non-deleted capability. //Exclude special caps like do_not_allow and exist because they can't be enabled. const excludedCaps = ['do_not_allow', 'exist']; - return _.chain(this.capabilities) - .map(function (capability) { + const result = _.chain(this.capabilities) + .map(function (capability: RexCapability) { if (excludedCaps.indexOf(capability.name) >= 0) { return null; } @@ -3320,6 +3443,9 @@ class RexRoleEditor implements AmeActorManagerInterface { return value !== null }) .value(); + + //TypeScript doesn't know that the filter above eliminates nulls. + return result as RexPermission[]; }, deferEvaluation: true }); @@ -3486,7 +3612,7 @@ class RexRoleEditor implements AmeActorManagerInterface { return this.dummyActor; } - getRole(name: string): RexRole { + getRole(name: string): RexRole | null { const actorId = 'role:' + name; if (this.actorLookup.hasOwnProperty(actorId)) { const role = this.actorLookup[actorId]; @@ -3566,7 +3692,7 @@ class RexRoleEditor implements AmeActorManagerInterface { return RexSuperAdmin.getInstance(); } - getUser(login: string): RexUser { + getUser(login: string): RexUser | null { const actorId = 'user:' + login; if (this.actorLookup.hasOwnProperty(actorId)) { const user = this.actorLookup[actorId]; @@ -3586,7 +3712,7 @@ class RexRoleEditor implements AmeActorManagerInterface { return caps.hasOwnProperty(capabilityName); } - addCapability(capabilityName: string): RexCategory { + addCapability(capabilityName: string): RexCategory | null { let capability: RexCapability; if (this.capabilities.hasOwnProperty(capabilityName)) { capability = this.capabilities[capabilityName]; @@ -3702,10 +3828,14 @@ class RexRoleEditor implements AmeActorManagerInterface { } } -declare var wsRexRoleEditorData: RexAppData; +declare var wsRexRoleEditorData: RexAppData | null; (function () { - jQuery(function ($) { + jQuery(function ($: JQueryStatic) { + if (wsRexRoleEditorData === null) { + throw 'wsRexRoleEditorData is null. This should never happen.'; + } + const rootElement = jQuery('#ame-role-editor-root'); //Initialize the application. @@ -3713,7 +3843,7 @@ declare var wsRexRoleEditorData: RexAppData; //The input data can be quite large, so let's give the browser a chance to free up that memory. wsRexRoleEditorData = null; - window['ameRoleEditor'] = app; + (window as any)['ameRoleEditor'] = app; //console.time('Apply Knockout bindings'); //ko.options.deferUpdates = true; @@ -3724,7 +3854,7 @@ declare var wsRexRoleEditorData: RexAppData; //Track the state of the Shift key. let isShiftKeyDown = false; - function handleKeyboardEvent(event) { + function handleKeyboardEvent(event: JQueryEventObject) { const newState = !!(event.shiftKey); if (newState !== isShiftKeyDown) { isShiftKeyDown = newState; @@ -3738,9 +3868,12 @@ declare var wsRexRoleEditorData: RexAppData; ); //Initialize permission tooltips. - let visiblePermissionTooltips = []; + let visiblePermissionTooltips: any[] = []; - rootElement.find('#rex-capability-view').on('mouseenter click', '.rex-permission-tip-trigger', function (event) { + rootElement.find('#rex-capability-view').on('mouseenter click', '.rex-permission-tip-trigger', function ( + this: HTMLElement, + event + ) { $(this).qtip({ overwrite: false, content: { @@ -3778,7 +3911,7 @@ declare var wsRexRoleEditorData: RexAppData; }, events: { - show: function (event, api) { + show: function (event: JQueryEventObject, api: any) { //Immediately hide all other permission tooltips. for (let i = visiblePermissionTooltips.length - 1; i >= 0; i--) { visiblePermissionTooltips[i].hide(); @@ -3797,7 +3930,7 @@ declare var wsRexRoleEditorData: RexAppData; visiblePermissionTooltips.push(api); }, - hide: function (event, api) { + hide: function (event: JQueryEventObject, api: any) { const index = visiblePermissionTooltips.indexOf(api); if (index >= 0) { visiblePermissionTooltips.splice(index, 1); @@ -3811,14 +3944,14 @@ declare var wsRexRoleEditorData: RexAppData; jQuery.fn.qtip.zindex = 100101 + 5000; //Set up dropdown menus. - $('.rex-dropdown-trigger').on('click', function (event) { + $('.rex-dropdown-trigger').on('click', function (this: HTMLElement, event) { const $trigger = $(this); const $dropdown = $('#' + $trigger.data('target-dropdown-id')); event.stopPropagation(); event.preventDefault(); - function hideThisDropdown(event) { + function hideThisDropdown(event: JQueryEventObject) { //Only do it if the user clicked something outside the dropdown. const $clickedDropdown = $(event.target).closest($dropdown.get(0)); if ($clickedDropdown.length < 1) { diff --git a/extras/modules/separator-styles/MenuSeparatorStyler.php b/extras/modules/separator-styles/MenuSeparatorStyler.php new file mode 100644 index 0000000..0804b04 --- /dev/null +++ b/extras/modules/separator-styles/MenuSeparatorStyler.php @@ -0,0 +1,630 @@ +menuEditor = $menuEditor; + ameMenu::add_custom_loader([$this, 'loadSeparatorSettings']); + + if ( !is_admin() ) { + return; + } + + add_filter('ame_pre_set_custom_menu', [$this, 'addSeparatorModTimeToConfiguration']); + add_action('init', [$this, 'registerSeparatorStylesheet']); + + add_filter('admin_menu_editor-aux_data_config', [$this, 'addAuxDataConfig']); + + add_action('admin_menu_editor-ms_ui_structure', [$this, 'addSeparatorsToMenuStyler']); + add_action('admin_menu_editor-ms_ui_setting_defaults', [$this, 'addDefaultsToMenuStyler']); + add_action('admin_menu_editor-ms_ui_style_generators', [$this, 'addStyleGeneratorToMenuStyler']); + } + + public function addSeparatorModTimeToConfiguration($customMenu) { + if ( empty($customMenu) || !is_array($customMenu) ) { + return $customMenu; + } + + if ( empty($customMenu[SeparatorStyleSettings::CONFIG_KEY]) ) { + unset($customMenu['separator_css_modified']); + return $customMenu; + } + + //For backwards compatibility reasons, the separator setting modification + //timestamp is stored in the root of the menu configuration. + $customMenu['separator_css_modified'] = time(); + + return $customMenu; + } + + public function loadSeparatorSettings($menuConfig, $storedConfig) { + //Copy separator settings. + if ( isset($storedConfig['separators']) ) { + $menuConfig['separators'] = $storedConfig['separators']; + + //Translate margin settings to the new format. Aliases only help + //when reading settings in PHP, but we also use them in JavaScript. + $missing = '__missing'; + foreach (SeparatorStyleSettings::getMarginAliases() as $newPath => $oldPath) { + $fullNewPath = 'separators.' . $newPath; + $fullOldPath = 'separators.' . $oldPath; + + $oldValue = ameMultiDictionary::get($menuConfig, $fullOldPath, $missing); + if ( $oldValue !== $missing ) { + if ( ameMultiDictionary::get($menuConfig, $fullNewPath, $missing) === $missing ) { + ameMultiDictionary::set($menuConfig, $fullNewPath, $oldValue); + } + ameMultiDictionary::delete($menuConfig, $fullOldPath); + } + } + } + //Copy the modification timestamp. + if ( isset($storedConfig['separator_css_modified']) ) { + $menuConfig['separator_css_modified'] = intval($storedConfig['separator_css_modified']); + } + return $menuConfig; + } + + protected function getSettings($menuConfigId = null) { + if ( $this->settings !== null ) { + return $this->settings; + } + + if ( $menuConfigId !== null ) { + $helper = MenuScopedStylesheetHelper::getInstance($this->menuEditor); + $menuConfigId = $helper->getConfigIdFromAjaxRequest(); + } + + $this->settings = new SeparatorStyleSettings( + MenuConfigurationWrapper::getStore($menuConfigId) + ->buildSlot(SeparatorStyleSettings::CONFIG_KEY) + ); + return $this->settings; + } + + protected function getInterfaceStructure() { + $settings = $this->getSettings(); + $b = $settings->elementBuilder(); + + $mainSection = $b->section( + 'Separators', + $b->auto('customSettingsEnabled')->asGroup()->params(['fullWidth' => true]) + )->id('ame-sep-Separators-section'); + + $sectionLabels = [ + 'topLevelSeparators' => 'Top level separators', + 'submenuSeparators' => 'Submenu separators', + ]; + + $submenuEnabledCondition = new SettingCondition( + $settings->findSetting('useTopLevelSettingsForSubmenus'), + SettingCondition::IS_FALSY, + false + ); + + foreach (SeparatorStyleSettings::SEPARATOR_TYPE_KEYS as $separatorType) { + $prefix = $separatorType . '.'; + + //Put each separator type in its own section. + $subSection = $b->section( + isset($sectionLabels[$separatorType]) ? $sectionLabels[$separatorType] : $separatorType + ); + //Show the "use top level settings" checkbox in the submenu section. + if ( $separatorType === 'submenuSeparators' ) { + $subSection->add( + $b->auto('useTopLevelSettingsForSubmenus')->asGroup()->params(['fullWidth' => true]) + ); + } + $condition = ($separatorType === 'submenuSeparators') ? $submenuEnabledCondition : true; + + $subSection->add( + $b->radioGroup($prefix . 'colorType') + ->choiceChild('custom', $b->auto($prefix . 'customColor')) + ->classes('ame-rg-with-color-pickers') + ->enabled($condition), + $b->control(BorderStyleSelector::class, $prefix . 'borderStyle')->enabled($condition), + $b->auto($prefix . 'height')->params(['step' => 1])->enabled($condition), + $b->radioGroup($prefix . 'widthStrategy') + ->enabled($condition) + ->choiceChild( + 'percentage', + $b->auto($prefix . 'widthInPercent')->params(['step' => 1]) + ) + ->choiceChild( + 'fixed', + $b->auto($prefix . 'widthInPixels')->params(['step' => 1]) + ), + $b->boxDimensions($prefix . 'margin')->enabled($condition), + $b->control(AlignmentSelector::class, $settings->findSetting($prefix . 'alignment')) + ->enabled($condition) + ); + $mainSection->add($subSection); + } + + $structure = $b->structure($mainSection); + return $structure->build(); + } + + private function getStyleGenerator(SeparatorStyleSettings $settings) { + $g = new StyleGenerator(); + $g->setStylesheetsToDisableOnPreview(['link#' . self::COLOR_STYLE_HANDLE . '-css']); + + $customSettingsEnabled = $settings->getSetting('customSettingsEnabled'); + + $this->addSeparatorRuleSetsFor( + $g, + $settings, + 'topLevelSeparators', + function ($key) use ($settings) { + return $settings->getSetting('topLevelSeparators.' . $key); + }, + '#adminmenumain #adminmenu li.wp-menu-separator .separator', + '#adminmenumain #adminmenu li.wp-menu-separator' + ); + + /* The expected DOM hierarchy for submenu separators is: + * li.wp-menu-separator.ws-submenu-separator-wrap > a.wp-menu-separator > hr.ws-submenu-separator + * + * The usual rule generator only handles the parent (li) and the separator body (hr), + * so we'll need to add a special rule for the middle element + */ + $g->addCondition( + $g->ifTruthy($customSettingsEnabled), + new CssRuleSet( + ['#adminmenumain #adminmenu .wp-submenu a.wp-menu-separator'], + ['padding' => 0, 'margin' => 0] + ) + ); + + //Submenus use either their own setting or top-level settings. To make that + //possible, let's add variables that will return either the top-level or submenu + //setting depending on the state of the useTopLevelSettingsForSubmenus setting. + $typeSettingNames = [ + 'colorType', + 'customColor', + 'borderStyle', + 'height', + 'widthStrategy', + 'widthInPercent', + 'widthInPixels', + 'alignment', + //Margins are handled separately because we need their children. + ]; + $useTopLevelSettings = $settings->getSetting('useTopLevelSettingsForSubmenus'); + $submenuVarPrefix = 'submenu_'; + foreach ($typeSettingNames as $name) { + $g->setVariable( + $submenuVarPrefix . $name, + $g->ifTruthy( + $useTopLevelSettings, + $g->cssValue($settings->getSetting('topLevelSeparators.' . $name)), + $g->cssValue($settings->getSetting('submenuSeparators.' . $name)) + ) + ); + } + //Margin settings have separate child settings for each side. + foreach (['top', 'bottom', 'left', 'right'] as $side) { + $g->setVariable( + $submenuVarPrefix . 'margin.' . $side, + $g->ifTruthy( + $useTopLevelSettings, + $g->cssValue($settings->getSetting('topLevelSeparators.margin.' . $side)), + $g->cssValue($settings->getSetting('submenuSeparators.margin.' . $side)) + ) + ); + } + + //Now we can add the submenu separator rules. + $this->addSeparatorRuleSetsFor( + $g, + $settings, + 'submenuSeparators', + function ($key) use ($g, $submenuVarPrefix) { + return $g->variable($submenuVarPrefix . $key); + }, + '#adminmenumain #adminmenu .wp-submenu .ws-submenu-separator', + '#adminmenumain #adminmenu .wp-submenu .ws-submenu-separator-wrap' + ); + + return $g; + } + + /** + * @param \YahnisElsts\AdminMenuEditor\StyleGenerator\StyleGenerator $g + * @param \YahnisElsts\AdminMenuEditor\MenuSeparatorStyles\SeparatorStyleSettings $s + * @param string $separatorType + * @param callable $valueGetter + * @param string $nodeSelector + * @param string $parentSelector + * @return void + */ + private function addSeparatorRuleSetsFor( + StyleGenerator $g, + SeparatorStyleSettings $s, + $separatorType, + $valueGetter, + $nodeSelector, + $parentSelector + ) { + $nodeSelector = trim($nodeSelector); + $parentSelector = trim($parentSelector); + + $customSettingsEnabled = $s->getSetting('customSettingsEnabled'); + + //If custom settings are enabled, always reset the margins, padding, and dimensions + //to avoid conflicts. + $g->addSimpleCondition( + $customSettingsEnabled, + '==', + true, + new CssRuleSet( + [$parentSelector], + [ + 'height' => 'auto', + 'margin' => '0', + 'padding' => '0', + 'width' => '100%', + ] + ) + ); + + //Determine the effective separator color. It defaults to "transparent" if that's + //the color type or if the custom color is empty. + $g->setVariable( + $separatorType . 'EffectiveColor', + $g->ifLooselyEqual( + $valueGetter('colorType'), + 'transparent', + 'transparent', + $g->ifLooselyEqual( + $valueGetter('customColor'), + '', + 'transparent', + $valueGetter('customColor') + ) + ) + ); + + //Border style and height. + $g->addCondition( + $g->ifAll([ + $customSettingsEnabled, + $g->ifLooselyEqual($valueGetter('borderStyle'), 'solid'), + ]), + new CssRuleSet( + [$nodeSelector], + [ + 'border' => 'none', + 'background-color' => $g->variable($separatorType . 'EffectiveColor'), + 'height' => $valueGetter('height'), + ] + ) + ); + $g->addCondition( + $g->ifAll([ + $customSettingsEnabled, + $g->compare($valueGetter('borderStyle'), '!=', 'solid'), + ]), + new CssRuleSet( + [$nodeSelector], + [ + 'border-top-style' => $valueGetter('borderStyle'), + 'border-top-width' => $valueGetter('height'), + 'height' => '0', + 'border-color' => $g->variable($separatorType . 'EffectiveColor'), + 'background' => 'transparent', + ] + ) + ); + + //Width. + $g->addCondition( + $g->ifAll([ + $customSettingsEnabled, + $g->ifLooselyEqual($valueGetter('widthStrategy'), 'percentage'), + ]), + new CssRuleSet( + [$nodeSelector], + ['width' => $valueGetter('widthInPercent')] + ) + ); + $g->addCondition( + $g->ifAll([ + $customSettingsEnabled, + $g->ifLooselyEqual($valueGetter('widthStrategy'), 'fixed'), + ]), + new CssRuleSet( + [$nodeSelector], + ['width' => $valueGetter('widthInPixels')] + ) + ); + + //Margins and alignment. + //Left and right margins should be "auto" when the separator is centered and not full-width. + foreach (['left', 'right'] as $side) { + $g->setVariable( + $separatorType . '_' . $side . 'Margin', + $g->ifAll( + [ + $g->compare($valueGetter('widthStrategy'), '!=', 'full'), + $g->ifLooselyEqual($valueGetter('alignment'), 'center'), + ], + 'auto', + $valueGetter('margin.' . $side) + ) + ); + } + + $g->addCondition( + $g->ifTruthy($customSettingsEnabled), + new CssRuleSet( + [$nodeSelector], + [ + 'margin-top' => $valueGetter('margin.top'), + 'margin-bottom' => $valueGetter('margin.bottom'), + 'margin-left' => $g->variable($separatorType . '_leftMargin'), + 'margin-right' => $g->variable($separatorType . '_rightMargin'), + ] + ) + ); + + //Left and right alignment. + $g->addCondition( + $g->ifAll([ + $customSettingsEnabled, + $g->compare($valueGetter('widthStrategy'), '!=', 'full'), + $g->ifSome([ + $g->ifLooselyEqual($valueGetter('alignment'), 'left'), + $g->ifLooselyEqual($valueGetter('alignment'), 'right'), + ]), + ]), + new CssRuleSet( + [$nodeSelector], + ['float' => $valueGetter('alignment')] + ), + //Clear floats. + new CssRuleSet( + [$parentSelector . '::after'], + [ + 'content' => '""', + 'display' => 'block', + 'clear' => 'both', + 'height' => '0', + ] + ) + ); + } + + public function addAuxDataConfig($config) { + $config['keys'][SeparatorStyleSettings::CONFIG_KEY] = SeparatorStyleSettings::SETTING_ID_PREFIX; + return $config; + } + + /** + * @param \YahnisElsts\AdminMenuEditor\Customizable\Builders\InterfaceBuilder $structure + * @return void + */ + public function addSeparatorsToMenuStyler($structure) { + $myStructure = $this->getInterfaceStructure(); + $separatorSection = $myStructure->findChildById('ame-sep-Separators-section'); + if ( $separatorSection instanceof Container ) { + $structure->addAfter($separatorSection, 'ame-ms-Submenus-section'); + } + } + + public function addDefaultsToMenuStyler($defaults) { + return array_merge($defaults, $this->getSettings()->getRecursiveDefaultsForJs()); + } + + public function addStyleGeneratorToMenuStyler($styleGenerators) { + $styleGenerators[] = $this->getStyleGenerator($this->getSettings()); + return $styleGenerators; + } + + public function registerSeparatorStylesheet() { + $helper = MenuScopedStylesheetHelper::getInstance($this->menuEditor); + + $helper->addStylesheet( + self::COLOR_STYLE_HANDLE, + function ($menuConfigId) { + return [ + function () use ($menuConfigId) { + $customMenu = $this->menuEditor->load_custom_menu($menuConfigId); + return isset($customMenu, $customMenu['separator_css_modified']) + ? max(intval($customMenu['separator_css_modified']), 0) + : 0; + }, + function () use ($menuConfigId) { + //In the preview frame, this should use the already-registered + //settings instead of loading them from the menu configuration. + if ( + apply_filters('admin_menu_editor-is_preview_frame', false) + && isset($this->settings) + ) { + $settings = $this->settings; + } else { + $settings = $this->getSettings($menuConfigId); + } + $generator = $this->getStyleGenerator($settings); + return $generator->generateCss(); + }, + ]; + }, + 'ame-menu-style-bundle' + ); + } +} + +class SeparatorStyleSettings extends AbstractSettingsDictionary { + const SETTING_ID_PREFIX = 'ws_separator_styles--'; + + const CONFIG_KEY = 'separators'; + + const SEPARATOR_TYPE_KEYS = ['topLevelSeparators', 'submenuSeparators']; + + private static $marginAliases = null; + + public function __construct(StorageInterface $store, $lastModifiedTimeEnabled = false) { + parent::__construct($store, self::SETTING_ID_PREFIX, $lastModifiedTimeEnabled); + + //Margins were previously stored in keys like "marginTop" instead of + //a single "margins" structure. Let's add aliases for the old keys. + $this->addReadAliases($this->getMarginAliases()); + } + + /** + * @return array + */ + public static function getMarginAliases() { + if ( isset(self::$marginAliases) ) { + return self::$marginAliases; + } + + self::$marginAliases = []; + foreach (self::SEPARATOR_TYPE_KEYS as $separatorType) { + self::$marginAliases = array_merge(self::$marginAliases, [ + $separatorType . '.margin.top' => $separatorType . '.marginTop', + $separatorType . '.margin.bottom' => $separatorType . '.marginBottom', + $separatorType . '.margin.left' => $separatorType . '.marginLeft', + $separatorType . '.margin.right' => $separatorType . '.marginRight', + ]); + } + return self::$marginAliases; + } + + protected function createDefaults() { + return []; + } + + protected function createSettings() { + $f = $this->settingFactory(); + $f->enablePostMessageSupport(); + $settings = [ + $f->boolean('customSettingsEnabled', 'Use custom separator styles'), + $f->boolean('useTopLevelSettingsForSubmenus', 'Use the same settings as top level separators'), + ]; + + foreach (self::SEPARATOR_TYPE_KEYS as $separatorType) { + $settings[] = $f->customStruct( + $separatorType, + function (SettingFactory $cf) { + $cf->enablePostMessageSupport(); + return [ + $cf->stringEnum( + 'colorType', + ['transparent', 'custom'], + 'Color', + ['default' => 'transparent'] + ), + $cf->cssColor('customColor', 'border-color', 'Custom color'), + $cf->cssEnum( + 'borderStyle', + 'border-style', + ['solid', 'dashed', 'double', 'dotted',], + 'Line style', + ['default' => 'solid'] + ) + ->describeChoice('solid', 'Solid') + ->describeChoice('dashed', 'Dashed') + ->describeChoice('double', 'Double') + ->describeChoice('dotted', 'Dotted'), + $cf->cssLength( + 'height', + 'Height', + 'height', + [ + 'defaultUnit' => 'px', + 'default' => 5, + 'minValue' => 1, + 'maxValue' => 100, + ] + ), + $cf->stringEnum( + 'widthStrategy', + ['full', 'percentage', 'fixed'], + 'Width', + ['default' => 'full'] + ) + ->describeChoice('full', 'Full width') + ->describeChoice('percentage', 'Percentage') + ->describeChoice('fixed', 'Fixed width'), + $cf->cssLength( + 'widthInPercent', + 'Width in percent', + 'width', + [ + 'defaultUnit' => '%', + 'default' => 100, + 'minValue' => 1, + 'maxValue' => 100, + ] + ), + $cf->cssLength( + 'widthInPixels', + 'Width in pixels', + 'width', + [ + 'defaultUnit' => 'px', + 'default' => 160, + 'minValue' => 1, + 'maxValue' => 300, + ] + ), + $cf->create( + Margins::class, + 'margin', + 'Margins', + [ + 'dimensionDefaults' => [ + 'top' => 0, + 'bottom' => 6, + 'left' => 0, + 'right' => 0, + ], + ] + ), + $cf->stringEnum( + 'alignment', + ['none', 'left', 'center', 'right'], + 'Alignment', + ['default' => 'none'] + ), + ]; + } + ); + } + + return $settings; + } +} \ No newline at end of file diff --git a/extras/modules/super-users/super-users.js b/extras/modules/super-users/super-users.js index 96692a8..fad071f 100644 --- a/extras/modules/super-users/super-users.js +++ b/extras/modules/super-users/super-users.js @@ -1,60 +1,59 @@ +"use strict"; /// /// /// /// /// -var AmeSuperUsers = /** @class */ (function () { - function AmeSuperUsers(settings) { - var _this = this; +class AmeSuperUsers { + constructor(settings) { this.addButtonText = 'Add User'; this.userEditUrl = settings.userEditUrl; this.currentUserLogin = settings.currentUserLogin; this.superUsers = ko.observableArray([]); - AmeSuperUsers._.forEach(settings.superUsers, function (userDetails) { + AmeSuperUsers._.forEach(settings.superUsers, (userDetails) => { var user = AmeUser.createFromProperties(userDetails); if (!AmeActors.getUser(user.userLogin)) { AmeActors.addUsers([user]); } - _this.superUsers.push(user); + this.superUsers.push(user); }); this.superUsers.sort(AmeSuperUsers.compareLogins); - this.settingsData = ko.computed(function () { - return AmeSuperUsers._.map(_this.superUsers(), 'userId').join(','); + this.settingsData = ko.computed(() => { + return AmeSuperUsers._.map(this.superUsers(), 'userId').join(','); }); //Store the state of the info box in a cookie. - var initialState = jQuery.cookie('ame_su_info_box_open'); - var _isBoxOpen = ko.observable((typeof initialState === 'undefined') ? true : (initialState === '1')); + let initialState = jQuery.cookie('ame_su_info_box_open'); + let _isBoxOpen = ko.observable((typeof initialState === 'undefined') ? true : (initialState === '1')); this.isInfoBoxOpen = ko.computed({ - read: function () { + read: () => { return _isBoxOpen(); }, - write: function (value) { + write: (value) => { jQuery.cookie('ame_su_info_box_open', value ? '1' : '0', { expires: 90 }); _isBoxOpen(value); } }); } - AmeSuperUsers.prototype.removeUser = function (user) { + removeUser(user) { this.superUsers.remove(user); - }; - AmeSuperUsers.prototype.getEditLink = function (user) { + } + getEditLink(user) { return this.userEditUrl + '?user_id=' + user.userId; - }; - AmeSuperUsers.prototype.selectHiddenUsers = function () { - var _this = this; + } + selectHiddenUsers() { AmeSelectUsersDialog.open({ selectedUsers: AmeSuperUsers._.map(this.superUsers(), 'userLogin'), users: AmeSuperUsers._.indexBy(this.superUsers(), 'userLogin'), actorManager: AmeActors, currentUserLogin: this.currentUserLogin, alwaysIncludeCurrentUser: false, - save: function (selectedUsers) { + save: (selectedUsers) => { selectedUsers.sort(AmeSuperUsers.compareLogins); - _this.superUsers(selectedUsers); + this.superUsers(selectedUsers); } }); - }; - AmeSuperUsers.compareLogins = function (a, b) { + } + static compareLogins(a, b) { if (a.userLogin > b.userLogin) { return 1; } @@ -62,9 +61,9 @@ var AmeSuperUsers = /** @class */ (function () { return -1; } return 0; - }; - AmeSuperUsers.prototype.formatUserRoles = function (user) { - var displayNames = AmeSuperUsers._.map(user.roles, function (roleId) { + } + formatUserRoles(user) { + let displayNames = AmeSuperUsers._.map(user.roles, (roleId) => { var actor = AmeActors.getActor('role:' + roleId); if (actor) { return actor.displayName; @@ -74,13 +73,12 @@ var AmeSuperUsers = /** @class */ (function () { } }); return displayNames.join(', '); - }; - AmeSuperUsers.prototype.toggleInfoBox = function () { + } + toggleInfoBox() { this.isInfoBoxOpen(!this.isInfoBoxOpen()); - }; - AmeSuperUsers._ = wsAmeLodash; - return AmeSuperUsers; -}()); + } +} +AmeSuperUsers._ = wsAmeLodash; jQuery(function () { var superUserVM = new AmeSuperUsers(wsAmeSuperUserSettings); ko.applyBindings(superUserVM, document.getElementById('ame-super-user-settings')); diff --git a/extras/modules/super-users/super-users.js.map b/extras/modules/super-users/super-users.js.map index 9f9285a..ac3a1c6 100644 --- a/extras/modules/super-users/super-users.js.map +++ b/extras/modules/super-users/super-users.js.map @@ -1 +1 @@ -{"version":3,"file":"super-users.js","sourceRoot":"","sources":["super-users.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"super-users.js","sourceRoot":"","sources":["super-users.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/super-users/super-users.php b/extras/modules/super-users/super-users.php index dd3c91f..252927d 100644 --- a/extras/modules/super-users/super-users.php +++ b/extras/modules/super-users/super-users.php @@ -193,7 +193,7 @@ public function enqueueTabScripts() { wp_enqueue_auto_versioned_script( 'ame-super-users', plugins_url('super-users.js', __FILE__), - array('knockout', 'jquery', 'ame-visible-users', 'ame-actor-manager', 'ame-jquery-cookie') + array('ame-knockout', 'jquery', 'ame-visible-users', 'ame-actor-manager', 'ame-jquery-cookie') ); //Pass users to JS. diff --git a/extras/modules/super-users/super-users.ts b/extras/modules/super-users/super-users.ts index b67e606..4b058f0 100644 --- a/extras/modules/super-users/super-users.ts +++ b/extras/modules/super-users/super-users.ts @@ -18,11 +18,11 @@ class AmeSuperUsers { public addButtonText: string = 'Add User'; public isInfoBoxOpen: KnockoutComputed; - constructor(settings) { + constructor(settings: any) { this.userEditUrl = settings.userEditUrl; this.currentUserLogin = settings.currentUserLogin; - this.superUsers = ko.observableArray([]); + this.superUsers = ko.observableArray([] as AmeUser[]); AmeSuperUsers._.forEach(settings.superUsers, (userDetails) => { var user = AmeUser.createFromProperties(userDetails); if (!AmeActors.getUser(user.userLogin)) { diff --git a/extras/modules/tweaks/ameGutenbergBlockManager.php b/extras/modules/tweaks/ameGutenbergBlockManager.php index 5021f72..cae4f47 100644 --- a/extras/modules/tweaks/ameGutenbergBlockManager.php +++ b/extras/modules/tweaks/ameGutenbergBlockManager.php @@ -48,7 +48,7 @@ public function enqueueGutenbergAssets() { wp_enqueue_script( self::SCRIPT_HANDLE, plugins_url('gutenberg-block-detector.js', __FILE__), - array('wp-blocks', 'wp-dom-ready', 'wp-edit-post', 'jquery'), + array('jquery', 'wp-dom-ready'), '20210218-4', true ); diff --git a/extras/modules/tweaks/default-tweaks.php b/extras/modules/tweaks/default-tweaks.php index 4ef6032..ac3d962 100644 --- a/extras/modules/tweaks/default-tweaks.php +++ b/extras/modules/tweaks/default-tweaks.php @@ -37,7 +37,8 @@ 'hide-gutenberg-options' => array( 'label' => 'Hide the Gutenberg options menu (three vertical dots)', - 'selector' => '#editor .edit-post-header__settings .edit-post-more-menu', + 'selector' => '#editor .edit-post-header__settings .edit-post-more-menu,' + .' #editor .edit-post-header__settings .interface-more-menu-dropdown', ), 'hide-gutenberg-fs-wp-logo' => array( 'label' => 'Hide the WordPress logo in Gutenberg fullscreen mode', diff --git a/extras/modules/tweaks/gutenberg-block-detector.js b/extras/modules/tweaks/gutenberg-block-detector.js index 74af337..895b9fb 100644 --- a/extras/modules/tweaks/gutenberg-block-detector.js +++ b/extras/modules/tweaks/gutenberg-block-detector.js @@ -34,6 +34,14 @@ if (typeof wp !== 'undefined' && typeof wp.domReady !== 'undefined') { //Wait for Gutenberg to load. loadGutenberg.then(function () { setTimeout(function () { + if ( + (typeof wp.blocks === 'undefined') + || (typeof wp.blocks.getBlockTypes === 'undefined') + || (typeof wp.blocks.getCategories === 'undefined') + ) { + return; + } + let hasNewData = false; //We're using arrays instead of objects because we want to preserve item order. diff --git a/extras/modules/tweaks/tweak-manager.js b/extras/modules/tweaks/tweak-manager.js index 77c3a2c..59d9db5 100644 --- a/extras/modules/tweaks/tweak-manager.js +++ b/extras/modules/tweaks/tweak-manager.js @@ -1,92 +1,66 @@ +"use strict"; /// /// /// /// /// /// -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var AmeNamedNode = /** @class */ (function () { - function AmeNamedNode(properties) { +let ameTweakManager; +class AmeNamedNode { + constructor(properties) { this.htmlId = ''; this.id = properties.id; this.label = properties.label; } - return AmeNamedNode; -}()); +} function isAmeSettingsGroupProperties(thing) { - var group = thing; + const group = thing; return (typeof group.children !== 'undefined'); } function isAmeSettingProperties(thing) { return (typeof thing.dataType === 'string'); } -var AmeSetting = /** @class */ (function (_super) { - __extends(AmeSetting, _super); - function AmeSetting(properties, store, path) { - if (path === void 0) { path = []; } - var _this = _super.call(this, properties) || this; - var defaultValue = null; +class AmeSetting extends AmeNamedNode { + constructor(properties, store, path = []) { + super(properties); + // noinspection JSUnusedGlobalSymbols Used in Knockout templates. + this.templateName = ''; + let defaultValue = null; if (typeof properties.defaultValue !== 'undefined') { defaultValue = properties.defaultValue; } - _this.inputValue = store.getObservableProperty(properties.id, defaultValue, path); + this.inputValue = store.getObservableProperty(properties.id, defaultValue, path); AmeSetting.idCounter++; - _this.uniqueInputId = 'ws-ame-gen-setting-' + AmeSetting.idCounter; - return _this; - } - AmeSetting.idCounter = 0; - return AmeSetting; -}(AmeNamedNode)); -var AmeStringSetting = /** @class */ (function (_super) { - __extends(AmeStringSetting, _super); - function AmeStringSetting(properties, module, store, path) { - if (path === void 0) { path = []; } - var _this = _super.call(this, properties, store, path) || this; - _this.syntaxHighlightingOptions = null; - _this.templateName = 'ame-tweak-textarea-input-template'; + this.uniqueInputId = 'ws-ame-gen-setting-' + AmeSetting.idCounter; + } +} +AmeSetting.idCounter = 0; +class AmeStringSetting extends AmeSetting { + constructor(properties, module, store, path = []) { + super(properties, store, path); + this.syntaxHighlightingOptions = null; + this.templateName = 'ame-tweak-textarea-input-template'; if (properties.syntaxHighlighting && module) { - _this.syntaxHighlightingOptions = module.getCodeMirrorOptions(properties.syntaxHighlighting); - } - return _this; - } - return AmeStringSetting; -}(AmeSetting)); -var AmeColorSetting = /** @class */ (function (_super) { - __extends(AmeColorSetting, _super); - function AmeColorSetting(properties, store, path) { - if (path === void 0) { path = []; } - var _this = _super.call(this, properties, store, path) || this; - _this.templateName = 'ame-tweak-color-input-template'; - return _this; - } - return AmeColorSetting; -}(AmeSetting)); -var AmeBooleanSetting = /** @class */ (function (_super) { - __extends(AmeBooleanSetting, _super); - function AmeBooleanSetting(properties, store, path) { - if (path === void 0) { path = []; } - var _this = _super.call(this, properties, store, path) || this; - _this.templateName = 'ame-tweak-boolean-input-template'; + this.syntaxHighlightingOptions = module.getCodeMirrorOptions(properties.syntaxHighlighting); + } + } +} +class AmeColorSetting extends AmeSetting { + constructor(properties, store, path = []) { + super(properties, store, path); + this.templateName = 'ame-tweak-color-input-template'; + } +} +class AmeBooleanSetting extends AmeSetting { + constructor(properties, store, path = []) { + super(properties, store, path); + this.templateName = 'ame-tweak-boolean-input-template'; //Ensure that the value is always a boolean. - var _internalValue = _this.inputValue; + let _internalValue = this.inputValue; if (typeof _internalValue() !== 'boolean') { _internalValue(!!_internalValue()); } - _this.inputValue = ko.computed({ + this.inputValue = ko.computed({ read: function () { return _internalValue(); }, @@ -96,35 +70,31 @@ var AmeBooleanSetting = /** @class */ (function (_super) { } _internalValue(newValue); }, - owner: _this + owner: this }); - return _this; } - return AmeBooleanSetting; -}(AmeSetting)); +} function isAmeActorFeatureProperties(thing) { return (typeof thing.hasAccessMap === 'boolean'); } -var AmeSettingStore = /** @class */ (function () { - function AmeSettingStore(initialProperties) { - if (initialProperties === void 0) { initialProperties = {}; } +class AmeSettingStore { + constructor(initialProperties = {}) { this.observableProperties = {}; this.accessMaps = {}; this.initialProperties = initialProperties; } - AmeSettingStore.prototype.getObservableProperty = function (name, defaultValue, path) { - if (path === void 0) { path = []; } + getObservableProperty(name, defaultValue, path = []) { path = this.getFullPath(name, path); if (this.observableProperties.hasOwnProperty(path)) { return this.observableProperties[path]; } - var _ = AmeTweakManagerModule._; - var value = _.get(this.initialProperties, path, defaultValue); - var observable = ko.observable(value); + const _ = AmeTweakManagerModule._; + const value = _.get(this.initialProperties, path, defaultValue); + const observable = ko.observable(value); this.observableProperties[path] = observable; return observable; - }; - AmeSettingStore.prototype.getFullPath = function (name, path) { + } + getFullPath(name, path) { if (typeof path !== 'string') { path = path.join('.'); } @@ -135,27 +105,33 @@ var AmeSettingStore = /** @class */ (function () { path = path + '.' + name; } return path; - }; - AmeSettingStore.prototype.propertiesToJs = function () { - var _ = AmeTweakManagerModule._; - var newProps = {}; + } + propertiesToJs() { + const _ = AmeTweakManagerModule._; + let newProps = {}; _.forOwn(this.observableProperties, function (observable, path) { + if (typeof path === 'undefined') { + return; + } _.set(newProps, path, observable()); }); _.forOwn(this.accessMaps, function (map, path) { + if (typeof path === 'undefined') { + return; + } //Since all tweaks are disabled by default, having a tweak disabled for a role is the same //as not having a setting, so we can save some space by removing it. This does not always //apply to users/Super Admins because they can have precedence over roles. - var temp = map.getAll(); - var enabled = {}; - var areAllFalse = true; - for (var actorId in temp) { + let temp = map.getAll(); + let enabled = {}; + let areAllFalse = true; + for (let actorId in temp) { if (!temp.hasOwnProperty(actorId)) { continue; } areAllFalse = areAllFalse && (!temp[actorId]); if (!temp[actorId]) { - var actor = AmeActors.getActor(actorId); + const actor = AmeActors.getActor(actorId); if (actor instanceof AmeRole) { continue; } @@ -168,77 +144,74 @@ var AmeSettingStore = /** @class */ (function () { _.set(newProps, path, enabled); }); return newProps; - }; - AmeSettingStore.prototype.getAccessMap = function (name, path, defaultAccessMap) { - if (path === void 0) { path = []; } - if (defaultAccessMap === void 0) { defaultAccessMap = null; } + } + getAccessMap(name, path = [], defaultAccessMap = null) { path = this.getFullPath(name, path); - var _ = AmeTweakManagerModule._; - var value = _.get(this.initialProperties, path, defaultAccessMap); + const _ = AmeTweakManagerModule._; + const value = _.get(this.initialProperties, path, defaultAccessMap); if (!this.accessMaps.hasOwnProperty(path)) { - this.accessMaps[path] = new AmeObservableActorSettings(value); + this.accessMaps[path] = new AmeObservableActorFeatureMap(value); } return this.accessMaps[path]; - }; - return AmeSettingStore; -}()); + } +} function isSettingStore(thing) { - var maybe = thing; + const maybe = thing; return (typeof maybe.getObservableProperty !== 'undefined') && (typeof maybe.propertiesToJs !== 'undefined'); } -var AmeCompositeNode = /** @class */ (function (_super) { - __extends(AmeCompositeNode, _super); - function AmeCompositeNode(properties, module, store, path) { - if (store === void 0) { store = null; } - if (path === void 0) { path = []; } - var _this = _super.call(this, properties) || this; - _this.children = null; - _this.id = properties.id; - _this.label = properties.label; +class AmeCompositeNode extends AmeNamedNode { + constructor(properties, module, store, path = []) { + super(properties); + this.propertyPath = []; + this.actorAccess = null; + this.properties = null; + this.id = properties.id; + this.label = properties.label; if (store === 'self') { - if (!_this.properties) { - _this.properties = new AmeSettingStore(properties); + if (!this.properties) { + this.properties = new AmeSettingStore(properties); } - store = _this.properties; + store = this.properties; } if (isAmeSettingsGroupProperties(properties)) { if ((typeof properties.propertyPath === 'string') && (properties.propertyPath !== '')) { - _this.propertyPath = properties.propertyPath.split('.'); + this.propertyPath = properties.propertyPath.split('.'); } else { - _this.propertyPath = []; + this.propertyPath = []; } if (path.length > 0) { - _this.propertyPath = path.concat(_this.propertyPath); + this.propertyPath = path.concat(this.propertyPath); } - var children = []; + let children = []; if (properties.children && (properties.children.length > 0)) { - for (var i = 0; i < properties.children.length; i++) { - var props = properties.children[i]; - var child = void 0; + for (let i = 0; i < properties.children.length; i++) { + const props = properties.children[i]; + let child; if (isAmeSettingProperties(props)) { - child = AmeCompositeNode.createSetting(props, module, store, _this.propertyPath); + child = AmeCompositeNode.createSetting(props, module, store, this.propertyPath); } else { - child = new AmeCompositeNode(props, module, store, _this.propertyPath); + child = new AmeCompositeNode(props, module, store, this.propertyPath); } if (child) { children.push(child); } } } - _this.children = ko.observableArray(children); + this.children = ko.observableArray(children); + } + else { + this.children = ko.observableArray([]); } if (isAmeActorFeatureProperties(properties)) { - var name_1 = (store === _this.properties) ? 'enabledForActor' : _this.id; - var defaultAccess = (typeof properties.defaultAccessMap !== 'undefined') ? properties.defaultAccessMap : null; - _this.actorAccess = new AmeActorAccess(store.getAccessMap(name_1, path, defaultAccess), module, _this.children); + let name = (store === this.properties) ? 'enabledForActor' : this.id; + const defaultAccess = (typeof properties.defaultAccessMap !== 'undefined') ? properties.defaultAccessMap : null; + this.actorAccess = new AmeActorAccess(store.getAccessMap(name, path, defaultAccess), module, this.children); } - return _this; } - AmeCompositeNode.createSetting = function (properties, module, store, path) { - if (path === void 0) { path = []; } - var inputType = properties.inputType ? properties.inputType : properties.dataType; + static createSetting(properties, module, store, path = []) { + const inputType = properties.inputType ? properties.inputType : properties.dataType; switch (inputType) { case 'text': case 'textarea': @@ -254,31 +227,28 @@ var AmeCompositeNode = /** @class */ (function (_super) { } return null; } - }; - return AmeCompositeNode; -}(AmeNamedNode)); -var AmeActorAccess = /** @class */ (function () { - function AmeActorAccess(actorSettings, module, children) { - if (children === void 0) { children = null; } - var _this = this; + } +} +class AmeActorAccess { + constructor(actorSettings, module, children = null) { this.module = module; this.enabledForActor = actorSettings; - var _isIndeterminate = ko.observable(false); - this.isIndeterminate = ko.computed(function () { + let _isIndeterminate = ko.observable(false); + this.isIndeterminate = ko.computed(() => { if (module.selectedActor() !== null) { return false; } return _isIndeterminate(); }); this.isChecked = ko.computed({ - read: function () { - var selectedActor = _this.module.selectedActor(); + read: () => { + const selectedActor = this.module.selectedActor(); if (selectedActor === null) { //All: Checked only if it's checked for all actors. - var allActors = _this.module.actorSelector.getVisibleActors(); - var isEnabledForAll = true, isEnabledForAny = false; - for (var index = 0; index < allActors.length; index++) { - if (_this.enabledForActor.get(allActors[index].getId(), false)) { + const allActors = this.module.actorSelector.getVisibleActors(); + let isEnabledForAll = true, isEnabledForAny = false; + for (let index = 0; index < allActors.length; index++) { + if (this.enabledForActor.get(allActors[index].getId(), false)) { isEnabledForAny = true; } else { @@ -289,21 +259,21 @@ var AmeActorAccess = /** @class */ (function () { return isEnabledForAll; } //Is there an explicit setting for this actor? - var ownSetting = _this.enabledForActor.get(selectedActor.getId(), null); + let ownSetting = this.enabledForActor.get(selectedActor.getId(), null); if (ownSetting !== null) { return ownSetting; } if (selectedActor instanceof AmeUser) { //The "Super Admin" setting takes precedence over regular roles. if (selectedActor.isSuperAdmin) { - var superAdminSetting = _this.enabledForActor.get(AmeSuperAdmin.permanentActorId, null); + let superAdminSetting = this.enabledForActor.get(AmeSuperAdmin.permanentActorId, null); if (superAdminSetting !== null) { return superAdminSetting; } } //Is it enabled for any of the user's roles? - for (var i = 0; i < selectedActor.roles.length; i++) { - var groupSetting = _this.enabledForActor.get('role:' + selectedActor.roles[i], null); + for (let i = 0; i < selectedActor.roles.length; i++) { + let groupSetting = this.enabledForActor.get('role:' + selectedActor.roles[i], null); if (groupSetting === true) { return true; } @@ -312,29 +282,29 @@ var AmeActorAccess = /** @class */ (function () { //All tweaks are unchecked by default. return false; }, - write: function (checked) { - var selectedActor = _this.module.selectedActor(); + write: (checked) => { + const selectedActor = this.module.selectedActor(); if (selectedActor === null) { //Enable/disable this tweak for all actors. if (checked === false) { //Since false is the default, this is the same as removing/resetting all values. - _this.enabledForActor.resetAll(); + this.enabledForActor.resetAll(); } else { - var allActors = _this.module.actorSelector.getVisibleActors(); - for (var i = 0; i < allActors.length; i++) { - _this.enabledForActor.set(allActors[i].getId(), checked); + const allActors = this.module.actorSelector.getVisibleActors(); + for (let i = 0; i < allActors.length; i++) { + this.enabledForActor.set(allActors[i].getId(), checked); } } } else { - _this.enabledForActor.set(selectedActor.getId(), checked); + this.enabledForActor.set(selectedActor.getId(), checked); } //Apply the same setting to all children. if (children) { - var childrenArray = children(); - for (var i = 0; i < childrenArray.length; i++) { - var child = childrenArray[i]; + const childrenArray = children(); + for (let i = 0; i < childrenArray.length; i++) { + const child = childrenArray[i]; if ((child instanceof AmeCompositeNode) && child.actorAccess) { child.actorAccess.isChecked(checked); } @@ -343,35 +313,32 @@ var AmeActorAccess = /** @class */ (function () { } }); } - return AmeActorAccess; -}()); -var AmeTweakItem = /** @class */ (function (_super) { - __extends(AmeTweakItem, _super); - function AmeTweakItem(properties, module) { - var _this = _super.call(this, properties, module, 'self') || this; - _this.initialProperties = null; - _this.section = null; - _this.parent = null; - _this.isUserDefined = properties.isUserDefined ? properties.isUserDefined : false; - if (_this.isUserDefined) { - _this.initialProperties = properties; - } - if (_this.isUserDefined) { - _this.label = ko.observable(properties.label); +} +class AmeTweakItem extends AmeCompositeNode { + constructor(properties, module) { + super(properties, module, 'self'); + this.initialProperties = null; + this.section = null; + this.parent = null; + this.isUserDefined = properties.isUserDefined ? properties.isUserDefined : false; + if (this.isUserDefined) { + this.initialProperties = properties; + } + if (this.isUserDefined) { + this.label = ko.observable(properties.label); } else { - _this.label = ko.pureComputed(function () { + this.label = ko.pureComputed(function () { return properties.label; }); } - _this.htmlId = 'ame-tweak-' + AmeTweakManagerModule.slugify(_this.id); - return _this; + this.htmlId = 'ame-tweak-' + AmeTweakManagerModule.slugify(this.id); } - AmeTweakItem.prototype.toJs = function () { - var result = { + toJs() { + let result = { id: this.id }; - var _ = AmeTweakManagerModule._; + const _ = AmeTweakManagerModule._; if (this.properties) { result = _.defaults(result, this.properties.propertiesToJs()); } @@ -379,88 +346,89 @@ var AmeTweakItem = /** @class */ (function (_super) { return result; } else { - var props = result; + let props = result; props.isUserDefined = this.isUserDefined; props.label = this.label(); props.sectionId = this.section ? this.section.id : null; props.parentId = this.parent ? this.parent.id : null; - props = _.defaults(props, _.omit(this.initialProperties, 'userInputValue', 'enabledForActor')); + if (this.initialProperties !== null) { + props = _.defaults(props, _.omit(this.initialProperties, 'userInputValue', 'enabledForActor')); + } return props; } - }; - AmeTweakItem.prototype.setSection = function (section) { + } + setSection(section) { this.section = section; return this; - }; - AmeTweakItem.prototype.setParent = function (tweak) { + } + setParent(tweak) { this.parent = tweak; return this; - }; - AmeTweakItem.prototype.getSection = function () { + } + getSection() { return this.section; - }; - AmeTweakItem.prototype.getParent = function () { + } + getParent() { return this.parent; - }; - AmeTweakItem.prototype.addChild = function (tweak) { + } + addChild(tweak) { this.children.push(tweak); tweak.setParent(this); return this; - }; - AmeTweakItem.prototype.removeChild = function (tweak) { + } + removeChild(tweak) { this.children.remove(tweak); - }; - AmeTweakItem.prototype.getEditableProperty = function (key) { + } + getEditableProperty(key) { if (this.properties) { return this.properties.getObservableProperty(key, ''); } - }; - AmeTweakItem.prototype.getTypeId = function () { + return null; + } + getTypeId() { if (!this.isUserDefined || !this.initialProperties) { return null; } - if (this.initialProperties.typeId) { - return this.initialProperties.typeId; + const tweakProps = this.initialProperties; + if ((typeof tweakProps.typeId === 'string') && (tweakProps.typeId !== '')) { + return tweakProps.typeId; } return null; - }; - return AmeTweakItem; -}(AmeCompositeNode)); -var AmeTweakSection = /** @class */ (function () { - function AmeTweakSection(properties) { + } +} +class AmeTweakSection { + constructor(properties) { this.footerTemplateName = null; this.id = properties.id; this.label = properties.label; this.isOpen = ko.observable(true); this.tweaks = ko.observableArray([]); } - AmeTweakSection.prototype.addTweak = function (tweak) { + addTweak(tweak) { this.tweaks.push(tweak); tweak.setSection(this); - }; - AmeTweakSection.prototype.removeTweak = function (tweak) { + } + removeTweak(tweak) { this.tweaks.remove(tweak); - }; - AmeTweakSection.prototype.hasContent = function () { + } + hasContent() { return this.tweaks().length > 0; - }; - AmeTweakSection.prototype.toggle = function () { + } + toggle() { this.isOpen(!this.isOpen()); - }; - return AmeTweakSection; -}()); -var AmeTweakManagerModule = /** @class */ (function () { - function AmeTweakManagerModule(scriptData) { - var _this = this; + } +} +class AmeTweakManagerModule { + constructor(scriptData) { this.tweaksById = {}; this.sectionsById = {}; this.sections = []; this.lastUserTweakSuffix = 0; - var _ = AmeTweakManagerModule._; + const _ = AmeTweakManagerModule._; this.actorSelector = new AmeActorSelector(AmeActors, scriptData.isProVersion); this.selectedActorId = this.actorSelector.createKnockoutObservable(ko); - this.selectedActor = ko.computed(function () { - var id = _this.selectedActorId(); + this.selectedActor = ko.computed(() => { + const id = this.selectedActorId(); if (id === null) { return null; } @@ -478,24 +446,24 @@ var AmeTweakManagerModule = /** @class */ (function () { } }); //Sort sections by priority, then by label. - var sectionData = _.sortByAll(scriptData.sections, ['priority', 'label']); + let sectionData = _.sortByAll(scriptData.sections, ['priority', 'label']); //Register sections. - _.forEach(sectionData, function (properties) { - var section = new AmeTweakSection(properties); - _this.sectionsById[section.id] = section; - _this.sections.push(section); + _.forEach(sectionData, (properties) => { + let section = new AmeTweakSection(properties); + this.sectionsById[section.id] = section; + this.sections.push(section); }); - var firstSection = this.sections[0]; - _.forEach(scriptData.tweaks, function (properties) { - var tweak = new AmeTweakItem(properties, _this); - _this.tweaksById[tweak.id] = tweak; - if (properties.parentId && _this.tweaksById.hasOwnProperty(properties.parentId)) { - _this.tweaksById[properties.parentId].addChild(tweak); + const firstSection = this.sections[0]; + _.forEach(scriptData.tweaks, (properties) => { + const tweak = new AmeTweakItem(properties, this); + this.tweaksById[tweak.id] = tweak; + if (properties.parentId && this.tweaksById.hasOwnProperty(properties.parentId)) { + this.tweaksById[properties.parentId].addChild(tweak); } else { - var ownerSection = firstSection; - if (properties.sectionId && _this.sectionsById.hasOwnProperty(properties.sectionId)) { - ownerSection = _this.sectionsById[properties.sectionId]; + let ownerSection = firstSection; + if (properties.sectionId && this.sectionsById.hasOwnProperty(properties.sectionId)) { + ownerSection = this.sectionsById[properties.sectionId]; } ownerSection.addTweak(tweak); } @@ -511,27 +479,27 @@ var AmeTweakManagerModule = /** @class */ (function () { //By default, all sections except the first one are closed. //The user can open/close sections and we automatically remember their state. this.openSectionIds = ko.computed({ - read: function () { - var result = []; - _.forEach(_this.sections, function (section) { + read: () => { + let result = []; + _.forEach(this.sections, section => { if (section.isOpen()) { result.push(section.id); } }); return result; }, - write: function (sectionIds) { - var openSections = _.indexBy(sectionIds); - _.forEach(_this.sections, function (section) { + write: (sectionIds) => { + const openSections = _.indexBy(sectionIds); + _.forEach(this.sections, section => { section.isOpen(openSections.hasOwnProperty(section.id)); }); } }); this.openSectionIds.extend({ rateLimit: { timeout: 1000, method: 'notifyWhenChangesStop' } }); - var initialState = null; - var cookieValue = jQuery.cookie(AmeTweakManagerModule.openSectionCookieName); + let initialState = null; + let cookieValue = jQuery.cookie(AmeTweakManagerModule.openSectionCookieName); if ((typeof cookieValue === 'string') && JSON && JSON.parse) { - var storedState = JSON.parse(cookieValue); + let storedState = JSON.parse(cookieValue); if (_.isArray(storedState)) { initialState = _.intersection(_.keys(this.sectionsById), storedState); } @@ -542,7 +510,7 @@ var AmeTweakManagerModule = /** @class */ (function () { else { this.openSectionIds([_.first(this.sections).id]); } - this.openSectionIds.subscribe(function (sectionIds) { + this.openSectionIds.subscribe((sectionIds) => { jQuery.cookie(AmeTweakManagerModule.openSectionCookieName, ko.toJSON(sectionIds), { expires: 90 }); }); if (scriptData.lastUserTweakSuffix) { @@ -552,23 +520,23 @@ var AmeTweakManagerModule = /** @class */ (function () { this.settingsData = ko.observable(''); this.isSaving = ko.observable(false); } - AmeTweakManagerModule.prototype.saveChanges = function () { + saveChanges() { this.isSaving(true); - var _ = wsAmeLodash; - var data = { + const _ = wsAmeLodash; + let data = { 'tweaks': _.indexBy(_.invoke(this.tweaksById, 'toJs'), 'id'), 'lastUserTweakSuffix': this.lastUserTweakSuffix }; this.settingsData(ko.toJSON(data)); return true; - }; - AmeTweakManagerModule.prototype.addAdminCssTweak = function (label, css) { + } + addAdminCssTweak(label, css) { this.lastUserTweakSuffix++; - var slug = AmeTweakManagerModule.slugify(label); + let slug = AmeTweakManagerModule.slugify(label); if (slug !== '') { slug = '-' + slug; } - var props = { + let props = { label: label, id: 'utw-' + this.lastUserTweakSuffix + slug, isUserDefined: true, @@ -578,7 +546,7 @@ var AmeTweakManagerModule = /** @class */ (function () { hasAccessMap: true }; props['css'] = css; - var cssInput = { + const cssInput = { id: 'css', label: '', dataType: 'string', @@ -586,17 +554,17 @@ var AmeTweakManagerModule = /** @class */ (function () { syntaxHighlighting: 'css' }; props.children.push(cssInput); - var newTweak = new AmeTweakItem(props, this); + const newTweak = new AmeTweakItem(props, this); this.tweaksById[newTweak.id] = newTweak; this.sectionsById['admin-css'].addTweak(newTweak); - }; - AmeTweakManagerModule.slugify = function (input) { - var _ = AmeTweakManagerModule._; - var output = _.deburr(input); + } + static slugify(input) { + const _ = AmeTweakManagerModule._; + let output = _.deburr(input); output = output.replace(/[^a-zA-Z0-9 \-]/, ''); return _.kebabCase(output); - }; - AmeTweakManagerModule.prototype.launchTweakEditor = function (tweak) { + } + launchTweakEditor(tweak) { // noinspection JSRedundantSwitchStatement switch (tweak.getTypeId()) { case 'admin-css': @@ -606,59 +574,58 @@ var AmeTweakManagerModule = /** @class */ (function () { default: alert('Error: Editor not implemented! This is probably a bug.'); } - }; - AmeTweakManagerModule.prototype.confirmDeleteTweak = function (tweak) { + } + confirmDeleteTweak(tweak) { if (!tweak.isUserDefined || !confirm('Delete this tweak?')) { return; } this.deleteTweak(tweak); - }; - AmeTweakManagerModule.prototype.deleteTweak = function (tweak) { - var section = tweak.getSection(); + } + deleteTweak(tweak) { + const section = tweak.getSection(); if (section) { section.removeTweak(tweak); } - var parent = tweak.getParent(); + const parent = tweak.getParent(); if (parent) { parent.removeChild(tweak); } delete this.tweaksById[tweak.id]; - }; - AmeTweakManagerModule.prototype.getCodeMirrorOptions = function (mode) { + } + getCodeMirrorOptions(mode) { if (mode === 'css') { return this.cssHighlightingOptions; } return null; - }; - AmeTweakManagerModule._ = wsAmeLodash; - AmeTweakManagerModule.openSectionCookieName = 'ame_tmce_open_sections'; - return AmeTweakManagerModule; -}()); -var AmeEditAdminCssDialog = /** @class */ (function () { - function AmeEditAdminCssDialog(manager) { - var _this = this; + } +} +AmeTweakManagerModule._ = wsAmeLodash; +AmeTweakManagerModule.openSectionCookieName = 'ame_tmce_open_sections'; +class AmeEditAdminCssDialog { + constructor(manager) { + this.jQueryWidget = null; this.autoCancelButton = false; this.options = { minWidth: 400 }; this.selectedTweak = null; - var _ = AmeTweakManagerModule._; + const _ = AmeTweakManagerModule._; this.manager = manager; this.tweakLabel = ko.observable(''); this.cssCode = ko.observable(''); this.confirmButtonText = ko.observable('Add Snippet'); this.title = ko.observable(null); - this.isAddButtonEnabled = ko.computed(function () { - return !((_.trim(_this.tweakLabel()) === '') || (_.trim(_this.cssCode()) === '')); + this.isAddButtonEnabled = ko.computed(() => { + return !((_.trim(this.tweakLabel()) === '') || (_.trim(this.cssCode()) === '')); }); this.isOpen = ko.observable(false); } - AmeEditAdminCssDialog.prototype.onOpen = function (event, ui) { + onOpen(event, ui) { if (this.selectedTweak) { this.tweakLabel(this.selectedTweak.label()); this.title('Edit admin CSS snippet'); this.confirmButtonText('Save Changes'); - var cssProperty = this.selectedTweak.getEditableProperty('css'); + const cssProperty = this.selectedTweak.getEditableProperty('css'); this.cssCode(cssProperty ? cssProperty() : ''); } else { @@ -667,123 +634,32 @@ var AmeEditAdminCssDialog = /** @class */ (function () { this.title('Add admin CSS snippet'); this.confirmButtonText('Add Snippet'); } - }; - AmeEditAdminCssDialog.prototype.onConfirm = function () { + } + onConfirm() { if (this.selectedTweak) { //Update the existing tweak. this.selectedTweak.label(this.tweakLabel()); - this.selectedTweak.getEditableProperty('css')(this.cssCode()); + const propertyObservable = this.selectedTweak.getEditableProperty('css'); + if (propertyObservable !== null) { + propertyObservable(this.cssCode()); + } } else { //Create a new tweak. this.manager.addAdminCssTweak(this.tweakLabel(), this.cssCode()); } this.close(); - }; - AmeEditAdminCssDialog.prototype.onClose = function () { + } + onClose() { this.selectedTweak = null; - }; - AmeEditAdminCssDialog.prototype.close = function () { + } + close() { this.isOpen(false); - }; - AmeEditAdminCssDialog.prototype.open = function () { + } + open() { this.isOpen(true); - }; - return AmeEditAdminCssDialog; -}()); -ko.bindingHandlers.ameCodeMirror = { - init: function (element, valueAccessor, allBindings) { - if (!wp.hasOwnProperty('codeEditor') || !wp.codeEditor.initialize) { - return; - } - var parameters = ko.unwrap(valueAccessor()); - if (!parameters) { - return; - } - var options; - var refreshTrigger; - if (parameters.options) { - options = parameters.options; - if (parameters.refreshTrigger) { - refreshTrigger = parameters.refreshTrigger; - } - } - else { - options = parameters; - } - var result = wp.codeEditor.initialize(element, options); - var cm = result.codemirror; - //Synchronise the editor contents with the observable passed to the "value" binding. - var valueObservable = allBindings.get('value'); - if (!ko.isObservable(valueObservable)) { - valueObservable = null; - } - var subscription = null; - var changeHandler = null; - if (valueObservable !== null) { - //Update the observable when the contents of the editor change. - var ignoreNextUpdate_1 = false; - changeHandler = function () { - //This will trigger our observable subscription (see below). - //We need to ignore that trigger to avoid recursive or duplicated updates. - ignoreNextUpdate_1 = true; - valueObservable(cm.doc.getValue()); - }; - cm.on('changes', changeHandler); - //Update the editor when the observable changes. - subscription = valueObservable.subscribe(function (newValue) { - if (ignoreNextUpdate_1) { - ignoreNextUpdate_1 = false; - return; - } - cm.doc.setValue(newValue); - ignoreNextUpdate_1 = false; - }); - } - //Refresh the size of the editor element when an observable changes value. - var refreshSubscription = null; - if (refreshTrigger) { - refreshSubscription = refreshTrigger.subscribe(function () { - cm.refresh(); - }); - } - //If the editor starts out hidden - for example, because it's inside a collapsed section - it will - //render incorrectly. To fix that, let's refresh it the first time it becomes visible. - if (!jQuery(element).is(':visible') && (typeof IntersectionObserver !== 'undefined')) { - var observer_1 = new IntersectionObserver(function (entries) { - for (var i = 0; i < entries.length; i++) { - if (entries[i].isIntersecting) { - //The editor is at least partially visible now. - observer_1.disconnect(); - cm.refresh(); - break; - } - } - }, { - //Use the browser viewport. - root: null, - //The threshold is somewhat arbitrary. Any value will work, but a lower setting means - //that the user is less likely to see an incorrectly rendered editor. - threshold: 0.05 - }); - observer_1.observe(cm.getWrapperElement()); - } - ko.utils.domNodeDisposal.addDisposeCallback(element, function () { - //Remove subscriptions and event handlers. - if (subscription) { - subscription.dispose(); - } - if (refreshSubscription) { - refreshSubscription.dispose(); - } - if (changeHandler) { - cm.off('changes', changeHandler); - } - //Destroy the CodeMirror instance. - jQuery(cm.getWrapperElement()).remove(); - }); } -}; +} jQuery(function () { ameTweakManager = new AmeTweakManagerModule(wsTweakManagerData); ko.applyBindings(ameTweakManager, document.getElementById('ame-tweak-manager')); diff --git a/extras/modules/tweaks/tweak-manager.js.map b/extras/modules/tweaks/tweak-manager.js.map index 87cb76c..b82fca3 100644 --- a/extras/modules/tweaks/tweak-manager.js.map +++ b/extras/modules/tweaks/tweak-manager.js.map @@ -1 +1 @@ -{"version":3,"file":"tweak-manager.js","sourceRoot":"","sources":["tweak-manager.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"tweak-manager.js","sourceRoot":"","sources":["tweak-manager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/tweaks/tweak-manager.ts b/extras/modules/tweaks/tweak-manager.ts index 64feb72..4e897fe 100644 --- a/extras/modules/tweaks/tweak-manager.ts +++ b/extras/modules/tweaks/tweak-manager.ts @@ -5,15 +5,9 @@ /// /// -declare let ameTweakManager: AmeTweakManagerModule; +let ameTweakManager: AmeTweakManagerModule; declare const wsTweakManagerData: AmeTweakManagerScriptData; -declare const wp: { - codeEditor: { - initialize: (textarea: string, options: object) => any; - }; -}; - interface AmeTweakManagerScriptData { selectedActor: string; isProVersion: boolean; @@ -63,7 +57,7 @@ abstract class AmeSetting extends AmeNamedNode { protected static idCounter = 0; // noinspection JSUnusedGlobalSymbols Used in Knockout templates. - templateName: string; + templateName: string = ''; inputValue: KnockoutObservable; readonly uniqueInputId: string; @@ -85,7 +79,7 @@ interface AmeStringSettingProperties extends AmeSettingProperties { } class AmeStringSetting extends AmeSetting { - syntaxHighlightingOptions: object = null; + syntaxHighlightingOptions: object | null = null; constructor( properties: AmeStringSettingProperties, @@ -158,7 +152,7 @@ type ConfigurationNodeProperties = AmeActorFeatureProperties | AmeSettingPropert class AmeSettingStore { private observableProperties: Record> = {}; - private accessMaps: Record = {}; + private accessMaps: Record = {}; private readonly initialProperties: Record; constructor(initialProperties: Record = {}) { @@ -195,10 +189,17 @@ class AmeSettingStore { const _ = AmeTweakManagerModule._; let newProps = {}; _.forOwn(this.observableProperties, function (observable, path) { + if (typeof path === 'undefined') { + return; + } _.set(newProps, path, observable()); }); - _.forOwn(this.accessMaps, function (map, path: string) { + _.forOwn(this.accessMaps, function (map, path) { + if (typeof path === 'undefined') { + return; + } + //Since all tweaks are disabled by default, having a tweak disabled for a role is the same //as not having a setting, so we can save some space by removing it. This does not always //apply to users/Super Admins because they can have precedence over roles. @@ -234,13 +235,13 @@ class AmeSettingStore { name: string, path: string | string[] = [], defaultAccessMap: AmeDictionary | null = null - ): AmeObservableActorSettings { + ): AmeObservableActorFeatureMap { path = this.getFullPath(name, path); const _ = AmeTweakManagerModule._; const value = _.get(this.initialProperties, path, defaultAccessMap); if (!this.accessMaps.hasOwnProperty(path)) { - this.accessMaps[path] = new AmeObservableActorSettings(value); + this.accessMaps[path] = new AmeObservableActorFeatureMap(value); } return this.accessMaps[path]; @@ -253,15 +254,15 @@ function isSettingStore(thing: object): thing is AmeSettingStore { } class AmeCompositeNode extends AmeNamedNode { - children: KnockoutObservableArray = null; - propertyPath: string[]; - actorAccess: AmeActorAccess; - properties: AmeSettingStore; + children: KnockoutObservableArray; + propertyPath: string[] = []; + actorAccess: AmeActorAccess | null = null; + properties: AmeSettingStore | null = null; - constructor( + protected constructor( properties: ConfigurationNodeProperties, module: AmeTweakManagerModule, - store: AmeSettingStore | 'self' = null, + store: AmeSettingStore | 'self', path: string[] = [] ) { super(properties); @@ -285,7 +286,7 @@ class AmeCompositeNode extends AmeNamedNode { this.propertyPath = path.concat(this.propertyPath); } - let children = []; + let children: AmeNamedNode[] = []; if (properties.children && (properties.children.length > 0)) { for (let i = 0; i < properties.children.length; i++) { const props = properties.children[i]; @@ -302,6 +303,8 @@ class AmeCompositeNode extends AmeNamedNode { } this.children = ko.observableArray(children); + } else { + this.children = ko.observableArray([] as AmeNamedNode[]); } if (isAmeActorFeatureProperties(properties)) { @@ -320,7 +323,7 @@ class AmeCompositeNode extends AmeNamedNode { module: AmeTweakManagerModule, store: AmeSettingStore, path: string[] = [] - ): AmeSetting { + ): AmeSetting | null { const inputType = properties.inputType ? properties.inputType : properties.dataType; switch (inputType) { @@ -343,14 +346,14 @@ class AmeCompositeNode extends AmeNamedNode { class AmeActorAccess { isChecked: KnockoutComputed; - protected enabledForActor: AmeObservableActorSettings; + protected enabledForActor: AmeObservableActorFeatureMap; protected module: AmeTweakManagerModule; isIndeterminate: KnockoutComputed; constructor( - actorSettings: AmeObservableActorSettings, + actorSettings: AmeObservableActorFeatureMap, module: AmeTweakManagerModule, - children: AmeCompositeNode['children'] = null + children: AmeCompositeNode['children'] | null = null ) { this.module = module; this.enabledForActor = actorSettings; @@ -450,8 +453,8 @@ interface AmeSavedTweakProperties { interface AmeTweakProperties extends AmeSavedTweakProperties, AmeActorFeatureProperties { description?: string; - parentId?: string; - sectionId?: string; + parentId?: string | null; + sectionId?: string | null; isUserDefined?: boolean; typeId?: string; @@ -464,10 +467,10 @@ class AmeTweakItem extends AmeCompositeNode { label: KnockoutObservable; public readonly isUserDefined: boolean; - private readonly initialProperties: AmeSavedTweakProperties = null; + private readonly initialProperties: AmeSavedTweakProperties | null = null; - private section: AmeTweakSection = null; - private parent: AmeTweakItem = null; + private section: AmeTweakSection | null = null; + private parent: AmeTweakItem | null = null; constructor(properties: AmeTweakProperties, module: AmeTweakManagerModule) { super(properties, module, 'self'); @@ -507,10 +510,12 @@ class AmeTweakItem extends AmeCompositeNode { props.sectionId = this.section ? this.section.id : null; props.parentId = this.parent ? this.parent.id : null; - props = _.defaults( - props, - _.omit(this.initialProperties, 'userInputValue', 'enabledForActor') - ); + if (this.initialProperties !== null) { + props = _.defaults( + props, + _.omit(this.initialProperties, 'userInputValue', 'enabledForActor') + ); + } return props; } } @@ -525,11 +530,11 @@ class AmeTweakItem extends AmeCompositeNode { return this; } - getSection(): AmeTweakSection { + getSection(): AmeTweakSection | null { return this.section; } - getParent(): AmeTweakItem { + getParent(): AmeTweakItem | null { return this.parent; } @@ -543,18 +548,20 @@ class AmeTweakItem extends AmeCompositeNode { this.children.remove(tweak); } - getEditableProperty(key: string): KnockoutObservable { + getEditableProperty(key: string): KnockoutObservable | null { if (this.properties) { return this.properties.getObservableProperty(key, ''); } + return null; } getTypeId(): string | null { if (!this.isUserDefined || !this.initialProperties) { return null; } - if ((this.initialProperties as AmeTweakProperties).typeId) { - return (this.initialProperties as AmeTweakProperties).typeId; + const tweakProps = this.initialProperties as AmeTweakProperties; + if ((typeof tweakProps.typeId === 'string') && (tweakProps.typeId !== '')) { + return tweakProps.typeId; } return null; } @@ -572,13 +579,13 @@ class AmeTweakSection { tweaks: KnockoutObservableArray; isOpen: KnockoutObservable; - footerTemplateName: string = null; + footerTemplateName: string | null = null; constructor(properties: AmeSectionProperties) { this.id = properties.id; this.label = properties.label; this.isOpen = ko.observable(true); - this.tweaks = ko.observableArray([]); + this.tweaks = ko.observableArray([] as AmeTweakItem[]); } addTweak(tweak: AmeTweakItem) { @@ -604,8 +611,8 @@ class AmeTweakManagerModule { static readonly openSectionCookieName = 'ame_tmce_open_sections'; readonly actorSelector: AmeActorSelector; - selectedActorId: KnockoutComputed; - selectedActor: KnockoutComputed; + selectedActorId: KnockoutComputed; + selectedActor: KnockoutComputed; private tweaksById: { [id: string]: AmeTweakItem } = {}; private sectionsById: AmeDictionary = {}; @@ -626,7 +633,7 @@ class AmeTweakManagerModule { this.actorSelector = new AmeActorSelector(AmeActors, scriptData.isProVersion); this.selectedActorId = this.actorSelector.createKnockoutObservable(ko); - this.selectedActor = ko.computed(() => { + this.selectedActor = ko.computed(() => { const id = this.selectedActorId(); if (id === null) { return null; @@ -690,7 +697,7 @@ class AmeTweakManagerModule { //The user can open/close sections and we automatically remember their state. this.openSectionIds = ko.computed({ read: () => { - let result = []; + let result: string[] = []; _.forEach(this.sections, section => { if (section.isOpen()) { result.push(section.id); @@ -707,7 +714,7 @@ class AmeTweakManagerModule { }); this.openSectionIds.extend({rateLimit: {timeout: 1000, method: 'notifyWhenChangesStop'}}); - let initialState: string[] = null; + let initialState: string[] | null = null; let cookieValue = jQuery.cookie(AmeTweakManagerModule.openSectionCookieName); if ((typeof cookieValue === 'string') && JSON && JSON.parse) { let storedState = JSON.parse(cookieValue); @@ -828,7 +835,7 @@ class AmeTweakManagerModule { } class AmeEditAdminCssDialog implements AmeKnockoutDialog { - jQueryWidget: JQuery; + jQueryWidget: JQuery | null = null; isOpen: KnockoutObservable; autoCancelButton: boolean = false; @@ -840,9 +847,9 @@ class AmeEditAdminCssDialog implements AmeKnockoutDialog { tweakLabel: KnockoutObservable; cssCode: KnockoutObservable; confirmButtonText: KnockoutObservable; - title: KnockoutObservable; + title: KnockoutObservable; - selectedTweak: AmeTweakItem = null; + selectedTweak: AmeTweakItem | null = null; private manager: AmeTweakManagerModule; @@ -861,7 +868,7 @@ class AmeEditAdminCssDialog implements AmeKnockoutDialog { this.isOpen = ko.observable(false); } - onOpen(event, ui) { + onOpen(event: JQueryEventObject, ui: any) { if (this.selectedTweak) { this.tweakLabel(this.selectedTweak.label()); this.title('Edit admin CSS snippet'); @@ -881,7 +888,10 @@ class AmeEditAdminCssDialog implements AmeKnockoutDialog { if (this.selectedTweak) { //Update the existing tweak. this.selectedTweak.label(this.tweakLabel()); - this.selectedTweak.getEditableProperty('css')(this.cssCode()); + const propertyObservable = this.selectedTweak.getEditableProperty('css'); + if (propertyObservable !== null) { + propertyObservable(this.cssCode()); + } } else { //Create a new tweak. this.manager.addAdminCssTweak( @@ -905,111 +915,6 @@ class AmeEditAdminCssDialog implements AmeKnockoutDialog { } } -ko.bindingHandlers.ameCodeMirror = { - init: function (element, valueAccessor, allBindings) { - if (!wp.hasOwnProperty('codeEditor') || !wp.codeEditor.initialize) { - return; - } - let parameters = ko.unwrap(valueAccessor()); - if (!parameters) { - return; - } - - let options; - let refreshTrigger: KnockoutObservable; - if (parameters.options) { - options = parameters.options; - if (parameters.refreshTrigger) { - refreshTrigger = parameters.refreshTrigger; - } - } else { - options = parameters; - } - - let result = wp.codeEditor.initialize(element, options); - const cm = result.codemirror; - - //Synchronise the editor contents with the observable passed to the "value" binding. - let valueObservable: KnockoutObservable = allBindings.get('value'); - if (!ko.isObservable(valueObservable)) { - valueObservable = null; - } - - let subscription = null; - let changeHandler = null; - if (valueObservable !== null) { - //Update the observable when the contents of the editor change. - let ignoreNextUpdate = false; - changeHandler = function () { - //This will trigger our observable subscription (see below). - //We need to ignore that trigger to avoid recursive or duplicated updates. - ignoreNextUpdate = true; - valueObservable(cm.doc.getValue()); - }; - cm.on('changes', changeHandler); - - //Update the editor when the observable changes. - subscription = valueObservable.subscribe(function (newValue) { - if (ignoreNextUpdate) { - ignoreNextUpdate = false; - return; - } - cm.doc.setValue(newValue); - ignoreNextUpdate = false; - }); - } - - //Refresh the size of the editor element when an observable changes value. - let refreshSubscription: KnockoutSubscription = null; - if (refreshTrigger) { - refreshSubscription = refreshTrigger.subscribe(function () { - cm.refresh(); - }); - } - - //If the editor starts out hidden - for example, because it's inside a collapsed section - it will - //render incorrectly. To fix that, let's refresh it the first time it becomes visible. - if (!jQuery(element).is(':visible') && (typeof IntersectionObserver !== 'undefined')) { - const observer = new IntersectionObserver( - function (entries) { - for (let i = 0; i < entries.length; i++) { - if (entries[i].isIntersecting) { - //The editor is at least partially visible now. - observer.disconnect(); - cm.refresh(); - break; - } - } - }, - { - //Use the browser viewport. - root: null, - //The threshold is somewhat arbitrary. Any value will work, but a lower setting means - //that the user is less likely to see an incorrectly rendered editor. - threshold: 0.05 - } - ); - observer.observe(cm.getWrapperElement()); - } - - ko.utils.domNodeDisposal.addDisposeCallback(element, function () { - //Remove subscriptions and event handlers. - if (subscription) { - subscription.dispose(); - } - if (refreshSubscription) { - refreshSubscription.dispose(); - } - if (changeHandler) { - cm.off('changes', changeHandler); - } - - //Destroy the CodeMirror instance. - jQuery(cm.getWrapperElement()).remove(); - }); - } -}; - jQuery(function () { ameTweakManager = new AmeTweakManagerModule(wsTweakManagerData); ko.applyBindings(ameTweakManager, document.getElementById('ame-tweak-manager')); diff --git a/extras/modules/tweaks/tweaks.php b/extras/modules/tweaks/tweaks.php index f1c69bd..66b0c7b 100644 --- a/extras/modules/tweaks/tweaks.php +++ b/extras/modules/tweaks/tweaks.php @@ -393,7 +393,7 @@ public function enqueueTabScripts() { plugins_url('tweak-manager.js', __FILE__), array( 'ame-lodash', - 'knockout', + 'ame-knockout', 'ame-actor-selector', 'ame-jquery-cookie', 'ame-ko-extensions', diff --git a/extras/persistent-pro-module.php b/extras/persistent-pro-module.php index f28ecb6..1450cfe 100644 --- a/extras/persistent-pro-module.php +++ b/extras/persistent-pro-module.php @@ -1,8 +1,67 @@ settings) ) { + return $this->settings; + } + if ( !$this->settingsWrapperEnabled ) { + return parent::loadSettings(); + } + + $scope = ($this->menuEditor->get_plugin_option('menu_config_scope') === 'site') + ? ScopedOptionStorage::SITE_SCOPE + : ScopedOptionStorage::GLOBAL_SCOPE; + + $this->settings = new ModuleSettings( + $this->optionName, + $scope, + $this->defaultSettings, + array($this, 'createSettingInstances'), + true, + $this->lastModifiedSettingEnabled + ); + $this->settings->addReadAliases($this->getSettingAliases()); + + return $this->settings; + } + + public function saveSettings() { + if ( !$this->settingsWrapperEnabled ) { + parent::saveSettings(); + return; + } + + if ( $this->settings ) { + $this->settings->save(); + } + } + + public function createSettingInstances(ModuleSettings $settings) { + //Subclasses should override this to create Setting instances. + return array(); + } + + protected function getSettingAliases() { + return array(); + } + + /** * @param array $importedData + * @internal */ public function handleDataImport($importedData) { //Action: admin_menu_editor-import_data @@ -13,7 +72,16 @@ public function handleDataImport($importedData) { public function exportSettings() { if ( isset($this->moduleId) ) { - return $this->loadSettings(); + if ( $this->settingsWrapperEnabled ) { + $settings = $this->loadSettings(); + if ( $settings instanceof AbstractSettingsDictionary ) { + return $settings->toArray(); + } else { + return null; + } + } else { + return $this->loadSettings(); + } } return null; } @@ -27,6 +95,16 @@ public function importSettings($newSettings) { $this->saveSettings(); } + public function mergeSettingsWith($newSettings) { + if ( !$this->settingsWrapperEnabled ) { + return parent::mergeSettingsWith($newSettings); + } + + $settings = $this->loadSettings(); + $settings->mergeWith($newSettings); + return $settings->toArray(); + } + /** * @return string */ diff --git a/extras/phpColors/src/color.php b/extras/phpColors/src/color.php index 329939f..9baf0eb 100644 --- a/extras/phpColors/src/color.php +++ b/extras/phpColors/src/color.php @@ -137,9 +137,9 @@ public static function hslToHex( $hsl = array() ){ } // Convert to hex - $r = dechex($r); - $g = dechex($g); - $b = dechex($b); + $r = dechex((int)round($r)); + $g = dechex((int)round($g)); + $b = dechex((int)round($b)); // Make sure we get 2 digits for decimals $r = (strlen("".$r)===1) ? "0".$r:$r; @@ -186,9 +186,9 @@ public static function rgbToHex( $rgb = array() ){ } // Convert RGB to HEX - $hex[0] = str_pad(dechex( $rgb['R'] ), 2, '0', STR_PAD_LEFT); - $hex[1] = str_pad(dechex( $rgb['G'] ), 2, '0', STR_PAD_LEFT); - $hex[2] = str_pad(dechex( $rgb['B'] ), 2, '0', STR_PAD_LEFT); + $hex[0] = str_pad(dechex( (int)round($rgb['R'])), 2, '0', STR_PAD_LEFT); + $hex[1] = str_pad(dechex( (int)round($rgb['G']) ), 2, '0', STR_PAD_LEFT); + $hex[2] = str_pad(dechex( (int)round($rgb['B']) ), 2, '0', STR_PAD_LEFT); return implode( '', $hex ); diff --git a/extras/pro-admin-helpers.js b/extras/pro-admin-helpers.js index 254dd19..e652706 100644 --- a/extras/pro-admin-helpers.js +++ b/extras/pro-admin-helpers.js @@ -2,21 +2,21 @@ /// 'use strict'; (function ($) { - var isHeadingStateRestored = false; + let isHeadingStateRestored = false; function setCollapsedState($heading, isCollapsed) { $heading.toggleClass('ame-is-collapsed-heading', isCollapsed); //Show/hide all menu items between this heading and the next one. - var containedItems = $heading.nextUntil('li.ame-menu-heading-item, #collapse-menu', 'li.menu-top,li.wp-menu-separator'); + const containedItems = $heading.nextUntil('li.ame-menu-heading-item, #collapse-menu', 'li.menu-top,li.wp-menu-separator'); containedItems.toggle(!isCollapsed); } /** * Save the collapsed/expanded state of menu headings. */ function saveCollapsedHeadings($adminMenu) { - var collapsedHeadings = loadCollapsedHeadings(); - var currentTime = Date.now(); + let collapsedHeadings = loadCollapsedHeadings(); + const currentTime = Date.now(); $adminMenu.find('li[id].ame-collapsible-heading').each(function () { - var $heading = $(this), id = $heading.attr('id'); + const $heading = $(this), id = $heading.attr('id'); if (id) { if ($heading.hasClass('ame-is-collapsed-heading')) { collapsedHeadings[id] = currentTime; @@ -29,10 +29,10 @@ //Discard stored data associated with headings that haven't been seen in a long time. //It's likely that the headings no longer exist. if (Object.keys) { - var threshold = currentTime - (90 * 24 * 3600 * 1000); - var headingIds = Object.keys(collapsedHeadings); - for (var i = 0; i < headingIds.length; i++) { - var id = headingIds[i]; + const threshold = currentTime - (90 * 24 * 3600 * 1000); + let headingIds = Object.keys(collapsedHeadings); + for (let i = 0; i < headingIds.length; i++) { + const id = headingIds[i]; if (!collapsedHeadings.hasOwnProperty(id)) { continue; } @@ -44,12 +44,12 @@ $.cookie('ame-collapsed-menu-headings', JSON.stringify(collapsedHeadings), { expires: 90 }); } function loadCollapsedHeadings() { - var defaultValue = {}; + let defaultValue = {}; if (!$.cookie) { return defaultValue; } try { - var settings = JSON.parse($.cookie('ame-collapsed-menu-headings')); + let settings = JSON.parse($.cookie('ame-collapsed-menu-headings')); if (typeof settings === 'object') { return settings; } @@ -64,13 +64,13 @@ */ function restoreCollapsedHeadings() { isHeadingStateRestored = true; - var previouslyCollapsedHeadings = loadCollapsedHeadings(); - var $adminMenu = $('#adminmenumain #adminmenu'); - for (var id in previouslyCollapsedHeadings) { + const previouslyCollapsedHeadings = loadCollapsedHeadings(); + const $adminMenu = $('#adminmenumain #adminmenu'); + for (let id in previouslyCollapsedHeadings) { if (!previouslyCollapsedHeadings.hasOwnProperty(id)) { continue; } - var $heading = $adminMenu.find('#' + id); + const $heading = $adminMenu.find('#' + id); if ($heading.length > 0) { setCollapsedState($heading, true); } @@ -83,19 +83,19 @@ }); jQuery(function ($) { //Menu headings: Handle clicks. - var $adminMenu = $('#adminmenumain #adminmenu'); + const $adminMenu = $('#adminmenumain #adminmenu'); $adminMenu.find('li.ame-menu-heading-item a').on('click', function () { - var $heading = $(this).closest('li'); - var canBeCollapsed = $heading.hasClass('ame-collapsible-heading'); + const $heading = $(this).closest('li'); + const canBeCollapsed = $heading.hasClass('ame-collapsible-heading'); if (!canBeCollapsed) { //By default, do nothing. The heading is implemented as a link due to how the admin menu //works, but we don't want it to go to a different URL on click. return false; } - var isCollapsed = !$heading.hasClass('ame-is-collapsed-heading'); + let isCollapsed = !$heading.hasClass('ame-is-collapsed-heading'); setCollapsedState($heading, isCollapsed); //Remember the collapsed/expanded state. - if ($.cookie) { + if (typeof $.cookie !== 'undefined') { setTimeout(saveCollapsedHeadings.bind(window, $adminMenu), 50); } return false; @@ -108,39 +108,39 @@ } //Menu headings: If the user hasn't specified a custom text color, make sure the color //doesn't change on hover/focus. - if (wsAmeProAdminHelperData.setHeadingHoverColor && Array.prototype.map) { - var baseTextColor = void 0; + if (wsAmeProAdminHelperData.setHeadingHoverColor && (typeof Array.prototype.map !== 'undefined')) { + let baseTextColor; //Look at the first N menu items to discover the default text color. - var $menus = $('#adminmenumain #adminmenu li.menu-top') + const $menus = $('#adminmenumain #adminmenu li.menu-top') .not('.wp-menu-separator') .not('.ame-menu-heading-item') .slice(0, 10) .find('> a .wp-menu-name'); - var mostCommonColor_1 = '#eeeeee', seenColors_1 = {}; - seenColors_1[mostCommonColor_1] = 0; + let mostCommonColor = '#eeeeee', seenColors = {}; + seenColors[mostCommonColor] = 0; $menus.each(function () { - var color = $(this).css('color'); + const color = $(this).css('color'); if (color) { - if (seenColors_1.hasOwnProperty(color)) { - seenColors_1[color] = seenColors_1[color] + 1; + if (seenColors.hasOwnProperty(color)) { + seenColors[color] = seenColors[color] + 1; } else { - seenColors_1[color] = 1; + seenColors[color] = 1; } - if (seenColors_1[color] > seenColors_1[mostCommonColor_1]) { - mostCommonColor_1 = color; + if (seenColors[color] > seenColors[mostCommonColor]) { + mostCommonColor = color; } } }); - baseTextColor = mostCommonColor_1; + baseTextColor = mostCommonColor; //We want to override the default menu colors, but not per-item styles. - var parentSelector_1 = '#adminmenu li.ame-menu-heading-item'; - var selectors = [':hover', ':active', ':focus', ' a:hover', ' a:active', ' a:focus'].map(function (suffix) { - return parentSelector_1 + suffix; + const parentSelector = '#adminmenu li.ame-menu-heading-item'; + let selectors = [':hover', ':active', ':focus', ' a:hover', ' a:active', ' a:focus'].map(function (suffix) { + return parentSelector + suffix; }); - var $newStyle = $('').appendTo('head'); + } + this.$styleElement.text(cssText); + } + clear() { + if (this.$styleElement) { + this.$styleElement.remove(); + this.$styleElement = null; + } + } + dispose() { + //Stop listening for CSS changes. + this.cssChangeSubscription.dispose(); + this.cssText.dispose(); + //Dispose rule sets. + for (const ruleset of this.statements) { + ruleset.dispose(); + } + //Remove the style element. + this.clear(); + } + } + })(Preview = AmeStyleGenerator.Preview || (AmeStyleGenerator.Preview = {})); +})(AmeStyleGenerator || (AmeStyleGenerator = {})); +//# sourceMappingURL=style-generator.js.map \ No newline at end of file diff --git a/extras/style-generator/style-generator.js.map b/extras/style-generator/style-generator.js.map new file mode 100644 index 0000000..434c904 --- /dev/null +++ b/extras/style-generator/style-generator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"style-generator.js","sourceRoot":"","sources":["style-generator.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/style-generator/style-generator.ts b/extras/style-generator/style-generator.ts new file mode 100644 index 0000000..1f021f6 --- /dev/null +++ b/extras/style-generator/style-generator.ts @@ -0,0 +1,1235 @@ +import {AmeCustomizable} from '../pro-customizables/assets/customizable.js'; + +export namespace AmeStyleGenerator { + const $ = jQuery; + + //region Values + interface ValueDescriptorData { + t: 'constant' | 'setting' | 'funcCall' | 'var' | 'array'; + } + + interface ConstantValueData extends ValueDescriptorData { + t: 'constant'; + value: string; + } + + interface ArrayValueData extends ValueDescriptorData { + t: 'array'; + items: AnyValueDescriptorData[]; + } + + interface SettingReferenceData extends ValueDescriptorData { + t: 'setting'; + id: string; + } + + interface FunctionCallData extends ValueDescriptorData { + t: 'funcCall'; + name: string; + args: AnyValueDescriptorData[] | Record; + } + + interface VariableReferenceData extends ValueDescriptorData { + t: 'var'; + name: string; + } + + type AnyValueDescriptorData = + ConstantValueData + | SettingReferenceData + | FunctionCallData + | VariableReferenceData + | ArrayValueData; + + abstract class ValueDescriptor { + abstract getValue(): any | null; + } + + class ConstantValue extends ValueDescriptor { + constructor(protected readonly value: string) { + super(); + } + + getValue() { + return this.value; + } + } + + class ArrayValue extends ValueDescriptor { + constructor(protected readonly items: ValueDescriptor[]) { + super(); + } + + getValue() { + return this.items.map(item => item.getValue()); + } + + getItemDescriptors() { + return this.items; + } + } + + class SettingReference extends ValueDescriptor { + constructor( + public readonly settingId: string, + protected readonly valueGetter: (id: string) => any + ) { + super(); + } + + getValue() { + return this.valueGetter(this.settingId); + } + } + + class VariableReference extends ValueDescriptor { + constructor( + public readonly name: string, + protected readonly valueGetter: (name: string) => any + ) { + super(); + } + + getValue(): any { + return this.valueGetter(this.name); + } + } + + type FunctionCallArgs = any[] | Record; + + type BoxedFunctionCallArgs = + A extends any[] ? ValueDescriptor[] : Record + + class FunctionCall extends ValueDescriptor { + constructor( + public readonly args: BoxedFunctionCallArgs, + protected readonly callback: (args: A) => R + ) { + super(); + } + + getValue() { + return this.callback(this.resolveArgs(this.args)); + } + + protected resolveArgs(args: BoxedFunctionCallArgs): A { + if (Array.isArray(args)) { + return args.map(arg => arg.getValue()) as A; + } + + return Object.keys(args).reduce((result, key) => { + result[key] = args[key].getValue(); + return result; + }, {} as Record) as A; + } + } + + //endregion + + function isEmptyCssValue(value: unknown): boolean { + return (typeof value === 'undefined') || (value === '') || (value === null); + } + + function convertToRgba(color: string, opacity: number = 1.0): string { + color = color.trim(); + if (color === '') { + return 'transparent'; + } + + //Strip the leading hash, if any. + if (color[0] === '#') { + color = color.substring(1); + } + + //If the color is in the shorthand format, expand it. + if (color.length === 3) { + color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2]; + } + + //The color should now be in the full 6-digit format. Convert it to RGBA. + if (color.length === 6) { + const red = parseInt(color.substring(0, 2), 16); + const green = parseInt(color.substring(2, 4), 16); + const blue = parseInt(color.substring(4, 6), 16); + return `rgba(${red}, ${green}, ${blue}, ${opacity})`; + } + + //The color may be invalid, or it's not in a hex format we recognize. + return color; + } + + function uniqueArrayValues(array: T[]): T[] { + return array.filter((value, index) => array.indexOf(value) === index); + } + + function constrain(value: number, min: number, max: number): number { + return Math.min(Math.max(value, min), max); + } + + interface HslOperationArgs { + color: string | null; + hue?: number | null; + saturation?: number | null; + lightness?: number | null; + } + + function modifyHexColorAsHsl( + args: HslOperationArgs, + operation: ( + input: JQueryColor, + hue: number | null, + saturation: number | null, + lightness: number | null + ) => JQueryColor + ): string { + const color = args.color || ''; + if (isEmptyCssValue(color)) { + return ''; + } + + const hue = args.hue || null; + const saturation = args.saturation || null; + const lightness = args.lightness || null; + + if ((hue === null) && (saturation === null) && (lightness === null)) { + return color; + } + + let output = $.Color(color); + output = operation(output, hue, saturation, lightness); + return output.toHexString(); + } + + // noinspection JSUnusedGlobalSymbols -- Used dynamically by declaration generators received from the server. + const builtinFunctions = { + simpleProperty: function (args: { name: string | null, value: string | null }): string[] { + if (isEmptyCssValue(args.value)) { + return []; + } + return [args.name + ': ' + args.value + ';']; + }, + + formatLength: function (args: { value: string | null, unit: string | null }): string { + if (isEmptyCssValue(args.value)) { + return ''; + } + + //Normalize numeric values. For example, while JS accepts "1." as a number, + //"1.px" is not a valid CSS length value, so it should be converted to "1px". + const numericValue = parseFloat(String(args.value)); + if (isNaN(numericValue)) { + return ''; + } + return '' + numericValue + (args.unit || ''); + }, + + shadow: function (args: Record): string[] { + const mode = args.mode || 'default'; + const color = args.color || ''; + + if (mode === 'default') { + return []; + } + if ((mode === 'none') || (color === '') || (color === null) || (color === 'transparent')) { + return ['box-shadow: none;']; + } + if (mode !== 'custom') { + return []; + } + + const components = []; + if (args.inset) { + components.push('inset'); + } + + const horizontal = args['offset-x'] || 0; + const vertical = args['offset-y'] || 0; + const blur = args.blur || 0; + const spread = args.spread || 0; + components.push(`${horizontal}px ${vertical}px ${blur}px ${spread}px`); + + const colorOpacity = args.colorOpacity || 1.0; + if (colorOpacity < 1.0) { + components.push(convertToRgba(color, colorOpacity)); + } else { + components.push(color); + } + + return [`box-shadow: ${components.join(' ')};`]; + }, + + boxSides: function (args: Record): string[] { + if (typeof args.cssPropertyPrefix !== 'string') { + throw new Error('Invalid config for the boxSides generator: missing cssPropertyPrefix'); + } + + const compositeValue = args.value || {}; + const unit = compositeValue.unit || ''; + + const declarations = []; + for (const side of ['top', 'right', 'bottom', 'left']) { + const value = compositeValue[side]; + if (isEmptyCssValue(value)) { + continue; + } + + const property = args.cssPropertyPrefix + side; + declarations.push(`${property}: ${value}${unit};`); + } + + return declarations; + }, + + firstNonEmpty(args: any[]): any | null { + for (const arg of args) { + if (!isEmptyCssValue(arg)) { + return arg; + } + } + return null; + }, + + /** + * Take a HEX color, convert it to HSL to edit its components, + * then convert back to HEX. + * + * @param args + */ + editHexAsHsl: function (args: HslOperationArgs): string { + return modifyHexColorAsHsl( + args, + (color, hue, saturation, lightness) => { + if (hue !== null) { + color = color.hue(hue); + } + if (saturation !== null) { + color = color.saturation(saturation); + } + if (lightness !== null) { + color = color.lightness(lightness); + } + return color; + } + ); + }, + + adjustHexAsHsl: function (args: HslOperationArgs): string { + return modifyHexColorAsHsl( + args, + (color, hue, saturation, lightness) => { + if (hue !== null) { + color = color.hue(constrain(color.hue() + hue, 0, 360)); + } + if (saturation !== null) { + color = color.saturation(constrain(color.saturation() + saturation, 0, 1.0)); + } + if (lightness !== null) { + color = color.lightness(constrain(color.lightness() + lightness, 0, 1.0)); + } + return color; + } + ); + }, + + mixColors: function (args: { color1: string | null, color2: string | null, weight?: number | null }): string { + const color1 = args.color1 || ''; + const color2 = args.color2 || ''; + if (isEmptyCssValue(color1) || isEmptyCssValue(color2)) { + return ''; + } + + const weight = args.weight || 50; + if (weight <= 0) { + return color2; + } else if (weight >= 100) { + return color1; + } + + return $.Color(color2).transition($.Color(color1), weight / 100).toHexString(); + }, + + changeLightness: function (args: { color: string | null, amount?: number | null }): string { + const color = args.color || ''; + if (isEmptyCssValue(color)) { + return ''; + } + + const amount = args.amount || 0; + if (amount === 0) { + return color; + } + + let output = $.Color(color); + + //Amount is a number between 0 and 100, while lightness is between 0.0 and 1.0. + let newLightness = output.lightness() + (amount / 100); + //Clamp to 0.0 - 1.0. + newLightness = constrain(newLightness, 0.0, 1.0); + + return output.lightness(newLightness).toHexString(); + }, + + darken: function (args: { color: string | null, amount?: number | null }): string { + const color = args.color || ''; + const amount = args.amount || 0; + return builtinFunctions.changeLightness({color, amount: -Math.abs(amount)}); + }, + + lighten: function (args: { color: string | null, amount?: number | null }): string { + const color = args.color || ''; + const amount = args.amount || 0; + return builtinFunctions.changeLightness({color, amount: Math.abs(amount)}); + }, + + compare: function ( + args: { value1: any, value2: any, op: string, thenResult?: T, elseResult?: F } + ): T | F | boolean | null { + const value1 = args.value1; + const value2 = args.value2; + const operator = args.op; + const thenResult = (typeof args.thenResult !== 'undefined') ? args.thenResult : true; + const elseResult = (typeof args.elseResult !== 'undefined') ? args.elseResult : null; + let result: any; + + switch (operator) { + case '==': + result = value1 == value2; + break; + case '!=': + result = value1 != value2; + break; + case '>': + result = value1 > value2; + break; + case '>=': + result = value1 >= value2; + break; + case '<': + result = value1 < value2; + break; + case '<=': + result = value1 <= value2; + break; + default: + throw new Error(`Unknown operator: ${operator}`); + } + + return result ? thenResult : elseResult; + }, + + ifTruthy: function (args: { value: any, thenResult: any, elseResult?: any }): any { + const value = args.value; + const thenResult = (typeof args.thenResult !== 'undefined') ? args.thenResult : true; + const elseResult = (typeof args.elseResult !== 'undefined') ? args.elseResult : null; + + return value ? thenResult : elseResult; + }, + + ifSome: function (args: { values: any[], thenResult: any, elseResult?: any }): any { + const values = args.values; + const thenResult = args.thenResult; + const elseResult = (typeof args.elseResult !== 'undefined') ? args.elseResult : null; + + for (const value of values) { + if (!!value) { + return thenResult; + } + } + return elseResult; + }, + + ifAll: function (args: { values: any[], thenResult: any, elseResult?: any }): any { + const values = args.values; + const thenResult = args.thenResult; + const elseResult = args.elseResult !== undefined ? args.elseResult : null; + + if (!values || (values.length === 0)) { + return elseResult; + } + + for (const value of values) { + if (!value) { + return elseResult; + } + } + return thenResult; + } + } + + export namespace Preview { + import SettingValueReader = AmeCustomizable.SettingValueReader; + const $ = jQuery; + + interface CssStatementConfig { + nestedStatements?: CssStatementConfig[]; + } + + interface CssRuleSetConfig extends CssStatementConfig { + selectors: string[]; + generators: AnyValueDescriptorData[]; + } + + interface CssConditionalAtRuleConfig extends CssStatementConfig { + t: 'conditionalAtRule'; + identifier: string; + condition: string; + } + + export interface StyleGeneratorPreviewConfig { + statementGroups: Array<{ + expression: AnyValueDescriptorData; + statements: CssStatementConfig[]; + }>; + variables: Record; + stylesheetsToDisable?: string[]; + previewAllOnFirstUpdate?: boolean; + } + + function isConditionalAtRuleConfig(config: CssStatementConfig): config is CssConditionalAtRuleConfig { + if ((typeof config !== 'object') || (config === null)) { + return false; + } + + const configAsRecord = config as Record; + + return ( + (typeof configAsRecord['t'] === 'string') + && (configAsRecord['t'] === 'conditionalAtRule') + && (typeof configAsRecord['identifier'] === 'string') + ); + } + + function isRuleSetConfig(config: CssStatementConfig): config is CssRuleSetConfig { + return ( + (config !== null) + && (Array.isArray((config as Record)['selectors'])) + && (Array.isArray((config as Record)['generators'])) + ); + } + + const inactiveSettingMarker = {'_ame_inactive_setting': true}; + + class PreviewSession { + + private readonly settings: Record> = {}; + private readonly valueReaders: Set = new Set(); + private readonly notFound = {}; + + private readonly variables: Record = {}; + private readonly styleBlocks: PreviewStyleBlock[] = []; + + private readonly stylesheetsToDisable: string[] = []; + private stylesheetWasEnabled: Record = {}; + + private readonly settingValueGetter: (settingId: string) => any; + private readonly variableValueGetter: (variableName: string) => any; + + /** + * Whether this is the first time the preview is being updated. + * This is set to false after preview() is called for the first time. + */ + private _isBeforeFirstUpdate: boolean = true; + + constructor(config: StyleGeneratorPreviewConfig) { + //Optimization: Create bound getters once instead of every time we need + //to create a setting or variable reference. + this.settingValueGetter = this.getSettingPreviewValue.bind(this); + this.variableValueGetter = (variableName: string) => { + if (variableName in this.variables) { + return this.variables[variableName].getValue(); + } + return null; + } + + //Optionally, disable already generated custom stylesheets while the preview + //is active to prevent old settings from interfering with the preview of new settings. + if (Array.isArray(config.stylesheetsToDisable)) { + this.stylesheetsToDisable = config.stylesheetsToDisable; + } + + //Variables + for (const variableName in config.variables) { + if (!config.variables.hasOwnProperty(variableName)) { + continue; + } + this.variables[variableName] = this.createValueDescriptor( + config.variables[variableName], + true + ); + } + + //CSS statement groups + for (const conditionConfig of config.statementGroups) { + const statements = this.createCssStatements(conditionConfig.statements); + if (statements.length < 1) { + continue; + } + + const condition = this.createValueDescriptor(conditionConfig.expression, true); + const usedSettingIds = this.getSettingIdsUsedBy(condition); + const conditionCallback = (): boolean => { + //For performance, conditions that reference settings should + //only be checked when at least one setting is active. + if (usedSettingIds.length > 0) { + if (!usedSettingIds.some((id) => this.isSettingActive(id))) { + return false; + } + } + + const isTruthy = condition.getValue(); + return !!isTruthy; //Convert to boolean. + }; + + this.styleBlocks.push(new PreviewStyleBlock(statements, conditionCallback)); + } + } + + private createValueDescriptor( + data: AnyValueDescriptorData, + allowUnknownVariables: boolean = false + ): ValueDescriptor { + switch (data.t) { + case 'constant': + return new ConstantValue(data.value); + case 'array': + return new ArrayValue(data.items.map( + (valueData) => this.createValueDescriptor(valueData, allowUnknownVariables) + )); + case 'setting': + this.registerPreviewableSettingId(data.id); + return new SettingReference(data.id, this.settingValueGetter); + case 'var': + if (!this.variables.hasOwnProperty(data.name) && !allowUnknownVariables) { + throw new Error('Unknown variable: ' + data.name); + } + return new VariableReference(data.name, this.variableValueGetter); + case 'funcCall': + let functionName: keyof typeof builtinFunctions; + if (data.name in builtinFunctions) { + functionName = data.name as (keyof typeof builtinFunctions); + } else { + throw new Error('Unknown function: ' + data.name); + } + + const func = builtinFunctions[functionName]; + //Initialize the function arguments. + let args: ValueDescriptor[] | Record; + if (Array.isArray(data.args)) { + args = data.args.map(arg => this.createValueDescriptor(arg, allowUnknownVariables)); + } else { + args = {}; + for (const argName in data.args) { + if (!data.args.hasOwnProperty(argName)) { + continue; + } + args[argName] = this.createValueDescriptor(data.args[argName], allowUnknownVariables); + } + } + // @ts-ignore - Can't really statically check this since the values come from the server. + return new FunctionCall(args, func); + } + } + + /** + * Get the IDs of all settings that are referenced by the given descriptor. + * + * @param descriptor + * @private + */ + private getSettingIdsUsedBy(descriptor: ValueDescriptor): string[] { + if (descriptor instanceof SettingReference) { + return [descriptor.settingId]; + } + + if (descriptor instanceof ArrayValue) { + let result: string[] = []; + for (const item of descriptor.getItemDescriptors()) { + result = result.concat(this.getSettingIdsUsedBy(item)); + } + return uniqueArrayValues(result); + } + + if (descriptor instanceof FunctionCall) { + let result: string[] = []; + const args = descriptor.args; + if (Array.isArray(args)) { + for (const arg of args) { + result = result.concat(this.getSettingIdsUsedBy(arg)); + } + } else { + for (const argName in args) { + if (args.hasOwnProperty(argName)) { + result = result.concat(this.getSettingIdsUsedBy(args[argName])); + } + } + } + return uniqueArrayValues(result); + } + + if (descriptor instanceof VariableReference) { + const varDef = this.getVariableDefinition(descriptor.name); + if (varDef === null) { + return []; + } + return this.getSettingIdsUsedBy(varDef); + } + + return []; + } + + public getVariableDefinition(variableName: string): ValueDescriptor | null { + if (!this.variables.hasOwnProperty(variableName)) { + return null; + } + return this.variables[variableName]; + } + + private createCssStatements(configs: CssStatementConfig[]): CssStatement[] { + let results = []; + for (const config of configs) { + if (isRuleSetConfig(config)) { + results.push(this.createRuleSetFromConfig(config)); + } else if (isConditionalAtRuleConfig(config)) { + results.push(new ConditionalAtRule( + config.identifier, + config.condition, + (typeof config.nestedStatements === 'undefined') + ? [] + : this.createCssStatements(config.nestedStatements) + )); + } else { + console.error('Unknown CSS statement type: ', config); + } + } + return results; + } + + private createRuleSetFromConfig(config: CssRuleSetConfig, parent: CssRuleSet | null = null): CssRuleSet { + const generatorWrappers = this.makeGeneratorWrappers(config.generators); + + const ruleSet = new CssRuleSet(config.selectors, generatorWrappers, parent); + const nestedRuleSets = this.createNestedRuleSets(config.nestedStatements, ruleSet); + ruleSet.setNestedRuleSets(nestedRuleSets); + + return ruleSet; + } + + private createNestedRuleSets( + configs?: CssStatementConfig[], + parent: CssRuleSet | null = null + ): CssRuleSet[] { + let results: CssRuleSet[] = []; + if (!configs) { + return results; + } + + for (const config of configs) { + if (!isRuleSetConfig(config)) { + throw new Error('A CSS rule set can only contain other rule sets, not other types of statements.'); + } + results.push(this.createRuleSetFromConfig(config, parent)); + } + return results; + } + + getPreviewableSettingIDs(): string[] { + return Object.keys(this.settings); + } + + preview(settingId: string, value: any, otherSettingReader: SettingValueReader): void { + if (this._isBeforeFirstUpdate) { + this._isBeforeFirstUpdate = false; + this.disableAssociatedStylesheets(); + } + + this.valueReaders.add(otherSettingReader); + + if (!this.settings.hasOwnProperty(settingId)) { + this.settings[settingId] = ko.observable(value); + } else { + this.settings[settingId](value); + } + } + + dispose(): void { + //Dispose of all style blocks. + for (const block of this.styleBlocks) { + block.dispose(); + } + + this.reEnableAssociatedStylesheets(); + } + + private disableAssociatedStylesheets() { + for (const stylesheetSelector of this.stylesheetsToDisable) { + const $link = $(stylesheetSelector); + if ($link.length > 0) { + this.stylesheetWasEnabled[stylesheetSelector] = $link.prop('disabled'); + $link.prop('disabled', true); + } + } + } + + private reEnableAssociatedStylesheets() { + for (const stylesheetSelector of this.stylesheetsToDisable) { + const $link = $(stylesheetSelector); + if (($link.length > 0) && this.stylesheetWasEnabled.hasOwnProperty(stylesheetSelector)) { + $link.prop('disabled', this.stylesheetWasEnabled[stylesheetSelector]); + } + } + } + + isSettingActive(settingId: string): boolean { + if (this.settings.hasOwnProperty(settingId)) { + return this.settings[settingId]() !== inactiveSettingMarker; + } + return false; + } + + getSettingPreviewValue(settingId: string): any { + if (!this.settings.hasOwnProperty(settingId)) { + const value = this.getSettingFromReaders(settingId); + this.settings[settingId] = ko.observable(value).extend({deferred: true}); + } + + const observable = this.settings[settingId]; + let value = observable(); + if (value === inactiveSettingMarker) { + value = this.getSettingFromReaders(settingId); + observable(value); + } + return value; + } + + private getSettingFromReaders(settingId: string): any { + for (const reader of this.valueReaders) { + const value = reader(settingId, this.notFound); + if (value !== this.notFound) { + return value; + } + } + throw new Error('Setting not found for preview: ' + settingId); + } + + private makeGeneratorWrappers(generatorConfigs: AnyValueDescriptorData[]): DeclarationGeneratorWrapper[] { + let generatorWrappers = []; + for (const generatorConfig of generatorConfigs) { + const wrapper = this.makeDeclarationGeneratorWrapper(generatorConfig); + if (wrapper !== null) { + generatorWrappers.push(wrapper); + } + } + return generatorWrappers; + } + + private makeDeclarationGeneratorWrapper(config: AnyValueDescriptorData): DeclarationGeneratorWrapper | null { + const generator = this.createValueDescriptor(config); + return new DeclarationGeneratorWrapper(generator, this); + } + + private registerPreviewableSettingId(settingId: string): void { + if (!this.settings.hasOwnProperty(settingId)) { + this.settings[settingId] = ko.observable(inactiveSettingMarker); + } + } + + get isBeforeFirstUpdate(): boolean { + return this._isBeforeFirstUpdate; + } + } + + /** + * Preview manager for the style generator. + * + * This is a thin wrapper around the PreviewSession class. It initializes the session + * as needed and destroys it when the preview is cleared. This makes it simpler to manage + * active settings, style blocks, and CSS rule-sets: instead of having to carefully + * track dependencies and deactivate/reactivate them in the right order whenever the preview + * is disabled/enabled, we can just destroy the session and start over. + */ + export class StyleGeneratorPreview implements AmeCustomizable.PreviewUpdater { + private currentSession: PreviewSession | null = null; + + constructor(private readonly config: StyleGeneratorPreviewConfig) { + } + + private getOrCreateSession(): PreviewSession { + if (this.currentSession === null) { + this.currentSession = new PreviewSession(this.config); + } + return this.currentSession; + } + + getPreviewableSettingIDs(): string[] { + return this.getOrCreateSession().getPreviewableSettingIDs(); + } + + preview(settingId: string, value: any, otherSettingReader: SettingValueReader): void { + const session = this.getOrCreateSession(); + + const shouldPreviewAll = (this.config.previewAllOnFirstUpdate && session.isBeforeFirstUpdate); + session.preview(settingId, value, otherSettingReader); + + if (shouldPreviewAll) { + //Preview all registered settings the first time the preview is updated. + const notFound = {}; + for (const otherId of session.getPreviewableSettingIDs()) { + const otherValue = otherSettingReader(otherId, notFound); + if ((otherId !== settingId) && (otherValue !== notFound)) { + session.preview(otherId, otherValue, otherSettingReader); + } + } + } + } + + clearPreview(): void { + if (this.currentSession !== null) { + this.currentSession.dispose(); + this.currentSession = null; + } + } + } + + interface DeclarationSource { + isActive(): boolean; + + cssDeclarations(): string[]; + + dispose(): void; + } + + class DeclarationGeneratorWrapper implements DeclarationSource { + public readonly cssDeclarations: KnockoutComputed; + private readonly usedSettingIds: string[]; + + constructor( + private readonly generator: ValueDescriptor, + private readonly settingSource: PreviewSession + ) { + //Introspect the generator and see which settings it uses. + //This will be useful to determine if the generator is active. + this.usedSettingIds = DeclarationGeneratorWrapper.findReferencedSettingIds( + generator, + settingSource + ); + + this.cssDeclarations = ko.computed({ + read: () => this.getDeclarations(), + deferEvaluation: true, + }).extend({deferred: true}); + } + + /** + * Recursively find all settings used by a value descriptor (such as a function call). + * + * @param {ValueDescriptor} thing + * @param variableSource Needed to get variable definitions and not just the final values. + */ + static findReferencedSettingIds(thing: ValueDescriptor, variableSource: PreviewSession): string[] { + let settingIds: string[] = []; + if (thing instanceof SettingReference) { + settingIds.push(thing.settingId); + } else if (thing instanceof FunctionCall) { + if (Array.isArray(thing.args)) { + for (const arg of thing.args) { + settingIds = settingIds.concat( + DeclarationGeneratorWrapper.findReferencedSettingIds(arg, variableSource) + ); + } + } else { + for (const key in thing.args) { + settingIds = settingIds.concat( + DeclarationGeneratorWrapper.findReferencedSettingIds(thing.args[key], variableSource) + ); + } + } + } else if (thing instanceof VariableReference) { + const value = variableSource.getVariableDefinition(thing.name); + if (value !== null) { + settingIds = settingIds.concat( + DeclarationGeneratorWrapper.findReferencedSettingIds(value, variableSource) + ); + } + } + return settingIds; + } + + isActive(): boolean { + //Check if any of the input settings are active. + let hasSettingLookups = false; + for (const settingId of this.usedSettingIds) { + hasSettingLookups = true; + if (this.settingSource.isSettingActive(settingId)) { + return true; + } + } + + //If there are no input settings, the generator is always active: it just + //generates a fixed declaration. + return !hasSettingLookups; + } + + protected getDeclarations(): string[] { + return this.generator.getValue(); + } + + dispose(): void { + this.cssDeclarations.dispose(); + } + } + + abstract class CssStatement { + public readonly cssText: KnockoutComputed; + + protected constructor() { + this.cssText = ko.computed({ + read: () => this.generateCss(), + deferEvaluation: true, + }).extend({deferred: true}); + } + + dispose(): void { + //Dispose the CSS text observable. + this.cssText.dispose(); + } + + protected abstract generateCss(): string; + + public abstract isActive(): boolean; + } + + class CssRuleSet extends CssStatement { + protected readonly effectiveSelectors: string[]; + private readonly selectorText: string; + private readonly nestedRuleSets: KnockoutObservableArray; + + constructor( + selectors: string[], + private readonly declarationSources: DeclarationSource[], + parent: CssRuleSet | null = null + ) { + super(); + this.nestedRuleSets = ko.observableArray([]); + + if (parent === null) { + this.effectiveSelectors = selectors; + } else { + this.effectiveSelectors = CssRuleSet.combineSelectors(selectors, parent.effectiveSelectors); + } + this.selectorText = this.effectiveSelectors.join(', '); + } + + protected static combineSelectors(selectors: string[], parentSelectors: string[]): string[] { + const combinedSelectors: string[] = []; + + for (const selector of selectors) { + if (selector === '') { + continue; + } + + if (selector.includes('&')) { + //Insert the parent selectors into the current selector at the position of the "&". + for (const parentSelector of parentSelectors) { + combinedSelectors.push(selector.replace('&', parentSelector.trim())); + } + } else { + //Just append the current selector to the parent selectors. + for (const parentSelector of parentSelectors) { + combinedSelectors.push(`${parentSelector} ${selector}`); + } + } + } + + return combinedSelectors; + } + + setNestedRuleSets(ruleSets: CssRuleSet[]): void { + //Dispose the old rule sets that are not part of the new list. + for (const oldRuleSet of this.nestedRuleSets()) { + if (ruleSets.indexOf(oldRuleSet) === -1) { + oldRuleSet.dispose(); + } + } + this.nestedRuleSets(ruleSets); + } + + protected generateCss(): string { + const declarations = this.getDeclarations(); + const nestedRuleSetParts: string[] = []; + for (const ruleSet of this.nestedRuleSets()) { + if (ruleSet.isActive()) { + nestedRuleSetParts.push(ruleSet.cssText()); + } + } + + let css = ''; + + if (declarations.length > 0) { + css += this.selectorText + ' {\n\t' + declarations.join('\n\t') + '\n}\n'; + } + if (nestedRuleSetParts.length > 0) { + css += nestedRuleSetParts.join('\n'); + } + return css; + } + + isActive(): boolean { + for (const source of this.declarationSources) { + if (source.isActive()) { + return true; + } + } + for (const ruleSet of this.nestedRuleSets()) { + if (ruleSet.isActive()) { + return true; + } + } + return false; + } + + protected getDeclarations(): string[] { + const declarations = []; + for (const source of this.declarationSources) { + if (source.isActive()) { + declarations.push(...source.cssDeclarations()); + } + } + return declarations; + } + + dispose(): void { + //Dispose declaration sources. + for (const source of this.declarationSources) { + source.dispose(); + } + //Dispose nested rule sets. + for (const ruleSet of this.nestedRuleSets()) { + ruleSet.dispose(); + } + super.dispose(); + } + } + + class ConditionalAtRule extends CssStatement { + constructor( + private readonly identifier: string, + private readonly condition: string, + private readonly nestedStatements: CssStatement[] + ) { + super(); + } + + protected generateCss(): string { + const pieces = []; + for (const statement of this.nestedStatements) { + const css = statement.cssText(); + if (css !== '') { + pieces.push(css); + } + } + if (pieces.length === 0) { + return ''; + } + return this.getAtRuleText() + ' {\n\t' + pieces.join('\n\t') + '\n}'; + } + + protected getAtRuleText(): string { + return '@' + this.identifier + ' ' + this.condition; + } + + isActive(): boolean { + for (const statement of this.nestedStatements) { + if (statement.isActive()) { + return true; + } + } + return false; + } + + dispose(): void { + //Dispose nested statements. + for (const statement of this.nestedStatements) { + statement.dispose(); + } + super.dispose(); + } + } + + class PreviewStyleBlock { + private readonly cssText: KnockoutComputed; + private $styleElement: JQuery | null = null; + private cssChangeSubscription: KnockoutSubscription; + + constructor( + private readonly statements: CssStatement[], + private readonly condition: null | (() => boolean) = null + ) { + this.cssText = ko.computed({ + read: () => { + if ((condition !== null) && !condition()) { + return ''; + } + + let pieces = []; + for (const statement of this.statements) { + if (statement.isActive()) { + const css = statement.cssText(); + if (css !== '') { + pieces.push(css); + } + } + } + if (pieces.length === 0) { + return ''; + } + + return pieces.join('\n'); + }, + deferEvaluation: true, + }).extend({deferred: true}); + + this.updateStyleElement(this.cssText()); + + this.cssChangeSubscription = this.cssText.subscribe((cssText) => { + this.updateStyleElement(cssText); + }); + } + + private updateStyleElement(cssText: string): void { + if (cssText === '') { + if (this.$styleElement) { + this.$styleElement.remove(); + this.$styleElement = null; + } + return; + } + + if (!this.$styleElement) { + this.$styleElement = $('').appendTo('head'); + } + this.$styleElement.text(cssText); + } + + private clear(): void { + if (this.$styleElement) { + this.$styleElement.remove(); + this.$styleElement = null; + } + } + + dispose(): void { + //Stop listening for CSS changes. + this.cssChangeSubscription.dispose(); + this.cssText.dispose(); + + //Dispose rule sets. + for (const ruleset of this.statements) { + ruleset.dispose(); + } + + //Remove the style element. + this.clear(); + } + } + } +} \ No newline at end of file diff --git a/extras/zod/LICENSE b/extras/zod/LICENSE new file mode 100644 index 0000000..2c93bb5 --- /dev/null +++ b/extras/zod/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Colin McDonnell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/extras/zod/README.md b/extras/zod/README.md new file mode 100644 index 0000000..ec988f6 --- /dev/null +++ b/extras/zod/README.md @@ -0,0 +1,2739 @@ +

    + Zod logo +

    Zod

    +

    + ✨ https://zod.dev ✨ +
    + TypeScript-first schema validation with static type inference +

    +

    +
    +

    +Zod CI status +Created by Colin McDonnell +License +npm +stars +discord server +

    + +
    + Documentation +   •   + Discord +   •   + npm +   •   + deno +   •   + Issues +   •   + @colinhacks +   •   + tRPC +
    +
    + +
    +
    + +> These docs have been translated into [Chinese](./README_ZH.md). + +## Table of contents + + + +- [Introduction](#introduction) + - [Sponsors](#sponsors) + - [Ecosystem](#ecosystem) +- [Installation](#installation) + - [Requirements](#requirements) + - [Node/npm](#from-npm-nodebun) + - [Deno](#from-denolandx-deno) +- [Basic usage](#basic-usage) +- [Primitives](#primitives) +- [Coercion for primitives](#coercion-for-primitives) +- [Literals](#literals) +- [Strings](#strings) + - [Datetime](#datetime-validation) + - [IP](#ip-address-validation) +- [Numbers](#numbers) +- [BigInts](#bigints) +- [NaNs](#nans) +- [Booleans](#booleans) +- [Dates](#dates) +- [Zod enums](#zod-enums) +- [Native enums](#native-enums) +- [Optionals](#optionals) +- [Nullables](#nullables) +- [Objects](#objects) + - [.shape](#shape) + - [.keyof](#keyof) + - [.extend](#extend) + - [.merge](#merge) + - [.pick/.omit](#pickomit) + - [.partial](#partial) + - [.deepPartial](#deepPartial) + - [.passthrough](#passthrough) + - [.strict](#strict) + - [.strip](#strip) + - [.catchall](#catchall) +- [Arrays](#arrays) + - [.element](#element) + - [.nonempty](#nonempty) + - [.min/.max/.length](#minmaxlength) +- [Tuples](#tuples) +- [Unions](#unions) +- [Discriminated Unions](#discriminated-unions) +- [Records](#records) +- [Maps](#maps) +- [Sets](#sets) +- [Intersections](#intersections) +- [Recursive types](#recursive-types) + - [JSON type](#json-type) + - [Cyclical data](#cyclical-objects) +- [Promises](#promises) +- [Instanceof](#instanceof) +- [Functions](#functions) +- [Preprocess](#preprocess) +- [Custom](#custom-schemas) +- [Schema methods](#schema-methods) + - [.parse](#parse) + - [.parseAsync](#parseasync) + - [.safeParse](#safeparse) + - [.safeParseAsync](#safeparseasync) + - [.refine](#refine) + - [.superRefine](#superRefine) + - [.transform](#transform) + - [.default](#default) + - [.describe](#describe) + - [.catch](#catch) + - [.optional](#optional) + - [.nullable](#nullable) + - [.nullish](#nullish) + - [.array](#array) + - [.promise](#promise) + - [.or](#or) + - [.and](#and) + - [.brand](#brand) + - [.pipe](#pipe) +- [Guides and concepts](#guides-and-concepts) + - [Type inference](#type-inference) + - [Writing generic functions](#writing-generic-functions) + - [Error handling](#error-handling) + - [Error formatting](#error-formatting) +- [Comparison](#comparison) + - [Joi](#joi) + - [Yup](#yup) + - [io-ts](#io-ts) + - [Runtypes](#runtypes) +- [Changelog](#changelog) + + + +## Introduction + +Zod is a TypeScript-first schema declaration and validation library. I'm using the term "schema" to broadly refer to any data type, from a simple `string` to a complex nested object. + +Zod is designed to be as developer-friendly as possible. The goal is to eliminate duplicative type declarations. With Zod, you declare a validator _once_ and Zod will automatically infer the static TypeScript type. It's easy to compose simpler types into complex data structures. + +Some other great aspects: + +- Zero dependencies +- Works in Node.js and all modern browsers +- Tiny: 8kb minified + zipped +- Immutable: methods (e.g. `.optional()`) return a new instance +- Concise, chainable interface +- Functional approach: [parse, don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) +- Works with plain JavaScript too! You don't need to use TypeScript. + +### Sponsors + +Sponsorship at any level is appreciated and encouraged. For individual developers, consider the [Cup of Coffee tier](https://github.com/sponsors/colinhacks). If you built a paid product using Zod, consider one of the [podium tiers](https://github.com/sponsors/colinhacks). + +#### Gold + + + + + + + + + + + + + + +
    + + Astro + +
    + Astro +
    + astro.build +
    +

    + Astro is a new kind of static
    + site builder for the modern web.
    + Powerful developer experience meets
    + lightweight output.

    +
    + + + +
    + Glow Wallet +
    + glow.app +
    +

    Your new favorite +
    + Solana wallet.

    +
    + + Deletype logo + +
    + Deletype +
    + deletype.com +
    + + Proxy logo + +
    + Proxy +
    + proxy.com +
    + + Trigger.dev logo + +
    + Trigger.dev +
    + trigger.dev +
    + +#### Silver + + + + + + + + + + + + + + +
    + + Numeric logo + +
    + Numeric +
    + numeric.io +
    + + Snaplet logo + +
    + Snaplet +
    + snaplet.dev +
    + + Marcato Partners + +
    + Marcato Partners +
    + marcatopartners.com +
    + + + +
    + Interval +
    + interval.com +
    + + + +
    + Seasoned Software +
    + seasoned.cc +
    + + Bamboo Creative logo + +
    + Bamboo Creative +
    + bamboocreative.nz +
    + +#### Bronze + + + + + + + + + + + + + + + + + +
    + + + +
    + Brandon Bayer +
    + @flybayer, + creator of Blitz.js +
    +
    + + + +
    + Jiří Brabec +
    + @brabeji +
    +
    + + + +
    + Alex Johansson +
    + @alexdotjs +
    + + Fungible Systems logo + +
    + Fungible Systems +
    + fungible.systems +
    +
    + + + +
    + Adaptable +
    + adaptable.io +
    +
    + + Avana Wallet logo + +
    + Avana Wallet +
    + avanawallet.com
    + Solana non-custodial wallet +
    +
    + + Learn with Jason logo + +
    + Jason Lengstorf +
    + learnwithjason.dev +
    +
    + + Global Illumination + +
    + Global Illumination, Inc. +
    + ill.inc +
    +
    + +### Ecosystem + +There are a growing number of tools that are built atop or support Zod natively! If you've built a tool or library on top of Zod, tell me about it [on Twitter](https://twitter.com/colinhacks) or [start a Discussion](https://github.com/colinhacks/zod/discussions). I'll add it below and tweet it out. + +#### Resources + +- [Total TypeScript Zod Tutorial](https://www.totaltypescript.com/tutorials/zod) by [@mattpocockuk](https://twitter.com/mattpocockuk) +- [Fixing TypeScript's Blindspot: Runtime Typechecking](https://www.youtube.com/watch?v=rY_XqfSHock) by [@jherr](https://twitter.com/jherr) + +#### API libraries + +- [`tRPC`](https://github.com/trpc/trpc): Build end-to-end typesafe APIs without GraphQL. +- [`@anatine/zod-nestjs`](https://github.com/anatine/zod-plugins/tree/main/packages/zod-nestjs): Helper methods for using Zod in a NestJS project. +- [`zod-endpoints`](https://github.com/flock-community/zod-endpoints): Contract-first strictly typed endpoints with Zod. OpenAPI compatible. +- [`domain-functions`](https://github.com/SeasonedSoftware/domain-functions/): Decouple your business logic from your framework using composable functions. With first-class type inference from end to end powered by Zod schemas. +- [`@zodios/core`](https://github.com/ecyrbe/zodios): A typescript API client with runtime and compile time validation backed by axios and zod. +- [`express-zod-api`](https://github.com/RobinTail/express-zod-api): Build Express-based APIs with I/O schema validation and custom middlewares. + +#### Form integrations + +- [`react-hook-form`](https://github.com/react-hook-form/resolvers#zod): A first-party Zod resolver for React Hook Form. +- [`zod-validation-error`](https://github.com/causaly/zod-validation-error): Generate user-friendly error messages from `ZodError`s. +- [`zod-formik-adapter`](https://github.com/robertLichtnow/zod-formik-adapter): A community-maintained Formik adapter for Zod. +- [`react-zorm`](https://github.com/esamattis/react-zorm): Standalone `` generation and validation for React using Zod. +- [`zodix`](https://github.com/rileytomasek/zodix): Zod utilities for FormData and URLSearchParams in Remix loaders and actions. +- [`remix-params-helper`](https://github.com/kiliman/remix-params-helper): Simplify integration of Zod with standard URLSearchParams and FormData for Remix apps. +- [`formik-validator-zod`](https://github.com/glazy/formik-validator-zod): Formik-compliant validator library that simplifies using Zod with Formik. +- [`zod-i18n-map`](https://github.com/aiji42/zod-i18n): Useful for translating Zod error messages. +- [`@modular-forms/solid`](https://github.com/fabian-hiller/modular-forms): Modular form library for SolidJS that supports Zod for validation. +- [`houseform`](https://github.com/crutchcorn/houseform/): A React form library that uses Zod for validation. + +#### Zod to X + +- [`zod-to-ts`](https://github.com/sachinraja/zod-to-ts): Generate TypeScript definitions from Zod schemas. +- [`zod-to-json-schema`](https://github.com/StefanTerdell/zod-to-json-schema): Convert your Zod schemas into [JSON Schemas](https://json-schema.org/). +- [`@anatine/zod-openapi`](https://github.com/anatine/zod-plugins/tree/main/packages/zod-openapi): Converts a Zod schema to an OpenAPI v3.x `SchemaObject`. +- [`zod-fast-check`](https://github.com/DavidTimms/zod-fast-check): Generate `fast-check` arbitraries from Zod schemas. +- [`zod-dto`](https://github.com/kbkk/abitia/tree/master/packages/zod-dto): Generate Nest.js DTOs from a Zod schema. +- [`fastify-type-provider-zod`](https://github.com/turkerdev/fastify-type-provider-zod): Create Fastify type providers from Zod schemas. +- [`zod-to-openapi`](https://github.com/asteasolutions/zod-to-openapi): Generate full OpenAPI (Swagger) docs from Zod, including schemas, endpoints & parameters. +- [`nestjs-graphql-zod`](https://github.com/incetarik/nestjs-graphql-zod): Generates NestJS GraphQL model classes from Zod schemas. Provides GraphQL method decorators working with Zod schemas. + +#### X to Zod + +- [`ts-to-zod`](https://github.com/fabien0102/ts-to-zod): Convert TypeScript definitions into Zod schemas. +- [`@runtyping/zod`](https://github.com/johngeorgewright/runtyping/tree/master/packages/zod): Generate Zod from static types & JSON schema. +- [`json-schema-to-zod`](https://github.com/StefanTerdell/json-schema-to-zod): Convert your [JSON Schemas](https://json-schema.org/) into Zod schemas. [Live demo](https://StefanTerdell.github.io/json-schema-to-zod-react/). +- [`json-to-zod`](https://github.com/rsinohara/json-to-zod): Convert JSON objects into Zod schemas. [Live demo](https://rsinohara.github.io/json-to-zod-react/). +- [`graphql-codegen-typescript-validation-schema`](https://github.com/Code-Hex/graphql-codegen-typescript-validation-schema): GraphQL Code Generator plugin to generate form validation schema from your GraphQL schema. +- [`zod-prisma`](https://github.com/CarterGrimmeisen/zod-prisma): Generate Zod schemas from your Prisma schema. +- [`Supervillain`](https://github.com/Southclaws/supervillain): Generate Zod schemas from your Go structs. +- [`prisma-zod-generator`](https://github.com/omar-dulaimi/prisma-zod-generator): Emit Zod schemas from your Prisma schema. +- [`prisma-trpc-generator`](https://github.com/omar-dulaimi/prisma-trpc-generator): Emit fully implemented tRPC routers and their validation schemas using Zod. +- [`zod-prisma-types`](https://github.com/chrishoermann/zod-prisma-types) Create Zod types from your Prisma models. + +#### Mocking + +- [`@anatine/zod-mock`](https://github.com/anatine/zod-plugins/tree/main/packages/zod-mock): Generate mock data from a Zod schema. Powered by [faker.js](https://github.com/faker-js/faker). +- [`zod-mocking`](https://github.com/dipasqualew/zod-mocking): Generate mock data from your Zod schemas. + +#### Powered by Zod + +- [`slonik`](https://github.com/gajus/slonik/tree/gajus/add-zod-validation-backwards-compatible#runtime-validation-and-static-type-inference): Node.js Postgres client with strong Zod integration. +- [`soly`](https://github.com/mdbetancourt/soly): Create CLI applications with zod. +- [`zod-xlsx`](https://github.com/sidwebworks/zod-xlsx): A xlsx based resource validator using Zod schemas. +- [`znv`](https://github.com/lostfictions/znv): Type-safe environment parsing and validation for Node.js with Zod schemas. + +#### Utilities for Zod + +- [`zod_utilz`](https://github.com/JacobWeisenburger/zod_utilz): Framework agnostic utilities for Zod. + +## Installation + +### Requirements + +- TypeScript 4.5+! +- You must enable `strict` mode in your `tsconfig.json`. This is a best practice for all TypeScript projects. + + ```ts + // tsconfig.json + { + // ... + "compilerOptions": { + // ... + "strict": true + } + } + ``` + +### From `npm` (Node/Bun) + +```sh +npm install zod # npm +yarn add zod # yarn +bun add zod # bun +pnpm add zod # pnpm +``` + +### From `deno.land/x` (Deno) + +Unlike Node, Deno relies on direct URL imports instead of a package manager like NPM. Zod is available on [deno.land/x](https://deno.land/x). The latest version can be imported like so: + +```ts +import { z } from "https://deno.land/x/zod/mod.ts"; +``` + +You can also specify a particular version: + +```ts +import { z } from "https://deno.land/x/zod@v3.16.1/mod.ts"; +``` + +> The rest of this README assumes you are using npm and importing directly from the `"zod"` package. + +## Basic usage + +Creating a simple string schema + +```ts +import { z } from "zod"; + +// creating a schema for strings +const mySchema = z.string(); + +// parsing +mySchema.parse("tuna"); // => "tuna" +mySchema.parse(12); // => throws ZodError + +// "safe" parsing (doesn't throw error if validation fails) +mySchema.safeParse("tuna"); // => { success: true; data: "tuna" } +mySchema.safeParse(12); // => { success: false; error: ZodError } +``` + +Creating an object schema + +```ts +import { z } from "zod"; + +const User = z.object({ + username: z.string(), +}); + +User.parse({ username: "Ludwig" }); + +// extract the inferred type +type User = z.infer; +// { username: string } +``` + +## Primitives + +```ts +import { z } from "zod"; + +// primitive values +z.string(); +z.number(); +z.bigint(); +z.boolean(); +z.date(); +z.symbol(); + +// empty types +z.undefined(); +z.null(); +z.void(); // accepts undefined + +// catch-all types +// allows any value +z.any(); +z.unknown(); + +// never type +// allows no values +z.never(); +``` + +## Coercion for primitives + +Zod now provides a more convenient way to coerce primitive values. + +```ts +const schema = z.coerce.string(); +schema.parse("tuna"); // => "tuna" +schema.parse(12); // => "12" +schema.parse(true); // => "true" +``` + +During the parsing step, the input is passed through the `String()` function, which is a JavaScript built-in for coercing data into strings. Note that the returned schema is a `ZodString` instance so you can use all string methods. + +```ts +z.coerce.string().email().min(5); +``` + +All primitive types support coercion. + +```ts +z.coerce.string(); // String(input) +z.coerce.number(); // Number(input) +z.coerce.boolean(); // Boolean(input) +z.coerce.bigint(); // BigInt(input) +z.coerce.date(); // new Date(input) +``` + +**Boolean coercion** + +Zod's boolean coercion is very simple! It passes the value into the `Boolean(value)` function, that's it. Any truthy value will resolve to `true`, any falsy value will resolve to `false`. + +```ts +z.coerce.boolean().parse("tuna"); // => true +z.coerce.boolean().parse("true"); // => true +z.coerce.boolean().parse("false"); // => true +z.coerce.boolean().parse(1); // => true +z.coerce.boolean().parse([]); // => true + +z.coerce.boolean().parse(0); // => false +z.coerce.boolean().parse(undefined); // => false +z.coerce.boolean().parse(null); // => false +``` + +## Literals + +Literal schemas represent a [literal type](https://www.typescriptlang.org/docs/handbook/literal-types.html), like `"hello world"` or `5`. + +```ts +const tuna = z.literal("tuna"); +const twelve = z.literal(12); +const twobig = z.literal(2n); // bigint literal +const tru = z.literal(true); + +const terrificSymbol = Symbol("terrific"); +const terrific = z.literal(terrificSymbol); + +// retrieve literal value +tuna.value; // "tuna" +``` + +> Currently there is no support for Date literals in Zod. If you have a use case for this feature, please file an issue. + +## Strings + +Zod includes a handful of string-specific validations. + +```ts +// validations +z.string().max(5); +z.string().min(5); +z.string().length(5); +z.string().email(); +z.string().url(); +z.string().emoji(); +z.string().uuid(); +z.string().cuid(); +z.string().cuid2(); +z.string().ulid(); +z.string().regex(regex); +z.string().includes(string); +z.string().startsWith(string); +z.string().endsWith(string); +z.string().datetime(); // defaults to UTC, see below for options +z.string().ip(); // defaults to IPv4 and IPv6, see below for options + +// transformations +z.string().trim(); // trim whitespace +z.string().toLowerCase(); // toLowerCase +z.string().toUpperCase(); // toUpperCase +``` + +> Check out [validator.js](https://github.com/validatorjs/validator.js) for a bunch of other useful string validation functions that can be used in conjunction with [Refinements](#refine). + +You can customize some common error messages when creating a string schema. + +```ts +const name = z.string({ + required_error: "Name is required", + invalid_type_error: "Name must be a string", +}); +``` + +When using validation methods, you can pass in an additional argument to provide a custom error message. + +```ts +z.string().min(5, { message: "Must be 5 or more characters long" }); +z.string().max(5, { message: "Must be 5 or fewer characters long" }); +z.string().length(5, { message: "Must be exactly 5 characters long" }); +z.string().email({ message: "Invalid email address" }); +z.string().url({ message: "Invalid url" }); +z.string().emoji({ message: "Contains non-emoji characters" }); +z.string().uuid({ message: "Invalid UUID" }); +z.string().includes("tuna", { message: "Must include tuna" }); +z.string().startsWith("https://", { message: "Must provide secure URL" }); +z.string().endsWith(".com", { message: "Only .com domains allowed" }); +z.string().datetime({ message: "Invalid datetime string! Must be UTC." }); +z.string().ip({ message: "Invalid IP address" }); +``` + +### ISO datetimes + +The `z.string().datetime()` method defaults to UTC validation: no timezone offsets with arbitrary sub-second decimal precision. + +```ts +const datetime = z.string().datetime(); + +datetime.parse("2020-01-01T00:00:00Z"); // pass +datetime.parse("2020-01-01T00:00:00.123Z"); // pass +datetime.parse("2020-01-01T00:00:00.123456Z"); // pass (arbitrary precision) +datetime.parse("2020-01-01T00:00:00+02:00"); // fail (no offsets allowed) +``` + +Timezone offsets can be allowed by setting the `offset` option to `true`. + +```ts +const datetime = z.string().datetime({ offset: true }); + +datetime.parse("2020-01-01T00:00:00+02:00"); // pass +datetime.parse("2020-01-01T00:00:00.123+02:00"); // pass (millis optional) +datetime.parse("2020-01-01T00:00:00.123+0200"); // pass (millis optional) +datetime.parse("2020-01-01T00:00:00.123+02"); // pass (only offset hours) +datetime.parse("2020-01-01T00:00:00Z"); // pass (Z still supported) +``` + +You can additionally constrain the allowable `precision`. By default, arbitrary sub-second precision is supported (but optional). + +```ts +const datetime = z.string().datetime({ precision: 3 }); + +datetime.parse("2020-01-01T00:00:00.123Z"); // pass +datetime.parse("2020-01-01T00:00:00Z"); // fail +datetime.parse("2020-01-01T00:00:00.123456Z"); // fail +``` + +### IP addresses + +The `z.string().ip()` method by default validate IPv4 and IPv6. + +```ts +const ip = z.string().ip(); + +ip.parse("192.168.1.1"); // pass +ip.parse("84d5:51a0:9114:1855:4cfa:f2d7:1f12:7003"); // pass +ip.parse("84d5:51a0:9114:1855:4cfa:f2d7:1f12:192.168.1.1"); // pass + +ip.parse("256.1.1.1"); // fail +ip.parse("84d5:51a0:9114:gggg:4cfa:f2d7:1f12:7003"); // fail +``` + +You can additionally set the IP `version`. + +```ts +const ipv4 = z.string().ip({ version: "v4" }); +ipv4.parse("84d5:51a0:9114:1855:4cfa:f2d7:1f12:7003"); // fail + +const ipv6 = z.string().ip({ version: "v6" }); +ipv6.parse("192.168.1.1"); // fail +``` + +## Numbers + +You can customize certain error messages when creating a number schema. + +```ts +const age = z.number({ + required_error: "Age is required", + invalid_type_error: "Age must be a number", +}); +``` + +Zod includes a handful of number-specific validations. + +```ts +z.number().gt(5); +z.number().gte(5); // alias .min(5) +z.number().lt(5); +z.number().lte(5); // alias .max(5) + +z.number().int(); // value must be an integer + +z.number().positive(); // > 0 +z.number().nonnegative(); // >= 0 +z.number().negative(); // < 0 +z.number().nonpositive(); // <= 0 + +z.number().multipleOf(5); // Evenly divisible by 5. Alias .step(5) + +z.number().finite(); // value must be finite, not Infinity or -Infinity +z.number().safe(); // value must be between Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER +``` + +Optionally, you can pass in a second argument to provide a custom error message. + +```ts +z.number().lte(5, { message: "this👏is👏too👏big" }); +``` + +## BigInts + +Zod includes a handful of bigint-specific validations. + +```ts +z.bigint().gt(5n); +z.bigint().gte(5n); // alias `.min(5n)` +z.bigint().lt(5n); +z.bigint().lte(5n); // alias `.max(5n)` + +z.bigint().positive(); // > 0n +z.bigint().nonnegative(); // >= 0n +z.bigint().negative(); // < 0n +z.bigint().nonpositive(); // <= 0n + +z.bigint().multipleOf(5n); // Evenly divisible by 5n. +``` + +## NaNs + +You can customize certain error messages when creating a nan schema. + +```ts +const isNaN = z.nan({ + required_error: "isNaN is required", + invalid_type_error: "isNaN must be not a number", +}); +``` + +## Booleans + +You can customize certain error messages when creating a boolean schema. + +```ts +const isActive = z.boolean({ + required_error: "isActive is required", + invalid_type_error: "isActive must be a boolean", +}); +``` + +## Dates + +Use z.date() to validate `Date` instances. + +```ts +z.date().safeParse(new Date()); // success: true +z.date().safeParse("2022-01-12T00:00:00.000Z"); // success: false +``` + +You can customize certain error messages when creating a date schema. + +```ts +const myDateSchema = z.date({ + required_error: "Please select a date and time", + invalid_type_error: "That's not a date!", +}); +``` + +Zod provides a handful of date-specific validations. + +```ts +z.date().min(new Date("1900-01-01"), { message: "Too old" }); +z.date().max(new Date(), { message: "Too young!" }); +``` + +**Coercion to Date** + +Since [zod 3.20](https://github.com/colinhacks/zod/releases/tag/v3.20), use [`z.coerce.date()`](#coercion-for-primitives) to pass the input through `new Date(input)`. + +```ts +const dateSchema = z.coerce.date(); +type DateSchema = z.infer; +// type DateSchema = Date + +/* valid dates */ +console.log(dateSchema.safeParse("2023-01-10T00:00:00.000Z").success); // true +console.log(dateSchema.safeParse("2023-01-10").success); // true +console.log(dateSchema.safeParse("1/10/23").success); // true +console.log(dateSchema.safeParse(new Date("1/10/23")).success); // true + +/* invalid dates */ +console.log(dateSchema.safeParse("2023-13-10").success); // false +console.log(dateSchema.safeParse("0000-00-00").success); // false +``` + +For older zod versions, use [`z.preprocess`](#preprocess) like [described in this thread](https://github.com/colinhacks/zod/discussions/879#discussioncomment-2036276). + +## Zod enums + +```ts +const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]); +type FishEnum = z.infer; +// 'Salmon' | 'Tuna' | 'Trout' +``` + +`z.enum` is a Zod-native way to declare a schema with a fixed set of allowable _string_ values. Pass the array of values directly into `z.enum()`. Alternatively, use `as const` to define your enum values as a tuple of strings. See the [const assertion docs](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions) for details. + +```ts +const VALUES = ["Salmon", "Tuna", "Trout"] as const; +const FishEnum = z.enum(VALUES); +``` + +This is not allowed, since Zod isn't able to infer the exact values of each element. + +```ts +const fish = ["Salmon", "Tuna", "Trout"]; +const FishEnum = z.enum(fish); +``` + +**Autocompletion** + +To get autocompletion with a Zod enum, use the `.enum` property of your schema: + +```ts +FishEnum.enum.Salmon; // => autocompletes + +FishEnum.enum; +/* +=> { + Salmon: "Salmon", + Tuna: "Tuna", + Trout: "Trout", +} +*/ +``` + +You can also retrieve the list of options as a tuple with the `.options` property: + +```ts +FishEnum.options; // ["Salmon", "Tuna", "Trout"]; +``` + +## Native enums + +Zod enums are the recommended approach to defining and validating enums. But if you need to validate against an enum from a third-party library (or you don't want to rewrite your existing enums) you can use `z.nativeEnum()`. + +**Numeric enums** + +```ts +enum Fruits { + Apple, + Banana, +} + +const FruitEnum = z.nativeEnum(Fruits); +type FruitEnum = z.infer; // Fruits + +FruitEnum.parse(Fruits.Apple); // passes +FruitEnum.parse(Fruits.Banana); // passes +FruitEnum.parse(0); // passes +FruitEnum.parse(1); // passes +FruitEnum.parse(3); // fails +``` + +**String enums** + +```ts +enum Fruits { + Apple = "apple", + Banana = "banana", + Cantaloupe, // you can mix numerical and string enums +} + +const FruitEnum = z.nativeEnum(Fruits); +type FruitEnum = z.infer; // Fruits + +FruitEnum.parse(Fruits.Apple); // passes +FruitEnum.parse(Fruits.Cantaloupe); // passes +FruitEnum.parse("apple"); // passes +FruitEnum.parse("banana"); // passes +FruitEnum.parse(0); // passes +FruitEnum.parse("Cantaloupe"); // fails +``` + +**Const enums** + +The `.nativeEnum()` function works for `as const` objects as well. ⚠️ `as const` required TypeScript 3.4+! + +```ts +const Fruits = { + Apple: "apple", + Banana: "banana", + Cantaloupe: 3, +} as const; + +const FruitEnum = z.nativeEnum(Fruits); +type FruitEnum = z.infer; // "apple" | "banana" | 3 + +FruitEnum.parse("apple"); // passes +FruitEnum.parse("banana"); // passes +FruitEnum.parse(3); // passes +FruitEnum.parse("Cantaloupe"); // fails +``` + +You can access the underlying object with the `.enum` property: + +```ts +FruitEnum.enum.Apple; // "apple" +``` + +## Optionals + +You can make any schema optional with `z.optional()`. This wraps the schema in a `ZodOptional` instance and returns the result. + +```ts +const schema = z.optional(z.string()); + +schema.parse(undefined); // => returns undefined +type A = z.infer; // string | undefined +``` + +For convenience, you can also call the `.optional()` method on an existing schema. + +```ts +const user = z.object({ + username: z.string().optional(), +}); +type C = z.infer; // { username?: string | undefined }; +``` + +You can extract the wrapped schema from a `ZodOptional` instance with `.unwrap()`. + +```ts +const stringSchema = z.string(); +const optionalString = stringSchema.optional(); +optionalString.unwrap() === stringSchema; // true +``` + +## Nullables + +Similarly, you can create nullable types with `z.nullable()`. + +```ts +const nullableString = z.nullable(z.string()); +nullableString.parse("asdf"); // => "asdf" +nullableString.parse(null); // => null +``` + +Or use the `.nullable()` method. + +```ts +const E = z.string().nullable(); // equivalent to nullableString +type E = z.infer; // string | null +``` + +Extract the inner schema with `.unwrap()`. + +```ts +const stringSchema = z.string(); +const nullableString = stringSchema.nullable(); +nullableString.unwrap() === stringSchema; // true +``` + +## Objects + +```ts +// all properties are required by default +const Dog = z.object({ + name: z.string(), + age: z.number(), +}); + +// extract the inferred type like this +type Dog = z.infer; + +// equivalent to: +type Dog = { + name: string; + age: number; +}; +``` + +### `.shape` + +Use `.shape` to access the schemas for a particular key. + +```ts +Dog.shape.name; // => string schema +Dog.shape.age; // => number schema +``` + +### `.keyof` + +Use `.keyof` to create a `ZodEnum` schema from the keys of an object schema. + +```ts +const keySchema = Dog.keyof(); +keySchema; // ZodEnum<["name", "age"]> +``` + +### `.extend` + +You can add additional fields to an object schema with the `.extend` method. + +```ts +const DogWithBreed = Dog.extend({ + breed: z.string(), +}); +``` + +You can use `.extend` to overwrite fields! Be careful with this power! + +### `.merge` + +Equivalent to `A.extend(B.shape)`. + +```ts +const BaseTeacher = z.object({ students: z.array(z.string()) }); +const HasID = z.object({ id: z.string() }); + +const Teacher = BaseTeacher.merge(HasID); +type Teacher = z.infer; // => { students: string[], id: string } +``` + +> If the two schemas share keys, the properties of B overrides the property of A. The returned schema also inherits the "unknownKeys" policy (strip/strict/passthrough) and the catchall schema of B. + +### `.pick/.omit` + +Inspired by TypeScript's built-in `Pick` and `Omit` utility types, all Zod object schemas have `.pick` and `.omit` methods that return a modified version. Consider this Recipe schema: + +```ts +const Recipe = z.object({ + id: z.string(), + name: z.string(), + ingredients: z.array(z.string()), +}); +``` + +To only keep certain keys, use `.pick` . + +```ts +const JustTheName = Recipe.pick({ name: true }); +type JustTheName = z.infer; +// => { name: string } +``` + +To remove certain keys, use `.omit` . + +```ts +const NoIDRecipe = Recipe.omit({ id: true }); + +type NoIDRecipe = z.infer; +// => { name: string, ingredients: string[] } +``` + +### `.partial` + +Inspired by the built-in TypeScript utility type [Partial](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialt), the `.partial` method makes all properties optional. + +Starting from this object: + +```ts +const user = z.object({ + email: z.string() + username: z.string(), +}); +// { email: string; username: string } +``` + +We can create a partial version: + +```ts +const partialUser = user.partial(); +// { email?: string | undefined; username?: string | undefined } +``` + +You can also specify which properties to make optional: + +```ts +const optionalEmail = user.partial({ + email: true, +}); +/* +{ + email?: string | undefined; + username: string +} +*/ +``` + +### `.deepPartial` + +The `.partial` method is shallow — it only applies one level deep. There is also a "deep" version: + +```ts +const user = z.object({ + username: z.string(), + location: z.object({ + latitude: z.number(), + longitude: z.number(), + }), + strings: z.array(z.object({ value: z.string() })), +}); + +const deepPartialUser = user.deepPartial(); + +/* +{ + username?: string | undefined, + location?: { + latitude?: number | undefined; + longitude?: number | undefined; + } | undefined, + strings?: { value?: string}[] +} +*/ +``` + +> Important limitation: deep partials only work as expected in hierarchies of objects, arrays, and tuples. + +### `.required` + +Contrary to the `.partial` method, the `.required` method makes all properties required. + +Starting from this object: + +```ts +const user = z.object({ + email: z.string() + username: z.string(), +}).partial(); +// { email?: string | undefined; username?: string | undefined } +``` + +We can create a required version: + +```ts +const requiredUser = user.required(); +// { email: string; username: string } +``` + +You can also specify which properties to make required: + +```ts +const requiredEmail = user.required({ + email: true, +}); +/* +{ + email: string; + username?: string | undefined; +} +*/ +``` + +### `.passthrough` + +By default Zod object schemas strip out unrecognized keys during parsing. + +```ts +const person = z.object({ + name: z.string(), +}); + +person.parse({ + name: "bob dylan", + extraKey: 61, +}); +// => { name: "bob dylan" } +// extraKey has been stripped +``` + +Instead, if you want to pass through unknown keys, use `.passthrough()` . + +```ts +person.passthrough().parse({ + name: "bob dylan", + extraKey: 61, +}); +// => { name: "bob dylan", extraKey: 61 } +``` + +### `.strict` + +By default Zod object schemas strip out unrecognized keys during parsing. You can _disallow_ unknown keys with `.strict()` . If there are any unknown keys in the input, Zod will throw an error. + +```ts +const person = z + .object({ + name: z.string(), + }) + .strict(); + +person.parse({ + name: "bob dylan", + extraKey: 61, +}); +// => throws ZodError +``` + +### `.strip` + +You can use the `.strip` method to reset an object schema to the default behavior (stripping unrecognized keys). + +### `.catchall` + +You can pass a "catchall" schema into an object schema. All unknown keys will be validated against it. + +```ts +const person = z + .object({ + name: z.string(), + }) + .catchall(z.number()); + +person.parse({ + name: "bob dylan", + validExtraKey: 61, // works fine +}); + +person.parse({ + name: "bob dylan", + validExtraKey: false, // fails +}); +// => throws ZodError +``` + +Using `.catchall()` obviates `.passthrough()` , `.strip()` , or `.strict()`. All keys are now considered "known". + +## Arrays + +```ts +const stringArray = z.array(z.string()); + +// equivalent +const stringArray = z.string().array(); +``` + +Be careful with the `.array()` method. It returns a new `ZodArray` instance. This means the _order_ in which you call methods matters. For instance: + +```ts +z.string().optional().array(); // (string | undefined)[] +z.string().array().optional(); // string[] | undefined +``` + +### `.element` + +Use `.element` to access the schema for an element of the array. + +```ts +stringArray.element; // => string schema +``` + +### `.nonempty` + +If you want to ensure that an array contains at least one element, use `.nonempty()`. + +```ts +const nonEmptyStrings = z.string().array().nonempty(); +// the inferred type is now +// [string, ...string[]] + +nonEmptyStrings.parse([]); // throws: "Array cannot be empty" +nonEmptyStrings.parse(["Ariana Grande"]); // passes +``` + +You can optionally specify a custom error message: + +```ts +// optional custom error message +const nonEmptyStrings = z.string().array().nonempty({ + message: "Can't be empty!", +}); +``` + +### `.min/.max/.length` + +```ts +z.string().array().min(5); // must contain 5 or more items +z.string().array().max(5); // must contain 5 or fewer items +z.string().array().length(5); // must contain 5 items exactly +``` + +Unlike `.nonempty()` these methods do not change the inferred type. + +## Tuples + +Unlike arrays, tuples have a fixed number of elements and each element can have a different type. + +```ts +const athleteSchema = z.tuple([ + z.string(), // name + z.number(), // jersey number + z.object({ + pointsScored: z.number(), + }), // statistics +]); + +type Athlete = z.infer; +// type Athlete = [string, number, { pointsScored: number }] +``` + +A variadic ("rest") argument can be added with the `.rest` method. + +```ts +const variadicTuple = z.tuple([z.string()]).rest(z.number()); +const result = variadicTuple.parse(["hello", 1, 2, 3]); +// => [string, ...number[]]; +``` + +## Unions + +Zod includes a built-in `z.union` method for composing "OR" types. + +```ts +const stringOrNumber = z.union([z.string(), z.number()]); + +stringOrNumber.parse("foo"); // passes +stringOrNumber.parse(14); // passes +``` + +Zod will test the input against each of the "options" in order and return the first value that validates successfully. + +For convenience, you can also use the [`.or` method](#or): + +```ts +const stringOrNumber = z.string().or(z.number()); +``` + +**Optional string validation:** + +To validate an optional form input, you can union the desired string validation with an empty string [literal](#literals). + +This example validates an input that is optional but needs to contain a [valid URL](#strings): + +```ts +const optionalUrl = z.union([z.string().url().nullish(), z.literal("")]); + +console.log(optionalUrl.safeParse(undefined).success); // true +console.log(optionalUrl.safeParse(null).success); // true +console.log(optionalUrl.safeParse("").success); // true +console.log(optionalUrl.safeParse("https://zod.dev").success); // true +console.log(optionalUrl.safeParse("not a valid url").success); // false +``` + +## Discriminated unions + +A discriminated union is a union of object schemas that all share a particular key. + +```ts +type MyUnion = + | { status: "success"; data: string } + | { status: "failed"; error: Error }; +``` + +Such unions can be represented with the `z.discriminatedUnion` method. This enables faster evaluation, because Zod can check the _discriminator key_ (`status` in the example above) to determine which schema should be used to parse the input. This makes parsing more efficient and lets Zod report friendlier errors. + +With the basic union method, the input is tested against each of the provided "options", and in the case of invalidity, issues for all the "options" are shown in the zod error. On the other hand, the discriminated union allows for selecting just one of the "options", testing against it, and showing only the issues related to this "option". + +```ts +const myUnion = z.discriminatedUnion("status", [ + z.object({ status: z.literal("success"), data: z.string() }), + z.object({ status: z.literal("failed"), error: z.instanceof(Error) }), +]); + +myUnion.parse({ status: "success", data: "yippie ki yay" }); +``` + +## Records + +Record schemas are used to validate types such as `{ [k: string]: number }`. + +If you want to validate the _values_ of an object against some schema but don't care about the keys, use `z.record(valueType)`: + +```ts +const NumberCache = z.record(z.number()); + +type NumberCache = z.infer; +// => { [k: string]: number } +``` + +This is particularly useful for storing or caching items by ID. + +```ts +const userStore: UserStore = {}; + +userStore["77d2586b-9e8e-4ecf-8b21-ea7e0530eadd"] = { + name: "Carlotta", +}; // passes + +userStore["77d2586b-9e8e-4ecf-8b21-ea7e0530eadd"] = { + whatever: "Ice cream sundae", +}; // TypeError +``` + +### Record key type + +If you want to validate both the keys and the values, use +`z.record(keyType, valueType)`: + +```ts +const NoEmptyKeysSchema = z.record(z.string().min(1), z.number()); +NoEmptyKeysSchema.parse({ count: 1 }); // => { 'count': 1 } +NoEmptyKeysSchema.parse({ "": 1 }); // fails +``` + +_(Notice how when passing two arguments, `valueType` is the second argument)_ + +**A note on numerical keys** + +While `z.record(keyType, valueType)` is able to accept numerical key types and TypeScript's built-in Record type is `Record`, it's hard to represent the TypeScript type `Record` in Zod. + +As it turns out, TypeScript's behavior surrounding `[k: number]` is a little unintuitive: + +```ts +const testMap: { [k: number]: string } = { + 1: "one", +}; + +for (const key in testMap) { + console.log(`${key}: ${typeof key}`); +} +// prints: `1: string` +``` + +As you can see, JavaScript automatically casts all object keys to strings under the hood. Since Zod is trying to bridge the gap between static and runtime types, it doesn't make sense to provide a way of creating a record schema with numerical keys, since there's no such thing as a numerical key in runtime JavaScript. + +## Maps + +```ts +const stringNumberMap = z.map(z.string(), z.number()); + +type StringNumberMap = z.infer; +// type StringNumberMap = Map +``` + +## Sets + +```ts +const numberSet = z.set(z.number()); +type NumberSet = z.infer; +// type NumberSet = Set +``` + +Set schemas can be further constrained with the following utility methods. + +```ts +z.set(z.string()).nonempty(); // must contain at least one item +z.set(z.string()).min(5); // must contain 5 or more items +z.set(z.string()).max(5); // must contain 5 or fewer items +z.set(z.string()).size(5); // must contain 5 items exactly +``` + +## Intersections + +Intersections are useful for creating "logical AND" types. This is useful for intersecting two object types. + +```ts +const Person = z.object({ + name: z.string(), +}); + +const Employee = z.object({ + role: z.string(), +}); + +const EmployedPerson = z.intersection(Person, Employee); + +// equivalent to: +const EmployedPerson = Person.and(Employee); +``` + +Though in many cases, it is recommended to use `A.merge(B)` to merge two objects. The `.merge` method returns a new `ZodObject` instance, whereas `A.and(B)` returns a less useful `ZodIntersection` instance that lacks common object methods like `pick` and `omit`. + +```ts +const a = z.union([z.number(), z.string()]); +const b = z.union([z.number(), z.boolean()]); +const c = z.intersection(a, b); + +type c = z.infer; // => number +``` + + + + + +## Recursive types + +You can define a recursive schema in Zod, but because of a limitation of TypeScript, their type can't be statically inferred. Instead you'll need to define the type definition manually, and provide it to Zod as a "type hint". + +```ts +const baseCategorySchema = z.object({ + name: z.string(), +}); + +type Category = z.infer & { + subcategories: Category[]; +}; + +const categorySchema: z.ZodType = baseCategorySchema.extend({ + subcategories: z.lazy(() => categorySchema.array()), +}); + +categorySchema.parse({ + name: "People", + subcategories: [ + { + name: "Politicians", + subcategories: [ + { + name: "Presidents", + subcategories: [], + }, + ], + }, + ], +}); // passes +``` + +Thanks to [crasite](https://github.com/crasite) for this example. + +### ZodType with ZodEffects + +When using `z.ZodType` with `z.ZodEffects` ( +[`.refine`](https://github.com/colinhacks/zod#refine), +[`.transform`](https://github.com/colinhacks/zod#transform), +[`preprocess`](https://github.com/colinhacks/zod#preprocess), +etc... +), you will need to define the input and output types of the schema. `z.ZodType` + +```ts +const isValidId = (id: string): id is `${string}/${string}` => + id.split("/").length === 2; + +const baseSchema = z.object({ + id: z.string().refine(isValidId), +}); + +type Input = z.input & { + children: Input[]; +}; + +type Output = z.output & { + children: Output[]; +}; + +const schema: z.ZodType = baseSchema.extend({ + children: z.lazy(() => schema.array()), +}); +``` + +Thanks to [marcus13371337](https://github.com/marcus13371337) and [JoelBeeldi](https://github.com/JoelBeeldi) for this example. + +### JSON type + +If you want to validate any JSON value, you can use the snippet below. + +```ts +const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]); +type Literal = z.infer; +type Json = Literal | { [key: string]: Json } | Json[]; +const jsonSchema: z.ZodType = z.lazy(() => + z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)]) +); + +jsonSchema.parse(data); +``` + +Thanks to [ggoodman](https://github.com/ggoodman) for suggesting this. + +### Cyclical objects + +Despite supporting recursive schemas, passing cyclical data into Zod will cause an infinite loop. + +## Promises + +```ts +const numberPromise = z.promise(z.number()); +``` + +"Parsing" works a little differently with promise schemas. Validation happens in two parts: + +1. Zod synchronously checks that the input is an instance of Promise (i.e. an object with `.then` and `.catch` methods.). +2. Zod uses `.then` to attach an additional validation step onto the existing Promise. You'll have to use `.catch` on the returned Promise to handle validation failures. + +```ts +numberPromise.parse("tuna"); +// ZodError: Non-Promise type: string + +numberPromise.parse(Promise.resolve("tuna")); +// => Promise + +const test = async () => { + await numberPromise.parse(Promise.resolve("tuna")); + // ZodError: Non-number type: string + + await numberPromise.parse(Promise.resolve(3.14)); + // => 3.14 +}; +``` + + + +## Instanceof + +You can use `z.instanceof` to check that the input is an instance of a class. This is useful to validate inputs against classes that are exported from third-party libraries. + +```ts +class Test { + name: string; +} + +const TestSchema = z.instanceof(Test); + +const blob: any = "whatever"; +TestSchema.parse(new Test()); // passes +TestSchema.parse("blob"); // throws +``` + +## Functions + +Zod also lets you define "function schemas". This makes it easy to validate the inputs and outputs of a function without intermixing your validation code and "business logic". + +You can create a function schema with `z.function(args, returnType)` . + +```ts +const myFunction = z.function(); + +type myFunction = z.infer; +// => ()=>unknown +``` + +Define inputs and outputs. + +```ts +const myFunction = z + .function() + .args(z.string(), z.number()) // accepts an arbitrary number of arguments + .returns(z.boolean()); + +type myFunction = z.infer; +// => (arg0: string, arg1: number)=>boolean +``` + + + +Function schemas have an `.implement()` method which accepts a function and returns a new function that automatically validates its inputs and outputs. + +```ts +const trimmedLength = z + .function() + .args(z.string()) // accepts an arbitrary number of arguments + .returns(z.number()) + .implement((x) => { + // TypeScript knows x is a string! + return x.trim().length; + }); + +trimmedLength("sandwich"); // => 8 +trimmedLength(" asdf "); // => 4 +``` + +If you only care about validating inputs, just don't call the `.returns()` method. The output type will be inferred from the implementation. + +> You can use the special `z.void()` option if your function doesn't return anything. This will let Zod properly infer the type of void-returning functions. (Void-returning functions actually return undefined.) + +```ts +const myFunction = z + .function() + .args(z.string()) + .implement((arg) => { + return [arg.length]; + }); + +myFunction; // (arg: string)=>number[] +``` + +Extract the input and output schemas from a function schema. + +```ts +myFunction.parameters(); +// => ZodTuple<[ZodString, ZodNumber]> + +myFunction.returnType(); +// => ZodBoolean +``` + + + +## Preprocess + +> Zod now supports primitive coercion without the need for `.preprocess()`. See the [coercion docs](#coercion-for-primitives) for more information. + +Typically Zod operates under a "parse then transform" paradigm. Zod validates the input first, then passes it through a chain of transformation functions. (For more information about transforms, read the [.transform docs](#transform).) + +But sometimes you want to apply some transform to the input _before_ parsing happens. A common use case: type coercion. Zod enables this with the `z.preprocess()`. + +```ts +const castToString = z.preprocess((val) => String(val), z.string()); +``` + +This returns a `ZodEffects` instance. `ZodEffects` is a wrapper class that contains all logic pertaining to preprocessing, refinements, and transforms. + +## Custom schemas + +You can create a Zod schema for any TypeScript type by using `z.custom()`. This is useful for creating schemas for types that are not supported by Zod out of the box, such as template string literals. + +```ts +const px = z.custom<`${number}px`>((val) => { + return /^\d+px$/.test(val as string); +}); + +type px = z.infer; // `${number}px` + +px.parse("42px"); // "42px" +px.parse("42vw"); // throws; +``` + +If you don't provide a validation function, Zod will allow any value. This can be dangerous! + +```ts +z.custom<{ arg: string }>(); // performs no validation +``` + +You can customize the error message and other options by passing a second argument. This parameter works the same way as the params parameter of [`.refine`](#refine). + +```ts +z.custom<...>((val) => ..., "custom error message"); +``` + +## Schema methods + +All Zod schemas contain certain methods. + +### `.parse` + +`.parse(data: unknown): T` + +Given any Zod schema, you can call its `.parse` method to check `data` is valid. If it is, a value is returned with full type information! Otherwise, an error is thrown. + +> IMPORTANT: The value returned by `.parse` is a _deep clone_ of the variable you passed in. + +```ts +const stringSchema = z.string(); + +stringSchema.parse("fish"); // => returns "fish" +stringSchema.parse(12); // throws error +``` + +### `.parseAsync` + +`.parseAsync(data:unknown): Promise` + +If you use asynchronous [refinements](#refine) or [transforms](#transform) (more on those later), you'll need to use `.parseAsync`. + +```ts +const stringSchema = z.string().refine(async (val) => val.length <= 8); + +await stringSchema.parseAsync("hello"); // => returns "hello" +await stringSchema.parseAsync("hello world"); // => throws error +``` + +### `.safeParse` + +`.safeParse(data:unknown): { success: true; data: T; } | { success: false; error: ZodError; }` + +If you don't want Zod to throw errors when validation fails, use `.safeParse`. This method returns an object containing either the successfully parsed data or a ZodError instance containing detailed information about the validation problems. + +```ts +stringSchema.safeParse(12); +// => { success: false; error: ZodError } + +stringSchema.safeParse("billie"); +// => { success: true; data: 'billie' } +``` + +The result is a _discriminated union_, so you can handle errors very conveniently: + +```ts +const result = stringSchema.safeParse("billie"); +if (!result.success) { + // handle error then return + result.error; +} else { + // do something + result.data; +} +``` + +### `.safeParseAsync` + +> Alias: `.spa` + +An asynchronous version of `safeParse`. + +```ts +await stringSchema.safeParseAsync("billie"); +``` + +For convenience, this has been aliased to `.spa`: + +```ts +await stringSchema.spa("billie"); +``` + +### `.refine` + +`.refine(validator: (data:T)=>any, params?: RefineParams)` + +Zod lets you provide custom validation logic via _refinements_. (For advanced features like creating multiple issues and customizing error codes, see [`.superRefine`](#superrefine).) + +Zod was designed to mirror TypeScript as closely as possible. But there are many so-called "refinement types" you may wish to check for that can't be represented in TypeScript's type system. For instance: checking that a number is an integer or that a string is a valid email address. + +For example, you can define a custom validation check on _any_ Zod schema with `.refine` : + +```ts +const myString = z.string().refine((val) => val.length <= 255, { + message: "String can't be more than 255 characters", +}); +``` + +> ⚠️ Refinement functions should not throw. Instead they should return a falsy value to signal failure. + +#### Arguments + +As you can see, `.refine` takes two arguments. + +1. The first is the validation function. This function takes one input (of type `T` — the inferred type of the schema) and returns `any`. Any truthy value will pass validation. (Prior to zod@1.6.2 the validation function had to return a boolean.) +2. The second argument accepts some options. You can use this to customize certain error-handling behavior: + +```ts +type RefineParams = { + // override error message + message?: string; + + // appended to error path + path?: (string | number)[]; + + // params object you can use to customize message + // in error map + params?: object; +}; +``` + +For advanced cases, the second argument can also be a function that returns `RefineParams`. + +```ts +const longString = z.string().refine( + (val) => val.length > 10, + (val) => ({ message: `${val} is not more than 10 characters` }) +); +``` + +#### Customize error path + +```ts +const passwordForm = z + .object({ + password: z.string(), + confirm: z.string(), + }) + .refine((data) => data.password === data.confirm, { + message: "Passwords don't match", + path: ["confirm"], // path of error + }); + +passwordForm.parse({ password: "asdf", confirm: "qwer" }); +``` + +Because you provided a `path` parameter, the resulting error will be: + +```ts +ZodError { + issues: [{ + "code": "custom", + "path": [ "confirm" ], + "message": "Passwords don't match" + }] +} +``` + +#### Asynchronous refinements + +Refinements can also be async: + +```ts +const userId = z.string().refine(async (id) => { + // verify that ID exists in database + return true; +}); +``` + +> ⚠️ If you use async refinements, you must use the `.parseAsync` method to parse data! Otherwise Zod will throw an error. + +#### Relationship to transforms + +Transforms and refinements can be interleaved: + +```ts +z.string() + .transform((val) => val.length) + .refine((val) => val > 25); +``` + + + +### `.superRefine` + +The `.refine` method is actually syntactic sugar atop a more versatile (and verbose) method called `superRefine`. Here's an example: + +```ts +const Strings = z.array(z.string()).superRefine((val, ctx) => { + if (val.length > 3) { + ctx.addIssue({ + code: z.ZodIssueCode.too_big, + maximum: 3, + type: "array", + inclusive: true, + message: "Too many items 😡", + }); + } + + if (val.length !== new Set(val).size) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: `No duplicates allowed.`, + }); + } +}); +``` + +You can add as many issues as you like. If `ctx.addIssue` is _not_ called during the execution of the function, validation passes. + +Normally refinements always create issues with a `ZodIssueCode.custom` error code, but with `superRefine` it's possible to throw issues of any `ZodIssueCode`. Each issue code is described in detail in the Error Handling guide: [ERROR_HANDLING.md](ERROR_HANDLING.md). + +#### Abort early + +By default, parsing will continue even after a refinement check fails. For instance, if you chain together multiple refinements, they will all be executed. However, it may be desirable to _abort early_ to prevent later refinements from being executed. To achieve this, pass the `fatal` flag to `ctx.addIssue` and return `z.NEVER`. + +```ts +const schema = z.number().superRefine((val, ctx) => { + if (val < 10) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "should be >= 10", + fatal: true, + }); + + return z.NEVER; + } + + if (val !== 12) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "should be twelve", + }); + } +}); +``` + +#### Type refinements + +If you provide a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) to `.refine()` or `.superRefine()`, the resulting type will be narrowed down to your predicate's type. This is useful if you are mixing multiple chained refinements and transformations: + +```ts +const schema = z + .object({ + first: z.string(), + second: z.number(), + }) + .nullable() + .superRefine((arg, ctx): arg is { first: string; second: number } => { + if (!arg) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, // customize your issue + message: "object should exist", + }); + } + + return z.NEVER; // The return value is not used, but we need to return something to satisfy the typing + }) + // here, TS knows that arg is not null + .refine((arg) => arg.first === "bob", "`first` is not `bob`!"); +``` + +> ⚠️ You **must** use `ctx.addIssue()` instead of returning a boolean value to indicate whether the validation passes. If `ctx.addIssue` is _not_ called during the execution of the function, validation passes. + +### `.transform` + +To transform data after parsing, use the `transform` method. + +```ts +const stringToNumber = z.string().transform((val) => val.length); + +stringToNumber.parse("string"); // => 6 +``` + +#### Chaining order + +Note that `stringToNumber` above is an instance of the `ZodEffects` subclass. It is NOT an instance of `ZodString`. If you want to use the built-in methods of `ZodString` (e.g. `.email()`) you must apply those methods _before_ any transforms. + +```ts +const emailToDomain = z + .string() + .email() + .transform((val) => val.split("@")[1]); + +emailToDomain.parse("colinhacks@example.com"); // => example.com +``` + +#### Validating during transform + +The `.transform` method can simultaneously validate and transform the value. This is often simpler and less duplicative than chaining `transform` and `refine`. + +As with `.superRefine`, the transform function receives a `ctx` object with an `addIssue` method that can be used to register validation issues. + +```ts +const numberInString = z.string().transform((val, ctx) => { + const parsed = parseInt(val); + if (isNaN(parsed)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Not a number", + }); + + // This is a special symbol you can use to + // return early from the transform function. + // It has type `never` so it does not affect the + // inferred return type. + return z.NEVER; + } + return parsed; +}); +``` + +#### Relationship to refinements + +Transforms and refinements can be interleaved. These will be executed in the order they are declared. + +```ts +const nameToGreeting = z + .string() + .transform((val) => val.toUpperCase()) + .refine((val) => val.length > 15) + .transform((val) => `Hello ${val}`) + .refine((val) => val.indexOf("!") === -1); +``` + +#### Async transforms + +Transforms can also be async. + +```ts +const IdToUser = z + .string() + .uuid() + .transform(async (id) => { + return await getUserById(id); + }); +``` + +> ⚠️ If your schema contains asynchronous transforms, you must use .parseAsync() or .safeParseAsync() to parse data. Otherwise Zod will throw an error. + +### `.default` + +You can use transforms to implement the concept of "default values" in Zod. + +```ts +const stringWithDefault = z.string().default("tuna"); + +stringWithDefault.parse(undefined); // => "tuna" +``` + +Optionally, you can pass a function into `.default` that will be re-executed whenever a default value needs to be generated: + +```ts +const numberWithRandomDefault = z.number().default(Math.random); + +numberWithRandomDefault.parse(undefined); // => 0.4413456736055323 +numberWithRandomDefault.parse(undefined); // => 0.1871840107401901 +numberWithRandomDefault.parse(undefined); // => 0.7223408162401552 +``` + +Conceptually, this is how Zod processes default values: + +1. If the input is `undefined`, the default value is returned +2. Otherwise, the data is parsed using the base schema + +### `.describe` + +Use `.describe()` to add a `description` property to the resulting schema. + +```ts +const documentedString = z + .string() + .describe("A useful bit of text, if you know what to do with it."); +documentedString.description; // A useful bit of text… +``` + +This can be useful for documenting a field, for example in a JSON Schema using a library like [`zod-to-json-schema`](https://github.com/StefanTerdell/zod-to-json-schema)). + +### `.catch` + +Use `.catch()` to provide a "catch value" to be returned in the event of a parsing error. + +```ts +const numberWithCatch = z.number().catch(42); + +numberWithCatch.parse(5); // => 5 +numberWithCatch.parse("tuna"); // => 42 +``` + +Optionally, you can pass a function into `.catch` that will be re-executed whenever a default value needs to be generated. A `ctx` object containing the caught error will be passed into this function. + +```ts +const numberWithRandomCatch = z.number().catch((ctx) => { + ctx.error; // the caught ZodError + return Math.random(); +}); + +numberWithRandomCatch.parse("sup"); // => 0.4413456736055323 +numberWithRandomCatch.parse("sup"); // => 0.1871840107401901 +numberWithRandomCatch.parse("sup"); // => 0.7223408162401552 +``` + +Conceptually, this is how Zod processes "catch values": + +1. The data is parsed using the base schema +2. If the parsing fails, the "catch value" is returned + +### `.optional` + +A convenience method that returns an optional version of a schema. + +```ts +const optionalString = z.string().optional(); // string | undefined + +// equivalent to +z.optional(z.string()); +``` + +### `.nullable` + +A convenience method that returns a nullable version of a schema. + +```ts +const nullableString = z.string().nullable(); // string | null + +// equivalent to +z.nullable(z.string()); +``` + +### `.nullish` + +A convenience method that returns a "nullish" version of a schema. Nullish schemas will accept both `undefined` and `null`. Read more about the concept of "nullish" [in the TypeScript 3.7 release notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#nullish-coalescing). + +```ts +const nullishString = z.string().nullish(); // string | null | undefined + +// equivalent to +z.string().optional().nullable(); +``` + +### `.array` + +A convenience method that returns an array schema for the given type: + +```ts +const stringArray = z.string().array(); // string[] + +// equivalent to +z.array(z.string()); +``` + +### `.promise` + +A convenience method for promise types: + +```ts +const stringPromise = z.string().promise(); // Promise + +// equivalent to +z.promise(z.string()); +``` + +### `.or` + +A convenience method for [union types](#unions). + +```ts +const stringOrNumber = z.string().or(z.number()); // string | number + +// equivalent to +z.union([z.string(), z.number()]); +``` + +### `.and` + +A convenience method for creating intersection types. + +```ts +const nameAndAge = z + .object({ name: z.string() }) + .and(z.object({ age: z.number() })); // { name: string } & { age: number } + +// equivalent to +z.intersection(z.object({ name: z.string() }), z.object({ age: z.number() })); +``` + +### `.brand` + +`.brand() => ZodBranded` + +TypeScript's type system is structural, which means that any two types that are structurally equivalent are considered the same. + +```ts +type Cat = { name: string }; +type Dog = { name: string }; + +const petCat = (cat: Cat) => {}; +const fido: Dog = { name: "fido" }; +petCat(fido); // works fine +``` + +In some cases, its can be desirable to simulate _nominal typing_ inside TypeScript. For instance, you may wish to write a function that only accepts an input that has been validated by Zod. This can be achieved with _branded types_ (AKA _opaque types_). + +```ts +const Cat = z.object({ name: z.string() }).brand<"Cat">(); +type Cat = z.infer; + +const petCat = (cat: Cat) => {}; + +// this works +const simba = Cat.parse({ name: "simba" }); +petCat(simba); + +// this doesn't +petCat({ name: "fido" }); +``` + +Under the hood, this works by attaching a "brand" to the inferred type using an intersection type. This way, plain/unbranded data structures are no longer assignable to the inferred type of the schema. + +```ts +const Cat = z.object({ name: z.string() }).brand<"Cat">(); +type Cat = z.infer; +// {name: string} & {[symbol]: "Cat"} +``` + +Note that branded types do not affect the runtime result of `.parse`. It is a static-only construct. + +### `.pipe()` + +Schemas can be chained into validation "pipelines". It's useful for easily validating the result after a `.transform()`: + +```ts +z.string() + .transform((val) => val.length) + .pipe(z.number().min(5)); +``` + +The `.pipe()` method returns a `ZodPipeline` instance. + +#### You can use `.pipe()` to fix common issues with `z.coerce`. + +You can constrain the input to types that work well with your chosen coercion. Then use `.pipe()` to apply the coercion. + +without constrained input: +```ts +const toDate = z.coerce.date() + +// works intuitively +console.log(toDate.safeParse('2023-01-01').success) // true + +// might not be what you want +console.log(toDate.safeParse(null).success) // true +``` + +with constrained input: +```ts +const datelike = z.union([z.number(), z.string(), z.date()]) +const datelikeToDate = datelike.pipe(z.coerce.date()) + +// still works intuitively +console.log(datelikeToDate.safeParse('2023-01-01').success) // true + +// more likely what you want +console.log(datelikeToDate.safeParse(null).success) // false +``` + +You can also use this technique to avoid coercions that throw uncaught errors. + +without constrained input: +```ts +const toBigInt = z.coerce.bigint() + +// works intuitively +console.log( toBigInt.safeParse( '42' ) ) // true + +// probably not what you want +console.log( toBigInt.safeParse( null ) ) // throws uncaught error +``` + +with constrained input: +```ts +const toNumber = z.number().or( z.string() ).pipe( z.coerce.number() ) +const toBigInt = z.bigint().or( toNumber ).pipe( z.coerce.bigint() ) + +// still works intuitively +console.log( toBigInt.safeParse( '42' ).success ) // true + +// error handled by zod, more likely what you want +console.log( toBigInt.safeParse( null ).success ) // false +``` + +## Guides and concepts + +### Type inference + +You can extract the TypeScript type of any schema with `z.infer` . + +```ts +const A = z.string(); +type A = z.infer; // string + +const u: A = 12; // TypeError +const u: A = "asdf"; // compiles +``` + +**What about transforms?** + +In reality each Zod schema internally tracks **two** types: an input and an output. For most schemas (e.g. `z.string()`) these two are the same. But once you add transforms into the mix, these two values can diverge. For instance `z.string().transform(val => val.length)` has an input of `string` and an output of `number`. + +You can separately extract the input and output types like so: + +```ts +const stringToNumber = z.string().transform((val) => val.length); + +// ⚠️ Important: z.infer returns the OUTPUT type! +type input = z.input; // string +type output = z.output; // number + +// equivalent to z.output! +type inferred = z.infer; // number +``` + +### Writing generic functions + +When attempting to write a function that accepts a Zod schema as an input, it's common to try something like this: + +```ts +function makeSchemaOptional(schema: z.ZodType) { + return schema.optional(); +} +``` + +This approach has some issues. The `schema` variable in this function is typed as an instance of `ZodType`, which is an abstract class that all Zod schemas inherit from. This approach loses type information, namely _which subclass_ the input actually is. + +```ts +const arg = makeSchemaOptional(z.string()); +arg.unwrap(); +``` + +A better approach is for the generic parameter to refer to _the schema as a whole_. + +```ts +function makeSchemaOptional(schema: T) { + return schema.optional(); +} +``` + +> `ZodTypeAny` is just a shorthand for `ZodType`, a type that is broad enough to match any Zod schema. + +As you can see, `schema` is now fully and properly typed. + +```ts +const arg = makeSchemaOptional(z.string()); +arg.unwrap(); // ZodString +``` + +#### Constraining allowable inputs + +The `ZodType` class has three generic parameters. + +```ts +class ZodType< + Output = any, + Def extends ZodTypeDef = ZodTypeDef, + Input = Output +> { ... } +``` + +By constraining these in your generic input, you can limit what schemas are allowable as inputs to your function: + +```ts +function makeSchemaOptional>(schema: T) { + return schema.optional(); +} + +makeSchemaOptional(z.string()); +// works fine + +makeSchemaOptional(z.number()); +// Error: 'ZodNumber' is not assignable to parameter of type 'ZodType' +``` + +### Error handling + +Zod provides a subclass of Error called `ZodError`. ZodErrors contain an `issues` array containing detailed information about the validation problems. + +```ts +const result = z + .object({ + name: z.string(), + }) + .safeParse({ name: 12 }); + +if (!result.success) { + result.error.issues; + /* [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ "name" ], + "message": "Expected string, received number" + } + ] */ +} +``` + +> For detailed information about the possible error codes and how to customize error messages, check out the dedicated error handling guide: [ERROR_HANDLING.md](ERROR_HANDLING.md) + +Zod's error reporting emphasizes _completeness_ and _correctness_. If you are looking to present a useful error message to the end user, you should either override Zod's error messages using an error map (described in detail in the Error Handling guide) or use a third-party library like [`zod-validation-error`](https://github.com/causaly/zod-validation-error) + +### Error formatting + +You can use the `.format()` method to convert this error into a nested object. + +```ts +const result = z + .object({ + name: z.string(), + }) + .safeParse({ name: 12 }); + +if (!result.success) { + const formatted = result.error.format(); + /* { + name: { _errors: [ 'Expected string, received number' ] } + } */ + + formatted.name?._errors; + // => ["Expected string, received number"] +} +``` + +## Comparison + +There are a handful of other widely-used validation libraries, but all of them have certain design limitations that make for a non-ideal developer experience. + + + + + + + +### Joi + +[https://github.com/hapijs/joi](https://github.com/hapijs/joi) + +Doesn't support static type inference 😕 + +### Yup + +[https://github.com/jquense/yup](https://github.com/jquense/yup) + +Yup is a full-featured library that was implemented first in vanilla JS, and later rewritten in TypeScript. + +- Supports casting and transforms +- All object fields are optional by default +- Missing object methods: (partial, deepPartial) + +- Missing promise schemas +- Missing function schemas +- Missing union & intersection schemas + + + +### io-ts + +[https://github.com/gcanti/io-ts](https://github.com/gcanti/io-ts) + +io-ts is an excellent library by gcanti. The API of io-ts heavily inspired the design of Zod. + +In our experience, io-ts prioritizes functional programming purity over developer experience in many cases. This is a valid and admirable design goal, but it makes io-ts particularly hard to integrate into an existing codebase with a more procedural or object-oriented bias. For instance, consider how to define an object with optional properties in io-ts: + +```ts +import * as t from "io-ts"; + +const A = t.type({ + foo: t.string, +}); + +const B = t.partial({ + bar: t.number, +}); + +const C = t.intersection([A, B]); + +type C = t.TypeOf; +// returns { foo: string; bar?: number | undefined } +``` + +You must define the required and optional props in separate object validators, pass the optionals through `t.partial` (which marks all properties as optional), then combine them with `t.intersection` . + +Consider the equivalent in Zod: + +```ts +const C = z.object({ + foo: z.string(), + bar: z.number().optional(), +}); + +type C = z.infer; +// returns { foo: string; bar?: number | undefined } +``` + +This more declarative API makes schema definitions vastly more concise. + +`io-ts` also requires the use of gcanti's functional programming library `fp-ts` to parse results and handle errors. This is another fantastic resource for developers looking to keep their codebase strictly functional. But depending on `fp-ts` necessarily comes with a lot of intellectual overhead; a developer has to be familiar with functional programming concepts and the `fp-ts` nomenclature to use the library. + +- Supports codecs with serialization & deserialization transforms +- Supports branded types +- Supports advanced functional programming, higher-kinded types, `fp-ts` compatibility +- Missing object methods: (pick, omit, partial, deepPartial, merge, extend) +- Missing nonempty arrays with proper typing (`[T, ...T[]]`) +- Missing promise schemas +- Missing function schemas + +### Runtypes + +[https://github.com/pelotom/runtypes](https://github.com/pelotom/runtypes) + +Good type inference support, but limited options for object type masking (no `.pick` , `.omit` , `.extend` , etc.). No support for `Record` s (their `Record` is equivalent to Zod's `object` ). They DO support readonly types, which Zod does not. + +- Supports "pattern matching": computed properties that distribute over unions +- Supports readonly types +- Missing object methods: (deepPartial, merge) +- Missing nonempty arrays with proper typing (`[T, ...T[]]`) +- Missing promise schemas +- Missing error customization + +### Ow + +[https://github.com/sindresorhus/ow](https://github.com/sindresorhus/ow) + +Ow is focused on function input validation. It's a library that makes it easy to express complicated assert statements, but it doesn't let you parse untyped data. They support a much wider variety of types; Zod has a nearly one-to-one mapping with TypeScript's type system, whereas ow lets you validate several highly-specific types out of the box (e.g. `int32Array` , see full list in their README). + +If you want to validate function inputs, use function schemas in Zod! It's a much simpler approach that lets you reuse a function type declaration without repeating yourself (namely, copy-pasting a bunch of ow assertions at the beginning of every function). Also Zod lets you validate your return types as well, so you can be sure there won't be any unexpected data passed downstream. + +## Changelog + +View the changelog at [CHANGELOG.md](CHANGELOG.md) diff --git a/extras/zod/index.d.ts b/extras/zod/index.d.ts new file mode 100644 index 0000000..d365067 --- /dev/null +++ b/extras/zod/index.d.ts @@ -0,0 +1,2 @@ +export * from "./lib/index"; +export as namespace Zod; diff --git a/extras/zod/lib/ZodError.d.ts b/extras/zod/lib/ZodError.d.ts new file mode 100644 index 0000000..90f54af --- /dev/null +++ b/extras/zod/lib/ZodError.d.ts @@ -0,0 +1,163 @@ +import type { TypeOf, ZodType } from "."; +import { Primitive } from "./helpers/typeAliases"; +import { util, ZodParsedType } from "./helpers/util"; +declare type allKeys = T extends any ? keyof T : never; +export declare type inferFlattenedErrors, U = string> = typeToFlattenedError, U>; +export declare type typeToFlattenedError = { + formErrors: U[]; + fieldErrors: { + [P in allKeys]?: U[]; + }; +}; +export declare const ZodIssueCode: { + invalid_type: "invalid_type"; + invalid_literal: "invalid_literal"; + custom: "custom"; + invalid_union: "invalid_union"; + invalid_union_discriminator: "invalid_union_discriminator"; + invalid_enum_value: "invalid_enum_value"; + unrecognized_keys: "unrecognized_keys"; + invalid_arguments: "invalid_arguments"; + invalid_return_type: "invalid_return_type"; + invalid_date: "invalid_date"; + invalid_string: "invalid_string"; + too_small: "too_small"; + too_big: "too_big"; + invalid_intersection_types: "invalid_intersection_types"; + not_multiple_of: "not_multiple_of"; + not_finite: "not_finite"; +}; +export declare type ZodIssueCode = keyof typeof ZodIssueCode; +export declare type ZodIssueBase = { + path: (string | number)[]; + message?: string; +}; +export interface ZodInvalidTypeIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_type; + expected: ZodParsedType; + received: ZodParsedType; +} +export interface ZodInvalidLiteralIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_literal; + expected: unknown; + received: unknown; +} +export interface ZodUnrecognizedKeysIssue extends ZodIssueBase { + code: typeof ZodIssueCode.unrecognized_keys; + keys: string[]; +} +export interface ZodInvalidUnionIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_union; + unionErrors: ZodError[]; +} +export interface ZodInvalidUnionDiscriminatorIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_union_discriminator; + options: Primitive[]; +} +export interface ZodInvalidEnumValueIssue extends ZodIssueBase { + received: string | number; + code: typeof ZodIssueCode.invalid_enum_value; + options: (string | number)[]; +} +export interface ZodInvalidArgumentsIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_arguments; + argumentsError: ZodError; +} +export interface ZodInvalidReturnTypeIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_return_type; + returnTypeError: ZodError; +} +export interface ZodInvalidDateIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_date; +} +export declare type StringValidation = "email" | "url" | "emoji" | "uuid" | "regex" | "cuid" | "cuid2" | "ulid" | "datetime" | "ip" | { + includes: string; + position?: number; +} | { + startsWith: string; +} | { + endsWith: string; +}; +export interface ZodInvalidStringIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_string; + validation: StringValidation; +} +export interface ZodTooSmallIssue extends ZodIssueBase { + code: typeof ZodIssueCode.too_small; + minimum: number | bigint; + inclusive: boolean; + exact?: boolean; + type: "array" | "string" | "number" | "set" | "date" | "bigint"; +} +export interface ZodTooBigIssue extends ZodIssueBase { + code: typeof ZodIssueCode.too_big; + maximum: number | bigint; + inclusive: boolean; + exact?: boolean; + type: "array" | "string" | "number" | "set" | "date" | "bigint"; +} +export interface ZodInvalidIntersectionTypesIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_intersection_types; +} +export interface ZodNotMultipleOfIssue extends ZodIssueBase { + code: typeof ZodIssueCode.not_multiple_of; + multipleOf: number | bigint; +} +export interface ZodNotFiniteIssue extends ZodIssueBase { + code: typeof ZodIssueCode.not_finite; +} +export interface ZodCustomIssue extends ZodIssueBase { + code: typeof ZodIssueCode.custom; + params?: { + [k: string]: any; + }; +} +export declare type DenormalizedError = { + [k: string]: DenormalizedError | string[]; +}; +export declare type ZodIssueOptionalMessage = ZodInvalidTypeIssue | ZodInvalidLiteralIssue | ZodUnrecognizedKeysIssue | ZodInvalidUnionIssue | ZodInvalidUnionDiscriminatorIssue | ZodInvalidEnumValueIssue | ZodInvalidArgumentsIssue | ZodInvalidReturnTypeIssue | ZodInvalidDateIssue | ZodInvalidStringIssue | ZodTooSmallIssue | ZodTooBigIssue | ZodInvalidIntersectionTypesIssue | ZodNotMultipleOfIssue | ZodNotFiniteIssue | ZodCustomIssue; +export declare type ZodIssue = ZodIssueOptionalMessage & { + fatal?: boolean; + message: string; +}; +export declare const quotelessJson: (obj: any) => string; +declare type recursiveZodFormattedError = T extends [any, ...any[]] ? { + [K in keyof T]?: ZodFormattedError; +} : T extends any[] ? { + [k: number]: ZodFormattedError; +} : T extends object ? { + [K in keyof T]?: ZodFormattedError; +} : unknown; +export declare type ZodFormattedError = { + _errors: U[]; +} & recursiveZodFormattedError>; +export declare type inferFormattedError, U = string> = ZodFormattedError, U>; +export declare class ZodError extends Error { + issues: ZodIssue[]; + get errors(): ZodIssue[]; + constructor(issues: ZodIssue[]); + format(): ZodFormattedError; + format(mapper: (issue: ZodIssue) => U): ZodFormattedError; + static create: (issues: ZodIssue[]) => ZodError; + toString(): string; + get message(): string; + get isEmpty(): boolean; + addIssue: (sub: ZodIssue) => void; + addIssues: (subs?: ZodIssue[]) => void; + flatten(): typeToFlattenedError; + flatten(mapper?: (issue: ZodIssue) => U): typeToFlattenedError; + get formErrors(): typeToFlattenedError; +} +declare type stripPath = T extends any ? util.OmitKeys : never; +export declare type IssueData = stripPath & { + path?: (string | number)[]; + fatal?: boolean; +}; +export declare type ErrorMapCtx = { + defaultError: string; + data: any; +}; +export declare type ZodErrorMap = (issue: ZodIssueOptionalMessage, _ctx: ErrorMapCtx) => { + message: string; +}; +export {}; diff --git a/extras/zod/lib/ZodError.js b/extras/zod/lib/ZodError.js new file mode 100644 index 0000000..5901765 --- /dev/null +++ b/extras/zod/lib/ZodError.js @@ -0,0 +1,124 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ZodError = exports.quotelessJson = exports.ZodIssueCode = void 0; +const util_1 = require("./helpers/util"); +exports.ZodIssueCode = util_1.util.arrayToEnum([ + "invalid_type", + "invalid_literal", + "custom", + "invalid_union", + "invalid_union_discriminator", + "invalid_enum_value", + "unrecognized_keys", + "invalid_arguments", + "invalid_return_type", + "invalid_date", + "invalid_string", + "too_small", + "too_big", + "invalid_intersection_types", + "not_multiple_of", + "not_finite", +]); +const quotelessJson = (obj) => { + const json = JSON.stringify(obj, null, 2); + return json.replace(/"([^"]+)":/g, "$1:"); +}; +exports.quotelessJson = quotelessJson; +class ZodError extends Error { + constructor(issues) { + super(); + this.issues = []; + this.addIssue = (sub) => { + this.issues = [...this.issues, sub]; + }; + this.addIssues = (subs = []) => { + this.issues = [...this.issues, ...subs]; + }; + const actualProto = new.target.prototype; + if (Object.setPrototypeOf) { + Object.setPrototypeOf(this, actualProto); + } + else { + this.__proto__ = actualProto; + } + this.name = "ZodError"; + this.issues = issues; + } + get errors() { + return this.issues; + } + format(_mapper) { + const mapper = _mapper || + function (issue) { + return issue.message; + }; + const fieldErrors = { _errors: [] }; + const processError = (error) => { + for (const issue of error.issues) { + if (issue.code === "invalid_union") { + issue.unionErrors.map(processError); + } + else if (issue.code === "invalid_return_type") { + processError(issue.returnTypeError); + } + else if (issue.code === "invalid_arguments") { + processError(issue.argumentsError); + } + else if (issue.path.length === 0) { + fieldErrors._errors.push(mapper(issue)); + } + else { + let curr = fieldErrors; + let i = 0; + while (i < issue.path.length) { + const el = issue.path[i]; + const terminal = i === issue.path.length - 1; + if (!terminal) { + curr[el] = curr[el] || { _errors: [] }; + } + else { + curr[el] = curr[el] || { _errors: [] }; + curr[el]._errors.push(mapper(issue)); + } + curr = curr[el]; + i++; + } + } + } + }; + processError(this); + return fieldErrors; + } + toString() { + return this.message; + } + get message() { + return JSON.stringify(this.issues, util_1.util.jsonStringifyReplacer, 2); + } + get isEmpty() { + return this.issues.length === 0; + } + flatten(mapper = (issue) => issue.message) { + const fieldErrors = {}; + const formErrors = []; + for (const sub of this.issues) { + if (sub.path.length > 0) { + fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || []; + fieldErrors[sub.path[0]].push(mapper(sub)); + } + else { + formErrors.push(mapper(sub)); + } + } + return { formErrors, fieldErrors }; + } + get formErrors() { + return this.flatten(); + } +} +exports.ZodError = ZodError; +ZodError.create = (issues) => { + const error = new ZodError(issues); + return error; +}; diff --git a/extras/zod/lib/errors.d.ts b/extras/zod/lib/errors.d.ts new file mode 100644 index 0000000..ebf6b0f --- /dev/null +++ b/extras/zod/lib/errors.d.ts @@ -0,0 +1,5 @@ +import defaultErrorMap from "./locales/en"; +import type { ZodErrorMap } from "./ZodError"; +export { defaultErrorMap }; +export declare function setErrorMap(map: ZodErrorMap): void; +export declare function getErrorMap(): ZodErrorMap; diff --git a/extras/zod/lib/errors.js b/extras/zod/lib/errors.js new file mode 100644 index 0000000..93c6bc1 --- /dev/null +++ b/extras/zod/lib/errors.js @@ -0,0 +1,17 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getErrorMap = exports.setErrorMap = exports.defaultErrorMap = void 0; +const en_1 = __importDefault(require("./locales/en")); +exports.defaultErrorMap = en_1.default; +let overrideErrorMap = en_1.default; +function setErrorMap(map) { + overrideErrorMap = map; +} +exports.setErrorMap = setErrorMap; +function getErrorMap() { + return overrideErrorMap; +} +exports.getErrorMap = getErrorMap; diff --git a/extras/zod/lib/external.d.ts b/extras/zod/lib/external.d.ts new file mode 100644 index 0000000..002e17d --- /dev/null +++ b/extras/zod/lib/external.d.ts @@ -0,0 +1,6 @@ +export * from "./errors"; +export * from "./helpers/parseUtil"; +export * from "./helpers/typeAliases"; +export * from "./helpers/util"; +export * from "./types"; +export * from "./ZodError"; diff --git a/extras/zod/lib/external.js b/extras/zod/lib/external.js new file mode 100644 index 0000000..48e6db2 --- /dev/null +++ b/extras/zod/lib/external.js @@ -0,0 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./errors"), exports); +__exportStar(require("./helpers/parseUtil"), exports); +__exportStar(require("./helpers/typeAliases"), exports); +__exportStar(require("./helpers/util"), exports); +__exportStar(require("./types"), exports); +__exportStar(require("./ZodError"), exports); diff --git a/extras/zod/lib/helpers/enumUtil.d.ts b/extras/zod/lib/helpers/enumUtil.d.ts new file mode 100644 index 0000000..fad28a7 --- /dev/null +++ b/extras/zod/lib/helpers/enumUtil.d.ts @@ -0,0 +1,8 @@ +export declare namespace enumUtil { + type UnionToIntersectionFn = (T extends unknown ? (k: () => T) => void : never) extends (k: infer Intersection) => void ? Intersection : never; + type GetUnionLast = UnionToIntersectionFn extends () => infer Last ? Last : never; + type UnionToTuple = [T] extends [never] ? Tuple : UnionToTuple>, [GetUnionLast, ...Tuple]>; + type CastToStringTuple = T extends [string, ...string[]] ? T : never; + export type UnionToTupleString = CastToStringTuple>; + export {}; +} diff --git a/extras/zod/lib/helpers/enumUtil.js b/extras/zod/lib/helpers/enumUtil.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/extras/zod/lib/helpers/enumUtil.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/extras/zod/lib/helpers/errorUtil.d.ts b/extras/zod/lib/helpers/errorUtil.d.ts new file mode 100644 index 0000000..5346d72 --- /dev/null +++ b/extras/zod/lib/helpers/errorUtil.d.ts @@ -0,0 +1,9 @@ +export declare namespace errorUtil { + type ErrMessage = string | { + message?: string; + }; + const errToObj: (message?: ErrMessage | undefined) => { + message?: string | undefined; + }; + const toString: (message?: ErrMessage | undefined) => string | undefined; +} diff --git a/extras/zod/lib/helpers/errorUtil.js b/extras/zod/lib/helpers/errorUtil.js new file mode 100644 index 0000000..7fa2797 --- /dev/null +++ b/extras/zod/lib/helpers/errorUtil.js @@ -0,0 +1,8 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.errorUtil = void 0; +var errorUtil; +(function (errorUtil) { + errorUtil.errToObj = (message) => typeof message === "string" ? { message } : message || {}; + errorUtil.toString = (message) => typeof message === "string" ? message : message === null || message === void 0 ? void 0 : message.message; +})(errorUtil = exports.errorUtil || (exports.errorUtil = {})); diff --git a/extras/zod/lib/helpers/parseUtil.d.ts b/extras/zod/lib/helpers/parseUtil.d.ts new file mode 100644 index 0000000..f3f2bba --- /dev/null +++ b/extras/zod/lib/helpers/parseUtil.d.ts @@ -0,0 +1,78 @@ +import type { IssueData, ZodErrorMap, ZodIssue } from "../ZodError"; +import type { ZodParsedType } from "./util"; +export declare const makeIssue: (params: { + data: any; + path: (string | number)[]; + errorMaps: ZodErrorMap[]; + issueData: IssueData; +}) => ZodIssue; +export declare type ParseParams = { + path: (string | number)[]; + errorMap: ZodErrorMap; + async: boolean; +}; +export declare type ParsePathComponent = string | number; +export declare type ParsePath = ParsePathComponent[]; +export declare const EMPTY_PATH: ParsePath; +export interface ParseContext { + readonly common: { + readonly issues: ZodIssue[]; + readonly contextualErrorMap?: ZodErrorMap; + readonly async: boolean; + }; + readonly path: ParsePath; + readonly schemaErrorMap?: ZodErrorMap; + readonly parent: ParseContext | null; + readonly data: any; + readonly parsedType: ZodParsedType; +} +export declare type ParseInput = { + data: any; + path: (string | number)[]; + parent: ParseContext; +}; +export declare function addIssueToContext(ctx: ParseContext, issueData: IssueData): void; +export declare type ObjectPair = { + key: SyncParseReturnType; + value: SyncParseReturnType; +}; +export declare class ParseStatus { + value: "aborted" | "dirty" | "valid"; + dirty(): void; + abort(): void; + static mergeArray(status: ParseStatus, results: SyncParseReturnType[]): SyncParseReturnType; + static mergeObjectAsync(status: ParseStatus, pairs: { + key: ParseReturnType; + value: ParseReturnType; + }[]): Promise>; + static mergeObjectSync(status: ParseStatus, pairs: { + key: SyncParseReturnType; + value: SyncParseReturnType; + alwaysSet?: boolean; + }[]): SyncParseReturnType; +} +export interface ParseResult { + status: "aborted" | "dirty" | "valid"; + data: any; +} +export declare type INVALID = { + status: "aborted"; +}; +export declare const INVALID: INVALID; +export declare type DIRTY = { + status: "dirty"; + value: T; +}; +export declare const DIRTY: (value: T) => DIRTY; +export declare type OK = { + status: "valid"; + value: T; +}; +export declare const OK: (value: T) => OK; +export declare type SyncParseReturnType = OK | DIRTY | INVALID; +export declare type AsyncParseReturnType = Promise>; +export declare type ParseReturnType = SyncParseReturnType | AsyncParseReturnType; +export declare const isAborted: (x: ParseReturnType) => x is INVALID; +export declare const isDirty: (x: ParseReturnType) => x is OK | DIRTY; +export declare const isValid: (x: ParseReturnType) => x is OK | DIRTY; +export declare const isAsync: (x: ParseReturnType) => x is AsyncParseReturnType; diff --git a/extras/zod/lib/helpers/parseUtil.js b/extras/zod/lib/helpers/parseUtil.js new file mode 100644 index 0000000..10b32da --- /dev/null +++ b/extras/zod/lib/helpers/parseUtil.js @@ -0,0 +1,114 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isAsync = exports.isValid = exports.isDirty = exports.isAborted = exports.OK = exports.DIRTY = exports.INVALID = exports.ParseStatus = exports.addIssueToContext = exports.EMPTY_PATH = exports.makeIssue = void 0; +const errors_1 = require("../errors"); +const en_1 = __importDefault(require("../locales/en")); +const makeIssue = (params) => { + const { data, path, errorMaps, issueData } = params; + const fullPath = [...path, ...(issueData.path || [])]; + const fullIssue = { + ...issueData, + path: fullPath, + }; + let errorMessage = ""; + const maps = errorMaps + .filter((m) => !!m) + .slice() + .reverse(); + for (const map of maps) { + errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message; + } + return { + ...issueData, + path: fullPath, + message: issueData.message || errorMessage, + }; +}; +exports.makeIssue = makeIssue; +exports.EMPTY_PATH = []; +function addIssueToContext(ctx, issueData) { + const issue = (0, exports.makeIssue)({ + issueData: issueData, + data: ctx.data, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + (0, errors_1.getErrorMap)(), + en_1.default, + ].filter((x) => !!x), + }); + ctx.common.issues.push(issue); +} +exports.addIssueToContext = addIssueToContext; +class ParseStatus { + constructor() { + this.value = "valid"; + } + dirty() { + if (this.value === "valid") + this.value = "dirty"; + } + abort() { + if (this.value !== "aborted") + this.value = "aborted"; + } + static mergeArray(status, results) { + const arrayValue = []; + for (const s of results) { + if (s.status === "aborted") + return exports.INVALID; + if (s.status === "dirty") + status.dirty(); + arrayValue.push(s.value); + } + return { status: status.value, value: arrayValue }; + } + static async mergeObjectAsync(status, pairs) { + const syncPairs = []; + for (const pair of pairs) { + syncPairs.push({ + key: await pair.key, + value: await pair.value, + }); + } + return ParseStatus.mergeObjectSync(status, syncPairs); + } + static mergeObjectSync(status, pairs) { + const finalObject = {}; + for (const pair of pairs) { + const { key, value } = pair; + if (key.status === "aborted") + return exports.INVALID; + if (value.status === "aborted") + return exports.INVALID; + if (key.status === "dirty") + status.dirty(); + if (value.status === "dirty") + status.dirty(); + if (typeof value.value !== "undefined" || pair.alwaysSet) { + finalObject[key.value] = value.value; + } + } + return { status: status.value, value: finalObject }; + } +} +exports.ParseStatus = ParseStatus; +exports.INVALID = Object.freeze({ + status: "aborted", +}); +const DIRTY = (value) => ({ status: "dirty", value }); +exports.DIRTY = DIRTY; +const OK = (value) => ({ status: "valid", value }); +exports.OK = OK; +const isAborted = (x) => x.status === "aborted"; +exports.isAborted = isAborted; +const isDirty = (x) => x.status === "dirty"; +exports.isDirty = isDirty; +const isValid = (x) => x.status === "valid"; +exports.isValid = isValid; +const isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise; +exports.isAsync = isAsync; diff --git a/extras/zod/lib/helpers/partialUtil.d.ts b/extras/zod/lib/helpers/partialUtil.d.ts new file mode 100644 index 0000000..dd56a9d --- /dev/null +++ b/extras/zod/lib/helpers/partialUtil.d.ts @@ -0,0 +1,8 @@ +import type { ZodArray, ZodNullable, ZodObject, ZodOptional, ZodRawShape, ZodTuple, ZodTupleItems, ZodTypeAny } from "../index"; +export declare namespace partialUtil { + type DeepPartial = T extends ZodObject ? ZodObject<{ + [k in keyof T["shape"]]: ZodOptional>; + }, T["_def"]["unknownKeys"], T["_def"]["catchall"]> : T extends ZodArray ? ZodArray, Card> : T extends ZodOptional ? ZodOptional> : T extends ZodNullable ? ZodNullable> : T extends ZodTuple ? { + [k in keyof Items]: Items[k] extends ZodTypeAny ? DeepPartial : never; + } extends infer PI ? PI extends ZodTupleItems ? ZodTuple : never : never : T; +} diff --git a/extras/zod/lib/helpers/partialUtil.js b/extras/zod/lib/helpers/partialUtil.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/extras/zod/lib/helpers/partialUtil.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/extras/zod/lib/helpers/typeAliases.d.ts b/extras/zod/lib/helpers/typeAliases.d.ts new file mode 100644 index 0000000..aba4b3a --- /dev/null +++ b/extras/zod/lib/helpers/typeAliases.d.ts @@ -0,0 +1,2 @@ +export declare type Primitive = string | number | symbol | bigint | boolean | null | undefined; +export declare type Scalars = Primitive | Primitive[]; diff --git a/extras/zod/lib/helpers/typeAliases.js b/extras/zod/lib/helpers/typeAliases.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/extras/zod/lib/helpers/typeAliases.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/extras/zod/lib/helpers/util.d.ts b/extras/zod/lib/helpers/util.d.ts new file mode 100644 index 0000000..d400ef6 --- /dev/null +++ b/extras/zod/lib/helpers/util.d.ts @@ -0,0 +1,68 @@ +export declare namespace util { + type AssertEqual = (() => V extends T ? 1 : 2) extends () => V extends U ? 1 : 2 ? true : false; + export type isAny = 0 extends 1 & T ? true : false; + export const assertEqual: (val: AssertEqual) => AssertEqual; + export function assertIs(_arg: T): void; + export function assertNever(_x: never): never; + export type Omit = Pick>; + export type OmitKeys = Pick>; + export type MakePartial = Omit & Partial>; + export const arrayToEnum: (items: U) => { [k in U[number]]: k; }; + export const getValidEnumValues: (obj: any) => any[]; + export const objectValues: (obj: any) => any[]; + export const objectKeys: ObjectConstructor["keys"]; + export const find: (arr: T[], checker: (arg: T) => any) => T | undefined; + export type identity = objectUtil.identity; + export type flatten = objectUtil.flatten; + export type noUndefined = T extends undefined ? never : T; + export const isInteger: NumberConstructor["isInteger"]; + export function joinValues(array: T, separator?: string): string; + export const jsonStringifyReplacer: (_: string, value: any) => any; + export {}; +} +export declare namespace objectUtil { + export type MergeShapes = { + [k in Exclude]: U[k]; + } & V; + type requiredKeys = { + [k in keyof T]: undefined extends T[k] ? never : k; + }[keyof T]; + export type addQuestionMarks> = Pick, R> & Partial; + export type identity = T; + export type flatten = identity<{ + [k in keyof T]: T[k]; + }>; + export type noNeverKeys = { + [k in keyof T]: [T[k]] extends [never] ? never : k; + }[keyof T]; + export type noNever = identity<{ + [k in noNeverKeys]: k extends keyof T ? T[k] : never; + }>; + export const mergeShapes: (first: U, second: T) => T & U; + export type extendShape = flatten & B>; + export {}; +} +export declare const ZodParsedType: { + function: "function"; + number: "number"; + string: "string"; + nan: "nan"; + integer: "integer"; + float: "float"; + boolean: "boolean"; + date: "date"; + bigint: "bigint"; + symbol: "symbol"; + undefined: "undefined"; + null: "null"; + array: "array"; + object: "object"; + unknown: "unknown"; + promise: "promise"; + void: "void"; + never: "never"; + map: "map"; + set: "set"; +}; +export declare type ZodParsedType = keyof typeof ZodParsedType; +export declare const getParsedType: (data: any) => ZodParsedType; diff --git a/extras/zod/lib/helpers/util.js b/extras/zod/lib/helpers/util.js new file mode 100644 index 0000000..d35a6eb --- /dev/null +++ b/extras/zod/lib/helpers/util.js @@ -0,0 +1,142 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getParsedType = exports.ZodParsedType = exports.objectUtil = exports.util = void 0; +var util; +(function (util) { + util.assertEqual = (val) => val; + function assertIs(_arg) { } + util.assertIs = assertIs; + function assertNever(_x) { + throw new Error(); + } + util.assertNever = assertNever; + util.arrayToEnum = (items) => { + const obj = {}; + for (const item of items) { + obj[item] = item; + } + return obj; + }; + util.getValidEnumValues = (obj) => { + const validKeys = util.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number"); + const filtered = {}; + for (const k of validKeys) { + filtered[k] = obj[k]; + } + return util.objectValues(filtered); + }; + util.objectValues = (obj) => { + return util.objectKeys(obj).map(function (e) { + return obj[e]; + }); + }; + util.objectKeys = typeof Object.keys === "function" + ? (obj) => Object.keys(obj) + : (object) => { + const keys = []; + for (const key in object) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + keys.push(key); + } + } + return keys; + }; + util.find = (arr, checker) => { + for (const item of arr) { + if (checker(item)) + return item; + } + return undefined; + }; + util.isInteger = typeof Number.isInteger === "function" + ? (val) => Number.isInteger(val) + : (val) => typeof val === "number" && isFinite(val) && Math.floor(val) === val; + function joinValues(array, separator = " | ") { + return array + .map((val) => (typeof val === "string" ? `'${val}'` : val)) + .join(separator); + } + util.joinValues = joinValues; + util.jsonStringifyReplacer = (_, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + return value; + }; +})(util = exports.util || (exports.util = {})); +var objectUtil; +(function (objectUtil) { + objectUtil.mergeShapes = (first, second) => { + return { + ...first, + ...second, + }; + }; +})(objectUtil = exports.objectUtil || (exports.objectUtil = {})); +exports.ZodParsedType = util.arrayToEnum([ + "string", + "nan", + "number", + "integer", + "float", + "boolean", + "date", + "bigint", + "symbol", + "function", + "undefined", + "null", + "array", + "object", + "unknown", + "promise", + "void", + "never", + "map", + "set", +]); +const getParsedType = (data) => { + const t = typeof data; + switch (t) { + case "undefined": + return exports.ZodParsedType.undefined; + case "string": + return exports.ZodParsedType.string; + case "number": + return isNaN(data) ? exports.ZodParsedType.nan : exports.ZodParsedType.number; + case "boolean": + return exports.ZodParsedType.boolean; + case "function": + return exports.ZodParsedType.function; + case "bigint": + return exports.ZodParsedType.bigint; + case "symbol": + return exports.ZodParsedType.symbol; + case "object": + if (Array.isArray(data)) { + return exports.ZodParsedType.array; + } + if (data === null) { + return exports.ZodParsedType.null; + } + if (data.then && + typeof data.then === "function" && + data.catch && + typeof data.catch === "function") { + return exports.ZodParsedType.promise; + } + if (typeof Map !== "undefined" && data instanceof Map) { + return exports.ZodParsedType.map; + } + if (typeof Set !== "undefined" && data instanceof Set) { + return exports.ZodParsedType.set; + } + if (typeof Date !== "undefined" && data instanceof Date) { + return exports.ZodParsedType.date; + } + return exports.ZodParsedType.object; + default: + return exports.ZodParsedType.unknown; + } +}; +exports.getParsedType = getParsedType; diff --git a/extras/zod/lib/index.d.ts b/extras/zod/lib/index.d.ts new file mode 100644 index 0000000..e6a2db6 --- /dev/null +++ b/extras/zod/lib/index.d.ts @@ -0,0 +1,4 @@ +import * as z from "./external"; +export * from "./external"; +export { z }; +export default z; diff --git a/extras/zod/lib/index.js b/extras/zod/lib/index.js new file mode 100644 index 0000000..69078d5 --- /dev/null +++ b/extras/zod/lib/index.js @@ -0,0 +1,3953 @@ +var util; +(function (util) { + util.assertEqual = (val) => val; + function assertIs(_arg) { } + util.assertIs = assertIs; + function assertNever(_x) { + throw new Error(); + } + util.assertNever = assertNever; + util.arrayToEnum = (items) => { + const obj = {}; + for (const item of items) { + obj[item] = item; + } + return obj; + }; + util.getValidEnumValues = (obj) => { + const validKeys = util.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number"); + const filtered = {}; + for (const k of validKeys) { + filtered[k] = obj[k]; + } + return util.objectValues(filtered); + }; + util.objectValues = (obj) => { + return util.objectKeys(obj).map(function (e) { + return obj[e]; + }); + }; + util.objectKeys = typeof Object.keys === "function" // eslint-disable-line ban/ban + ? (obj) => Object.keys(obj) // eslint-disable-line ban/ban + : (object) => { + const keys = []; + for (const key in object) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + keys.push(key); + } + } + return keys; + }; + util.find = (arr, checker) => { + for (const item of arr) { + if (checker(item)) + return item; + } + return undefined; + }; + util.isInteger = typeof Number.isInteger === "function" + ? (val) => Number.isInteger(val) // eslint-disable-line ban/ban + : (val) => typeof val === "number" && isFinite(val) && Math.floor(val) === val; + function joinValues(array, separator = " | ") { + return array + .map((val) => (typeof val === "string" ? `'${val}'` : val)) + .join(separator); + } + util.joinValues = joinValues; + util.jsonStringifyReplacer = (_, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + return value; + }; +})(util || (util = {})); +var objectUtil; +(function (objectUtil) { + objectUtil.mergeShapes = (first, second) => { + return { + ...first, + ...second, // second overwrites first + }; + }; +})(objectUtil || (objectUtil = {})); +const ZodParsedType = util.arrayToEnum([ + "string", + "nan", + "number", + "integer", + "float", + "boolean", + "date", + "bigint", + "symbol", + "function", + "undefined", + "null", + "array", + "object", + "unknown", + "promise", + "void", + "never", + "map", + "set", +]); +const getParsedType = (data) => { + const t = typeof data; + switch (t) { + case "undefined": + return ZodParsedType.undefined; + case "string": + return ZodParsedType.string; + case "number": + return isNaN(data) ? ZodParsedType.nan : ZodParsedType.number; + case "boolean": + return ZodParsedType.boolean; + case "function": + return ZodParsedType.function; + case "bigint": + return ZodParsedType.bigint; + case "symbol": + return ZodParsedType.symbol; + case "object": + if (Array.isArray(data)) { + return ZodParsedType.array; + } + if (data === null) { + return ZodParsedType.null; + } + if (data.then && + typeof data.then === "function" && + data.catch && + typeof data.catch === "function") { + return ZodParsedType.promise; + } + if (typeof Map !== "undefined" && data instanceof Map) { + return ZodParsedType.map; + } + if (typeof Set !== "undefined" && data instanceof Set) { + return ZodParsedType.set; + } + if (typeof Date !== "undefined" && data instanceof Date) { + return ZodParsedType.date; + } + return ZodParsedType.object; + default: + return ZodParsedType.unknown; + } +}; + +const ZodIssueCode = util.arrayToEnum([ + "invalid_type", + "invalid_literal", + "custom", + "invalid_union", + "invalid_union_discriminator", + "invalid_enum_value", + "unrecognized_keys", + "invalid_arguments", + "invalid_return_type", + "invalid_date", + "invalid_string", + "too_small", + "too_big", + "invalid_intersection_types", + "not_multiple_of", + "not_finite", +]); +const quotelessJson = (obj) => { + const json = JSON.stringify(obj, null, 2); + return json.replace(/"([^"]+)":/g, "$1:"); +}; +class ZodError extends Error { + constructor(issues) { + super(); + this.issues = []; + this.addIssue = (sub) => { + this.issues = [...this.issues, sub]; + }; + this.addIssues = (subs = []) => { + this.issues = [...this.issues, ...subs]; + }; + const actualProto = new.target.prototype; + if (Object.setPrototypeOf) { + // eslint-disable-next-line ban/ban + Object.setPrototypeOf(this, actualProto); + } + else { + this.__proto__ = actualProto; + } + this.name = "ZodError"; + this.issues = issues; + } + get errors() { + return this.issues; + } + format(_mapper) { + const mapper = _mapper || + function (issue) { + return issue.message; + }; + const fieldErrors = { _errors: [] }; + const processError = (error) => { + for (const issue of error.issues) { + if (issue.code === "invalid_union") { + issue.unionErrors.map(processError); + } + else if (issue.code === "invalid_return_type") { + processError(issue.returnTypeError); + } + else if (issue.code === "invalid_arguments") { + processError(issue.argumentsError); + } + else if (issue.path.length === 0) { + fieldErrors._errors.push(mapper(issue)); + } + else { + let curr = fieldErrors; + let i = 0; + while (i < issue.path.length) { + const el = issue.path[i]; + const terminal = i === issue.path.length - 1; + if (!terminal) { + curr[el] = curr[el] || { _errors: [] }; + // if (typeof el === "string") { + // curr[el] = curr[el] || { _errors: [] }; + // } else if (typeof el === "number") { + // const errorArray: any = []; + // errorArray._errors = []; + // curr[el] = curr[el] || errorArray; + // } + } + else { + curr[el] = curr[el] || { _errors: [] }; + curr[el]._errors.push(mapper(issue)); + } + curr = curr[el]; + i++; + } + } + } + }; + processError(this); + return fieldErrors; + } + toString() { + return this.message; + } + get message() { + return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2); + } + get isEmpty() { + return this.issues.length === 0; + } + flatten(mapper = (issue) => issue.message) { + const fieldErrors = {}; + const formErrors = []; + for (const sub of this.issues) { + if (sub.path.length > 0) { + fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || []; + fieldErrors[sub.path[0]].push(mapper(sub)); + } + else { + formErrors.push(mapper(sub)); + } + } + return { formErrors, fieldErrors }; + } + get formErrors() { + return this.flatten(); + } +} +ZodError.create = (issues) => { + const error = new ZodError(issues); + return error; +}; + +const errorMap = (issue, _ctx) => { + let message; + switch (issue.code) { + case ZodIssueCode.invalid_type: + if (issue.received === ZodParsedType.undefined) { + message = "Required"; + } + else { + message = `Expected ${issue.expected}, received ${issue.received}`; + } + break; + case ZodIssueCode.invalid_literal: + message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util.jsonStringifyReplacer)}`; + break; + case ZodIssueCode.unrecognized_keys: + message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, ", ")}`; + break; + case ZodIssueCode.invalid_union: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_union_discriminator: + message = `Invalid discriminator value. Expected ${util.joinValues(issue.options)}`; + break; + case ZodIssueCode.invalid_enum_value: + message = `Invalid enum value. Expected ${util.joinValues(issue.options)}, received '${issue.received}'`; + break; + case ZodIssueCode.invalid_arguments: + message = `Invalid function arguments`; + break; + case ZodIssueCode.invalid_return_type: + message = `Invalid function return type`; + break; + case ZodIssueCode.invalid_date: + message = `Invalid date`; + break; + case ZodIssueCode.invalid_string: + if (typeof issue.validation === "object") { + if ("includes" in issue.validation) { + message = `Invalid input: must include "${issue.validation.includes}"`; + if (typeof issue.validation.position === "number") { + message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`; + } + } + else if ("startsWith" in issue.validation) { + message = `Invalid input: must start with "${issue.validation.startsWith}"`; + } + else if ("endsWith" in issue.validation) { + message = `Invalid input: must end with "${issue.validation.endsWith}"`; + } + else { + util.assertNever(issue.validation); + } + } + else if (issue.validation !== "regex") { + message = `Invalid ${issue.validation}`; + } + else { + message = "Invalid"; + } + break; + case ZodIssueCode.too_small: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact + ? `exactly equal to ` + : issue.inclusive + ? `greater than or equal to ` + : `greater than `}${issue.minimum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact + ? `exactly equal to ` + : issue.inclusive + ? `greater than or equal to ` + : `greater than `}${new Date(Number(issue.minimum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.too_big: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `less than or equal to` + : `less than`} ${issue.maximum}`; + else if (issue.type === "bigint") + message = `BigInt must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `less than or equal to` + : `less than`} ${issue.maximum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `smaller than or equal to` + : `smaller than`} ${new Date(Number(issue.maximum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.custom: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_intersection_types: + message = `Intersection results could not be merged`; + break; + case ZodIssueCode.not_multiple_of: + message = `Number must be a multiple of ${issue.multipleOf}`; + break; + case ZodIssueCode.not_finite: + message = "Number must be finite"; + break; + default: + message = _ctx.defaultError; + util.assertNever(issue); + } + return { message }; +}; + +let overrideErrorMap = errorMap; +function setErrorMap(map) { + overrideErrorMap = map; +} +function getErrorMap() { + return overrideErrorMap; +} + +const makeIssue = (params) => { + const { data, path, errorMaps, issueData } = params; + const fullPath = [...path, ...(issueData.path || [])]; + const fullIssue = { + ...issueData, + path: fullPath, + }; + let errorMessage = ""; + const maps = errorMaps + .filter((m) => !!m) + .slice() + .reverse(); + for (const map of maps) { + errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message; + } + return { + ...issueData, + path: fullPath, + message: issueData.message || errorMessage, + }; +}; +const EMPTY_PATH = []; +function addIssueToContext(ctx, issueData) { + const issue = makeIssue({ + issueData: issueData, + data: ctx.data, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + getErrorMap(), + errorMap, // then global default map + ].filter((x) => !!x), + }); + ctx.common.issues.push(issue); +} +class ParseStatus { + constructor() { + this.value = "valid"; + } + dirty() { + if (this.value === "valid") + this.value = "dirty"; + } + abort() { + if (this.value !== "aborted") + this.value = "aborted"; + } + static mergeArray(status, results) { + const arrayValue = []; + for (const s of results) { + if (s.status === "aborted") + return INVALID; + if (s.status === "dirty") + status.dirty(); + arrayValue.push(s.value); + } + return { status: status.value, value: arrayValue }; + } + static async mergeObjectAsync(status, pairs) { + const syncPairs = []; + for (const pair of pairs) { + syncPairs.push({ + key: await pair.key, + value: await pair.value, + }); + } + return ParseStatus.mergeObjectSync(status, syncPairs); + } + static mergeObjectSync(status, pairs) { + const finalObject = {}; + for (const pair of pairs) { + const { key, value } = pair; + if (key.status === "aborted") + return INVALID; + if (value.status === "aborted") + return INVALID; + if (key.status === "dirty") + status.dirty(); + if (value.status === "dirty") + status.dirty(); + if (typeof value.value !== "undefined" || pair.alwaysSet) { + finalObject[key.value] = value.value; + } + } + return { status: status.value, value: finalObject }; + } +} +const INVALID = Object.freeze({ + status: "aborted", +}); +const DIRTY = (value) => ({ status: "dirty", value }); +const OK = (value) => ({ status: "valid", value }); +const isAborted = (x) => x.status === "aborted"; +const isDirty = (x) => x.status === "dirty"; +const isValid = (x) => x.status === "valid"; +const isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise; + +var errorUtil; +(function (errorUtil) { + errorUtil.errToObj = (message) => typeof message === "string" ? { message } : message || {}; + errorUtil.toString = (message) => typeof message === "string" ? message : message === null || message === void 0 ? void 0 : message.message; +})(errorUtil || (errorUtil = {})); + +class ParseInputLazyPath { + constructor(parent, value, path, key) { + this._cachedPath = []; + this.parent = parent; + this.data = value; + this._path = path; + this._key = key; + } + get path() { + if (!this._cachedPath.length) { + if (this._key instanceof Array) { + this._cachedPath.push(...this._path, ...this._key); + } + else { + this._cachedPath.push(...this._path, this._key); + } + } + return this._cachedPath; + } +} +const handleResult = (ctx, result) => { + if (isValid(result)) { + return { success: true, data: result.value }; + } + else { + if (!ctx.common.issues.length) { + throw new Error("Validation failed but no issues detected."); + } + return { + success: false, + get error() { + if (this._error) + return this._error; + const error = new ZodError(ctx.common.issues); + this._error = error; + return this._error; + }, + }; + } +}; +function processCreateParams(params) { + if (!params) + return {}; + const { errorMap, invalid_type_error, required_error, description } = params; + if (errorMap && (invalid_type_error || required_error)) { + throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`); + } + if (errorMap) + return { errorMap: errorMap, description }; + const customMap = (iss, ctx) => { + if (iss.code !== "invalid_type") + return { message: ctx.defaultError }; + if (typeof ctx.data === "undefined") { + return { message: required_error !== null && required_error !== void 0 ? required_error : ctx.defaultError }; + } + return { message: invalid_type_error !== null && invalid_type_error !== void 0 ? invalid_type_error : ctx.defaultError }; + }; + return { errorMap: customMap, description }; +} +class ZodType { + constructor(def) { + /** Alias of safeParseAsync */ + this.spa = this.safeParseAsync; + this._def = def; + this.parse = this.parse.bind(this); + this.safeParse = this.safeParse.bind(this); + this.parseAsync = this.parseAsync.bind(this); + this.safeParseAsync = this.safeParseAsync.bind(this); + this.spa = this.spa.bind(this); + this.refine = this.refine.bind(this); + this.refinement = this.refinement.bind(this); + this.superRefine = this.superRefine.bind(this); + this.optional = this.optional.bind(this); + this.nullable = this.nullable.bind(this); + this.nullish = this.nullish.bind(this); + this.array = this.array.bind(this); + this.promise = this.promise.bind(this); + this.or = this.or.bind(this); + this.and = this.and.bind(this); + this.transform = this.transform.bind(this); + this.brand = this.brand.bind(this); + this.default = this.default.bind(this); + this.catch = this.catch.bind(this); + this.describe = this.describe.bind(this); + this.pipe = this.pipe.bind(this); + this.isNullable = this.isNullable.bind(this); + this.isOptional = this.isOptional.bind(this); + } + get description() { + return this._def.description; + } + _getType(input) { + return getParsedType(input.data); + } + _getOrReturnCtx(input, ctx) { + return (ctx || { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent, + }); + } + _processInputParams(input) { + return { + status: new ParseStatus(), + ctx: { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent, + }, + }; + } + _parseSync(input) { + const result = this._parse(input); + if (isAsync(result)) { + throw new Error("Synchronous parse encountered promise."); + } + return result; + } + _parseAsync(input) { + const result = this._parse(input); + return Promise.resolve(result); + } + parse(data, params) { + const result = this.safeParse(data, params); + if (result.success) + return result.data; + throw result.error; + } + safeParse(data, params) { + var _a; + const ctx = { + common: { + issues: [], + async: (_a = params === null || params === void 0 ? void 0 : params.async) !== null && _a !== void 0 ? _a : false, + contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap, + }, + path: (params === null || params === void 0 ? void 0 : params.path) || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data), + }; + const result = this._parseSync({ data, path: ctx.path, parent: ctx }); + return handleResult(ctx, result); + } + async parseAsync(data, params) { + const result = await this.safeParseAsync(data, params); + if (result.success) + return result.data; + throw result.error; + } + async safeParseAsync(data, params) { + const ctx = { + common: { + issues: [], + contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap, + async: true, + }, + path: (params === null || params === void 0 ? void 0 : params.path) || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data), + }; + const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx }); + const result = await (isAsync(maybeAsyncResult) + ? maybeAsyncResult + : Promise.resolve(maybeAsyncResult)); + return handleResult(ctx, result); + } + refine(check, message) { + const getIssueProperties = (val) => { + if (typeof message === "string" || typeof message === "undefined") { + return { message }; + } + else if (typeof message === "function") { + return message(val); + } + else { + return message; + } + }; + return this._refinement((val, ctx) => { + const result = check(val); + const setError = () => ctx.addIssue({ + code: ZodIssueCode.custom, + ...getIssueProperties(val), + }); + if (typeof Promise !== "undefined" && result instanceof Promise) { + return result.then((data) => { + if (!data) { + setError(); + return false; + } + else { + return true; + } + }); + } + if (!result) { + setError(); + return false; + } + else { + return true; + } + }); + } + refinement(check, refinementData) { + return this._refinement((val, ctx) => { + if (!check(val)) { + ctx.addIssue(typeof refinementData === "function" + ? refinementData(val, ctx) + : refinementData); + return false; + } + else { + return true; + } + }); + } + _refinement(refinement) { + return new ZodEffects({ + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "refinement", refinement }, + }); + } + superRefine(refinement) { + return this._refinement(refinement); + } + optional() { + return ZodOptional.create(this, this._def); + } + nullable() { + return ZodNullable.create(this, this._def); + } + nullish() { + return this.nullable().optional(); + } + array() { + return ZodArray.create(this, this._def); + } + promise() { + return ZodPromise.create(this, this._def); + } + or(option) { + return ZodUnion.create([this, option], this._def); + } + and(incoming) { + return ZodIntersection.create(this, incoming, this._def); + } + transform(transform) { + return new ZodEffects({ + ...processCreateParams(this._def), + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "transform", transform }, + }); + } + default(def) { + const defaultValueFunc = typeof def === "function" ? def : () => def; + return new ZodDefault({ + ...processCreateParams(this._def), + innerType: this, + defaultValue: defaultValueFunc, + typeName: ZodFirstPartyTypeKind.ZodDefault, + }); + } + brand() { + return new ZodBranded({ + typeName: ZodFirstPartyTypeKind.ZodBranded, + type: this, + ...processCreateParams(this._def), + }); + } + catch(def) { + const catchValueFunc = typeof def === "function" ? def : () => def; + return new ZodCatch({ + ...processCreateParams(this._def), + innerType: this, + catchValue: catchValueFunc, + typeName: ZodFirstPartyTypeKind.ZodCatch, + }); + } + describe(description) { + const This = this.constructor; + return new This({ + ...this._def, + description, + }); + } + pipe(target) { + return ZodPipeline.create(this, target); + } + isOptional() { + return this.safeParse(undefined).success; + } + isNullable() { + return this.safeParse(null).success; + } +} +const cuidRegex = /^c[^\s-]{8,}$/i; +const cuid2Regex = /^[a-z][a-z0-9]*$/; +const ulidRegex = /[0-9A-HJKMNP-TV-Z]{26}/; +const uuidRegex = /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i; +// from https://stackoverflow.com/a/46181/1550155 +// old version: too slow, didn't support unicode +// const emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; +//old email regex +// const emailRegex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@((?!-)([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{1,})[^-<>()[\].,;:\s@"]$/i; +// eslint-disable-next-line +const emailRegex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\])|(\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\.[A-Za-z]{2,})+))$/; +// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression +const emojiRegex = /^(\p{Extended_Pictographic}|\p{Emoji_Component})+$/u; +const ipv4Regex = /^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/; +const ipv6Regex = /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/; +// Adapted from https://stackoverflow.com/a/3143231 +const datetimeRegex = (args) => { + if (args.precision) { + if (args.offset) { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{${args.precision}}(([+-]\\d{2}(:?\\d{2})?)|Z)$`); + } + else { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{${args.precision}}Z$`); + } + } + else if (args.precision === 0) { + if (args.offset) { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(([+-]\\d{2}(:?\\d{2})?)|Z)$`); + } + else { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$`); + } + } + else { + if (args.offset) { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(([+-]\\d{2}(:?\\d{2})?)|Z)$`); + } + else { + return new RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$`); + } + } +}; +function isValidIP(ip, version) { + if ((version === "v4" || !version) && ipv4Regex.test(ip)) { + return true; + } + if ((version === "v6" || !version) && ipv6Regex.test(ip)) { + return true; + } + return false; +} +class ZodString extends ZodType { + constructor() { + super(...arguments); + this._regex = (regex, validation, message) => this.refinement((data) => regex.test(data), { + validation, + code: ZodIssueCode.invalid_string, + ...errorUtil.errToObj(message), + }); + /** + * @deprecated Use z.string().min(1) instead. + * @see {@link ZodString.min} + */ + this.nonempty = (message) => this.min(1, errorUtil.errToObj(message)); + this.trim = () => new ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "trim" }], + }); + this.toLowerCase = () => new ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toLowerCase" }], + }); + this.toUpperCase = () => new ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toUpperCase" }], + }); + } + _parse(input) { + if (this._def.coerce) { + input.data = String(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.string) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.string, + received: ctx.parsedType, + } + // + ); + return INVALID; + } + const status = new ParseStatus(); + let ctx = undefined; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.length < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "max") { + if (input.data.length > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "length") { + const tooBig = input.data.length > check.value; + const tooSmall = input.data.length < check.value; + if (tooBig || tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + if (tooBig) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message, + }); + } + else if (tooSmall) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message, + }); + } + status.dirty(); + } + } + else if (check.kind === "email") { + if (!emailRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "email", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "emoji") { + if (!emojiRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "emoji", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "uuid") { + if (!uuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "uuid", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "cuid") { + if (!cuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "cuid2") { + if (!cuid2Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid2", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "ulid") { + if (!ulidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ulid", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "url") { + try { + new URL(input.data); + } + catch (_a) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "url", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "regex") { + check.regex.lastIndex = 0; + const testResult = check.regex.test(input.data); + if (!testResult) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "regex", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "trim") { + input.data = input.data.trim(); + } + else if (check.kind === "includes") { + if (!input.data.includes(check.value, check.position)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { includes: check.value, position: check.position }, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "toLowerCase") { + input.data = input.data.toLowerCase(); + } + else if (check.kind === "toUpperCase") { + input.data = input.data.toUpperCase(); + } + else if (check.kind === "startsWith") { + if (!input.data.startsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { startsWith: check.value }, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "endsWith") { + if (!input.data.endsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { endsWith: check.value }, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "datetime") { + const regex = datetimeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "datetime", + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "ip") { + if (!isValidIP(input.data, check.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ip", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + _addCheck(check) { + return new ZodString({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + email(message) { + return this._addCheck({ kind: "email", ...errorUtil.errToObj(message) }); + } + url(message) { + return this._addCheck({ kind: "url", ...errorUtil.errToObj(message) }); + } + emoji(message) { + return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message) }); + } + uuid(message) { + return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message) }); + } + cuid(message) { + return this._addCheck({ kind: "cuid", ...errorUtil.errToObj(message) }); + } + cuid2(message) { + return this._addCheck({ kind: "cuid2", ...errorUtil.errToObj(message) }); + } + ulid(message) { + return this._addCheck({ kind: "ulid", ...errorUtil.errToObj(message) }); + } + ip(options) { + return this._addCheck({ kind: "ip", ...errorUtil.errToObj(options) }); + } + datetime(options) { + var _a; + if (typeof options === "string") { + return this._addCheck({ + kind: "datetime", + precision: null, + offset: false, + message: options, + }); + } + return this._addCheck({ + kind: "datetime", + precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === "undefined" ? null : options === null || options === void 0 ? void 0 : options.precision, + offset: (_a = options === null || options === void 0 ? void 0 : options.offset) !== null && _a !== void 0 ? _a : false, + ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message), + }); + } + regex(regex, message) { + return this._addCheck({ + kind: "regex", + regex: regex, + ...errorUtil.errToObj(message), + }); + } + includes(value, options) { + return this._addCheck({ + kind: "includes", + value: value, + position: options === null || options === void 0 ? void 0 : options.position, + ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message), + }); + } + startsWith(value, message) { + return this._addCheck({ + kind: "startsWith", + value: value, + ...errorUtil.errToObj(message), + }); + } + endsWith(value, message) { + return this._addCheck({ + kind: "endsWith", + value: value, + ...errorUtil.errToObj(message), + }); + } + min(minLength, message) { + return this._addCheck({ + kind: "min", + value: minLength, + ...errorUtil.errToObj(message), + }); + } + max(maxLength, message) { + return this._addCheck({ + kind: "max", + value: maxLength, + ...errorUtil.errToObj(message), + }); + } + length(len, message) { + return this._addCheck({ + kind: "length", + value: len, + ...errorUtil.errToObj(message), + }); + } + get isDatetime() { + return !!this._def.checks.find((ch) => ch.kind === "datetime"); + } + get isEmail() { + return !!this._def.checks.find((ch) => ch.kind === "email"); + } + get isURL() { + return !!this._def.checks.find((ch) => ch.kind === "url"); + } + get isEmoji() { + return !!this._def.checks.find((ch) => ch.kind === "emoji"); + } + get isUUID() { + return !!this._def.checks.find((ch) => ch.kind === "uuid"); + } + get isCUID() { + return !!this._def.checks.find((ch) => ch.kind === "cuid"); + } + get isCUID2() { + return !!this._def.checks.find((ch) => ch.kind === "cuid2"); + } + get isULID() { + return !!this._def.checks.find((ch) => ch.kind === "ulid"); + } + get isIP() { + return !!this._def.checks.find((ch) => ch.kind === "ip"); + } + get minLength() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxLength() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +} +ZodString.create = (params) => { + var _a; + return new ZodString({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodString, + coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false, + ...processCreateParams(params), + }); +}; +// https://stackoverflow.com/questions/3966484/why-does-modulus-operator-return-fractional-number-in-javascript/31711034#31711034 +function floatSafeRemainder(val, step) { + const valDecCount = (val.toString().split(".")[1] || "").length; + const stepDecCount = (step.toString().split(".")[1] || "").length; + const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount; + const valInt = parseInt(val.toFixed(decCount).replace(".", "")); + const stepInt = parseInt(step.toFixed(decCount).replace(".", "")); + return (valInt % stepInt) / Math.pow(10, decCount); +} +class ZodNumber extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + this.step = this.multipleOf; + } + _parse(input) { + if (this._def.coerce) { + input.data = Number(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.number) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.number, + received: ctx.parsedType, + }); + return INVALID; + } + let ctx = undefined; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "int") { + if (!util.isInteger(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: "integer", + received: "float", + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "min") { + const tooSmall = check.inclusive + ? input.data < check.value + : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "max") { + const tooBig = check.inclusive + ? input.data > check.value + : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "multipleOf") { + if (floatSafeRemainder(input.data, check.value) !== 0) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "finite") { + if (!Number.isFinite(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_finite, + message: check.message, + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new ZodNumber({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message), + }, + ], + }); + } + _addCheck(check) { + return new ZodNumber({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + int(message) { + return this._addCheck({ + kind: "int", + message: errorUtil.toString(message), + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: false, + message: errorUtil.toString(message), + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: false, + message: errorUtil.toString(message), + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: true, + message: errorUtil.toString(message), + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: true, + message: errorUtil.toString(message), + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value: value, + message: errorUtil.toString(message), + }); + } + finite(message) { + return this._addCheck({ + kind: "finite", + message: errorUtil.toString(message), + }); + } + safe(message) { + return this._addCheck({ + kind: "min", + inclusive: true, + value: Number.MIN_SAFE_INTEGER, + message: errorUtil.toString(message), + })._addCheck({ + kind: "max", + inclusive: true, + value: Number.MAX_SAFE_INTEGER, + message: errorUtil.toString(message), + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } + get isInt() { + return !!this._def.checks.find((ch) => ch.kind === "int" || + (ch.kind === "multipleOf" && util.isInteger(ch.value))); + } + get isFinite() { + let max = null, min = null; + for (const ch of this._def.checks) { + if (ch.kind === "finite" || + ch.kind === "int" || + ch.kind === "multipleOf") { + return true; + } + else if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + else if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return Number.isFinite(min) && Number.isFinite(max); + } +} +ZodNumber.create = (params) => { + return new ZodNumber({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodNumber, + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + ...processCreateParams(params), + }); +}; +class ZodBigInt extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + } + _parse(input) { + if (this._def.coerce) { + input.data = BigInt(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.bigint) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.bigint, + received: ctx.parsedType, + }); + return INVALID; + } + let ctx = undefined; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "min") { + const tooSmall = check.inclusive + ? input.data < check.value + : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + type: "bigint", + minimum: check.value, + inclusive: check.inclusive, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "max") { + const tooBig = check.inclusive + ? input.data > check.value + : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + type: "bigint", + maximum: check.value, + inclusive: check.inclusive, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "multipleOf") { + if (input.data % check.value !== BigInt(0)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message, + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new ZodBigInt({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message), + }, + ], + }); + } + _addCheck(check) { + return new ZodBigInt({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message), + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message), + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message), + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message), + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message), + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +} +ZodBigInt.create = (params) => { + var _a; + return new ZodBigInt({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodBigInt, + coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false, + ...processCreateParams(params), + }); +}; +class ZodBoolean extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = Boolean(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.boolean) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.boolean, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodBoolean.create = (params) => { + return new ZodBoolean({ + typeName: ZodFirstPartyTypeKind.ZodBoolean, + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + ...processCreateParams(params), + }); +}; +class ZodDate extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = new Date(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.date) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.date, + received: ctx.parsedType, + }); + return INVALID; + } + if (isNaN(input.data.getTime())) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_date, + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = undefined; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.getTime() < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + message: check.message, + inclusive: true, + exact: false, + minimum: check.value, + type: "date", + }); + status.dirty(); + } + } + else if (check.kind === "max") { + if (input.data.getTime() > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + message: check.message, + inclusive: true, + exact: false, + maximum: check.value, + type: "date", + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { + status: status.value, + value: new Date(input.data.getTime()), + }; + } + _addCheck(check) { + return new ZodDate({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + min(minDate, message) { + return this._addCheck({ + kind: "min", + value: minDate.getTime(), + message: errorUtil.toString(message), + }); + } + max(maxDate, message) { + return this._addCheck({ + kind: "max", + value: maxDate.getTime(), + message: errorUtil.toString(message), + }); + } + get minDate() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min != null ? new Date(min) : null; + } + get maxDate() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max != null ? new Date(max) : null; + } +} +ZodDate.create = (params) => { + return new ZodDate({ + checks: [], + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + typeName: ZodFirstPartyTypeKind.ZodDate, + ...processCreateParams(params), + }); +}; +class ZodSymbol extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.symbol) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.symbol, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodSymbol.create = (params) => { + return new ZodSymbol({ + typeName: ZodFirstPartyTypeKind.ZodSymbol, + ...processCreateParams(params), + }); +}; +class ZodUndefined extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.undefined, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodUndefined.create = (params) => { + return new ZodUndefined({ + typeName: ZodFirstPartyTypeKind.ZodUndefined, + ...processCreateParams(params), + }); +}; +class ZodNull extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.null) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.null, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodNull.create = (params) => { + return new ZodNull({ + typeName: ZodFirstPartyTypeKind.ZodNull, + ...processCreateParams(params), + }); +}; +class ZodAny extends ZodType { + constructor() { + super(...arguments); + // to prevent instances of other classes from extending ZodAny. this causes issues with catchall in ZodObject. + this._any = true; + } + _parse(input) { + return OK(input.data); + } +} +ZodAny.create = (params) => { + return new ZodAny({ + typeName: ZodFirstPartyTypeKind.ZodAny, + ...processCreateParams(params), + }); +}; +class ZodUnknown extends ZodType { + constructor() { + super(...arguments); + // required + this._unknown = true; + } + _parse(input) { + return OK(input.data); + } +} +ZodUnknown.create = (params) => { + return new ZodUnknown({ + typeName: ZodFirstPartyTypeKind.ZodUnknown, + ...processCreateParams(params), + }); +}; +class ZodNever extends ZodType { + _parse(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.never, + received: ctx.parsedType, + }); + return INVALID; + } +} +ZodNever.create = (params) => { + return new ZodNever({ + typeName: ZodFirstPartyTypeKind.ZodNever, + ...processCreateParams(params), + }); +}; +class ZodVoid extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.void, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodVoid.create = (params) => { + return new ZodVoid({ + typeName: ZodFirstPartyTypeKind.ZodVoid, + ...processCreateParams(params), + }); +}; +class ZodArray extends ZodType { + _parse(input) { + const { ctx, status } = this._processInputParams(input); + const def = this._def; + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType, + }); + return INVALID; + } + if (def.exactLength !== null) { + const tooBig = ctx.data.length > def.exactLength.value; + const tooSmall = ctx.data.length < def.exactLength.value; + if (tooBig || tooSmall) { + addIssueToContext(ctx, { + code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small, + minimum: (tooSmall ? def.exactLength.value : undefined), + maximum: (tooBig ? def.exactLength.value : undefined), + type: "array", + inclusive: true, + exact: true, + message: def.exactLength.message, + }); + status.dirty(); + } + } + if (def.minLength !== null) { + if (ctx.data.length < def.minLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.minLength.message, + }); + status.dirty(); + } + } + if (def.maxLength !== null) { + if (ctx.data.length > def.maxLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.maxLength.message, + }); + status.dirty(); + } + } + if (ctx.common.async) { + return Promise.all([...ctx.data].map((item, i) => { + return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + })).then((result) => { + return ParseStatus.mergeArray(status, result); + }); + } + const result = [...ctx.data].map((item, i) => { + return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + }); + return ParseStatus.mergeArray(status, result); + } + get element() { + return this._def.type; + } + min(minLength, message) { + return new ZodArray({ + ...this._def, + minLength: { value: minLength, message: errorUtil.toString(message) }, + }); + } + max(maxLength, message) { + return new ZodArray({ + ...this._def, + maxLength: { value: maxLength, message: errorUtil.toString(message) }, + }); + } + length(len, message) { + return new ZodArray({ + ...this._def, + exactLength: { value: len, message: errorUtil.toString(message) }, + }); + } + nonempty(message) { + return this.min(1, message); + } +} +ZodArray.create = (schema, params) => { + return new ZodArray({ + type: schema, + minLength: null, + maxLength: null, + exactLength: null, + typeName: ZodFirstPartyTypeKind.ZodArray, + ...processCreateParams(params), + }); +}; +function deepPartialify(schema) { + if (schema instanceof ZodObject) { + const newShape = {}; + for (const key in schema.shape) { + const fieldSchema = schema.shape[key]; + newShape[key] = ZodOptional.create(deepPartialify(fieldSchema)); + } + return new ZodObject({ + ...schema._def, + shape: () => newShape, + }); + } + else if (schema instanceof ZodArray) { + return new ZodArray({ + ...schema._def, + type: deepPartialify(schema.element), + }); + } + else if (schema instanceof ZodOptional) { + return ZodOptional.create(deepPartialify(schema.unwrap())); + } + else if (schema instanceof ZodNullable) { + return ZodNullable.create(deepPartialify(schema.unwrap())); + } + else if (schema instanceof ZodTuple) { + return ZodTuple.create(schema.items.map((item) => deepPartialify(item))); + } + else { + return schema; + } +} +class ZodObject extends ZodType { + constructor() { + super(...arguments); + this._cached = null; + /** + * @deprecated In most cases, this is no longer needed - unknown properties are now silently stripped. + * If you want to pass through unknown properties, use `.passthrough()` instead. + */ + this.nonstrict = this.passthrough; + // extend< + // Augmentation extends ZodRawShape, + // NewOutput extends util.flatten<{ + // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation + // ? Augmentation[k]["_output"] + // : k extends keyof Output + // ? Output[k] + // : never; + // }>, + // NewInput extends util.flatten<{ + // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation + // ? Augmentation[k]["_input"] + // : k extends keyof Input + // ? Input[k] + // : never; + // }> + // >( + // augmentation: Augmentation + // ): ZodObject< + // extendShape, + // UnknownKeys, + // Catchall, + // NewOutput, + // NewInput + // > { + // return new ZodObject({ + // ...this._def, + // shape: () => ({ + // ...this._def.shape(), + // ...augmentation, + // }), + // }) as any; + // } + /** + * @deprecated Use `.extend` instead + * */ + this.augment = this.extend; + } + _getCached() { + if (this._cached !== null) + return this._cached; + const shape = this._def.shape(); + const keys = util.objectKeys(shape); + return (this._cached = { shape, keys }); + } + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.object) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType, + }); + return INVALID; + } + const { status, ctx } = this._processInputParams(input); + const { shape, keys: shapeKeys } = this._getCached(); + const extraKeys = []; + if (!(this._def.catchall instanceof ZodNever && + this._def.unknownKeys === "strip")) { + for (const key in ctx.data) { + if (!shapeKeys.includes(key)) { + extraKeys.push(key); + } + } + } + const pairs = []; + for (const key of shapeKeys) { + const keyValidator = shape[key]; + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)), + alwaysSet: key in ctx.data, + }); + } + if (this._def.catchall instanceof ZodNever) { + const unknownKeys = this._def.unknownKeys; + if (unknownKeys === "passthrough") { + for (const key of extraKeys) { + pairs.push({ + key: { status: "valid", value: key }, + value: { status: "valid", value: ctx.data[key] }, + }); + } + } + else if (unknownKeys === "strict") { + if (extraKeys.length > 0) { + addIssueToContext(ctx, { + code: ZodIssueCode.unrecognized_keys, + keys: extraKeys, + }); + status.dirty(); + } + } + else if (unknownKeys === "strip") ; + else { + throw new Error(`Internal ZodObject error: invalid unknownKeys value.`); + } + } + else { + // run catchall validation + const catchall = this._def.catchall; + for (const key of extraKeys) { + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: catchall._parse(new ParseInputLazyPath(ctx, value, ctx.path, key) //, ctx.child(key), value, getParsedType(value) + ), + alwaysSet: key in ctx.data, + }); + } + } + if (ctx.common.async) { + return Promise.resolve() + .then(async () => { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + syncPairs.push({ + key, + value: await pair.value, + alwaysSet: pair.alwaysSet, + }); + } + return syncPairs; + }) + .then((syncPairs) => { + return ParseStatus.mergeObjectSync(status, syncPairs); + }); + } + else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get shape() { + return this._def.shape(); + } + strict(message) { + errorUtil.errToObj; + return new ZodObject({ + ...this._def, + unknownKeys: "strict", + ...(message !== undefined + ? { + errorMap: (issue, ctx) => { + var _a, _b, _c, _d; + const defaultError = (_c = (_b = (_a = this._def).errorMap) === null || _b === void 0 ? void 0 : _b.call(_a, issue, ctx).message) !== null && _c !== void 0 ? _c : ctx.defaultError; + if (issue.code === "unrecognized_keys") + return { + message: (_d = errorUtil.errToObj(message).message) !== null && _d !== void 0 ? _d : defaultError, + }; + return { + message: defaultError, + }; + }, + } + : {}), + }); + } + strip() { + return new ZodObject({ + ...this._def, + unknownKeys: "strip", + }); + } + passthrough() { + return new ZodObject({ + ...this._def, + unknownKeys: "passthrough", + }); + } + // const AugmentFactory = + // (def: Def) => + // ( + // augmentation: Augmentation + // ): ZodObject< + // extendShape, Augmentation>, + // Def["unknownKeys"], + // Def["catchall"] + // > => { + // return new ZodObject({ + // ...def, + // shape: () => ({ + // ...def.shape(), + // ...augmentation, + // }), + // }) as any; + // }; + extend(augmentation) { + return new ZodObject({ + ...this._def, + shape: () => ({ + ...this._def.shape(), + ...augmentation, + }), + }); + } + /** + * Prior to zod@1.0.12 there was a bug in the + * inferred type of merged objects. Please + * upgrade if you are experiencing issues. + */ + merge(merging) { + const merged = new ZodObject({ + unknownKeys: merging._def.unknownKeys, + catchall: merging._def.catchall, + shape: () => ({ + ...this._def.shape(), + ...merging._def.shape(), + }), + typeName: ZodFirstPartyTypeKind.ZodObject, + }); + return merged; + } + // merge< + // Incoming extends AnyZodObject, + // Augmentation extends Incoming["shape"], + // NewOutput extends { + // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation + // ? Augmentation[k]["_output"] + // : k extends keyof Output + // ? Output[k] + // : never; + // }, + // NewInput extends { + // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation + // ? Augmentation[k]["_input"] + // : k extends keyof Input + // ? Input[k] + // : never; + // } + // >( + // merging: Incoming + // ): ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"], + // NewOutput, + // NewInput + // > { + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + setKey(key, schema) { + return this.augment({ [key]: schema }); + } + // merge( + // merging: Incoming + // ): //ZodObject = (merging) => { + // ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"] + // > { + // // const mergedShape = objectUtil.mergeShapes( + // // this._def.shape(), + // // merging._def.shape() + // // ); + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + catchall(index) { + return new ZodObject({ + ...this._def, + catchall: index, + }); + } + pick(mask) { + const shape = {}; + util.objectKeys(mask).forEach((key) => { + if (mask[key] && this.shape[key]) { + shape[key] = this.shape[key]; + } + }); + return new ZodObject({ + ...this._def, + shape: () => shape, + }); + } + omit(mask) { + const shape = {}; + util.objectKeys(this.shape).forEach((key) => { + if (!mask[key]) { + shape[key] = this.shape[key]; + } + }); + return new ZodObject({ + ...this._def, + shape: () => shape, + }); + } + /** + * @deprecated + */ + deepPartial() { + return deepPartialify(this); + } + partial(mask) { + const newShape = {}; + util.objectKeys(this.shape).forEach((key) => { + const fieldSchema = this.shape[key]; + if (mask && !mask[key]) { + newShape[key] = fieldSchema; + } + else { + newShape[key] = fieldSchema.optional(); + } + }); + return new ZodObject({ + ...this._def, + shape: () => newShape, + }); + } + required(mask) { + const newShape = {}; + util.objectKeys(this.shape).forEach((key) => { + if (mask && !mask[key]) { + newShape[key] = this.shape[key]; + } + else { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional) { + newField = newField._def.innerType; + } + newShape[key] = newField; + } + }); + return new ZodObject({ + ...this._def, + shape: () => newShape, + }); + } + keyof() { + return createZodEnum(util.objectKeys(this.shape)); + } +} +ZodObject.create = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params), + }); +}; +ZodObject.strictCreate = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strict", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params), + }); +}; +ZodObject.lazycreate = (shape, params) => { + return new ZodObject({ + shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params), + }); +}; +class ZodUnion extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const options = this._def.options; + function handleResults(results) { + // return first issue-free validation if it exists + for (const result of results) { + if (result.result.status === "valid") { + return result.result; + } + } + for (const result of results) { + if (result.result.status === "dirty") { + // add issues from dirty option + ctx.common.issues.push(...result.ctx.common.issues); + return result.result; + } + } + // return invalid + const unionErrors = results.map((result) => new ZodError(result.ctx.common.issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors, + }); + return INVALID; + } + if (ctx.common.async) { + return Promise.all(options.map(async (option) => { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [], + }, + parent: null, + }; + return { + result: await option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: childCtx, + }), + ctx: childCtx, + }; + })).then(handleResults); + } + else { + let dirty = undefined; + const issues = []; + for (const option of options) { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [], + }, + parent: null, + }; + const result = option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: childCtx, + }); + if (result.status === "valid") { + return result; + } + else if (result.status === "dirty" && !dirty) { + dirty = { result, ctx: childCtx }; + } + if (childCtx.common.issues.length) { + issues.push(childCtx.common.issues); + } + } + if (dirty) { + ctx.common.issues.push(...dirty.ctx.common.issues); + return dirty.result; + } + const unionErrors = issues.map((issues) => new ZodError(issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors, + }); + return INVALID; + } + } + get options() { + return this._def.options; + } +} +ZodUnion.create = (types, params) => { + return new ZodUnion({ + options: types, + typeName: ZodFirstPartyTypeKind.ZodUnion, + ...processCreateParams(params), + }); +}; +///////////////////////////////////////////////////// +///////////////////////////////////////////////////// +////////// ////////// +////////// ZodDiscriminatedUnion ////////// +////////// ////////// +///////////////////////////////////////////////////// +///////////////////////////////////////////////////// +const getDiscriminator = (type) => { + if (type instanceof ZodLazy) { + return getDiscriminator(type.schema); + } + else if (type instanceof ZodEffects) { + return getDiscriminator(type.innerType()); + } + else if (type instanceof ZodLiteral) { + return [type.value]; + } + else if (type instanceof ZodEnum) { + return type.options; + } + else if (type instanceof ZodNativeEnum) { + // eslint-disable-next-line ban/ban + return Object.keys(type.enum); + } + else if (type instanceof ZodDefault) { + return getDiscriminator(type._def.innerType); + } + else if (type instanceof ZodUndefined) { + return [undefined]; + } + else if (type instanceof ZodNull) { + return [null]; + } + else { + return null; + } +}; +class ZodDiscriminatedUnion extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType, + }); + return INVALID; + } + const discriminator = this.discriminator; + const discriminatorValue = ctx.data[discriminator]; + const option = this.optionsMap.get(discriminatorValue); + if (!option) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union_discriminator, + options: Array.from(this.optionsMap.keys()), + path: [discriminator], + }); + return INVALID; + } + if (ctx.common.async) { + return option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + } + else { + return option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + } + } + get discriminator() { + return this._def.discriminator; + } + get options() { + return this._def.options; + } + get optionsMap() { + return this._def.optionsMap; + } + /** + * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor. + * However, it only allows a union of objects, all of which need to share a discriminator property. This property must + * have a different value for each object in the union. + * @param discriminator the name of the discriminator property + * @param types an array of object schemas + * @param params + */ + static create(discriminator, options, params) { + // Get all the valid discriminator values + const optionsMap = new Map(); + // try { + for (const type of options) { + const discriminatorValues = getDiscriminator(type.shape[discriminator]); + if (!discriminatorValues) { + throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`); + } + for (const value of discriminatorValues) { + if (optionsMap.has(value)) { + throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`); + } + optionsMap.set(value, type); + } + } + return new ZodDiscriminatedUnion({ + typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion, + discriminator, + options, + optionsMap, + ...processCreateParams(params), + }); + } +} +function mergeValues(a, b) { + const aType = getParsedType(a); + const bType = getParsedType(b); + if (a === b) { + return { valid: true, data: a }; + } + else if (aType === ZodParsedType.object && bType === ZodParsedType.object) { + const bKeys = util.objectKeys(b); + const sharedKeys = util + .objectKeys(a) + .filter((key) => bKeys.indexOf(key) !== -1); + const newObj = { ...a, ...b }; + for (const key of sharedKeys) { + const sharedValue = mergeValues(a[key], b[key]); + if (!sharedValue.valid) { + return { valid: false }; + } + newObj[key] = sharedValue.data; + } + return { valid: true, data: newObj }; + } + else if (aType === ZodParsedType.array && bType === ZodParsedType.array) { + if (a.length !== b.length) { + return { valid: false }; + } + const newArray = []; + for (let index = 0; index < a.length; index++) { + const itemA = a[index]; + const itemB = b[index]; + const sharedValue = mergeValues(itemA, itemB); + if (!sharedValue.valid) { + return { valid: false }; + } + newArray.push(sharedValue.data); + } + return { valid: true, data: newArray }; + } + else if (aType === ZodParsedType.date && + bType === ZodParsedType.date && + +a === +b) { + return { valid: true, data: a }; + } + else { + return { valid: false }; + } +} +class ZodIntersection extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const handleParsed = (parsedLeft, parsedRight) => { + if (isAborted(parsedLeft) || isAborted(parsedRight)) { + return INVALID; + } + const merged = mergeValues(parsedLeft.value, parsedRight.value); + if (!merged.valid) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_intersection_types, + }); + return INVALID; + } + if (isDirty(parsedLeft) || isDirty(parsedRight)) { + status.dirty(); + } + return { status: status.value, value: merged.data }; + }; + if (ctx.common.async) { + return Promise.all([ + this._def.left._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }), + this._def.right._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }), + ]).then(([left, right]) => handleParsed(left, right)); + } + else { + return handleParsed(this._def.left._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }), this._def.right._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + })); + } + } +} +ZodIntersection.create = (left, right, params) => { + return new ZodIntersection({ + left: left, + right: right, + typeName: ZodFirstPartyTypeKind.ZodIntersection, + ...processCreateParams(params), + }); +}; +class ZodTuple extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType, + }); + return INVALID; + } + if (ctx.data.length < this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: this._def.items.length, + inclusive: true, + exact: false, + type: "array", + }); + return INVALID; + } + const rest = this._def.rest; + if (!rest && ctx.data.length > this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: this._def.items.length, + inclusive: true, + exact: false, + type: "array", + }); + status.dirty(); + } + const items = [...ctx.data] + .map((item, itemIndex) => { + const schema = this._def.items[itemIndex] || this._def.rest; + if (!schema) + return null; + return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex)); + }) + .filter((x) => !!x); // filter nulls + if (ctx.common.async) { + return Promise.all(items).then((results) => { + return ParseStatus.mergeArray(status, results); + }); + } + else { + return ParseStatus.mergeArray(status, items); + } + } + get items() { + return this._def.items; + } + rest(rest) { + return new ZodTuple({ + ...this._def, + rest, + }); + } +} +ZodTuple.create = (schemas, params) => { + if (!Array.isArray(schemas)) { + throw new Error("You must pass an array of schemas to z.tuple([ ... ])"); + } + return new ZodTuple({ + items: schemas, + typeName: ZodFirstPartyTypeKind.ZodTuple, + rest: null, + ...processCreateParams(params), + }); +}; +class ZodRecord extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType, + }); + return INVALID; + } + const pairs = []; + const keyType = this._def.keyType; + const valueType = this._def.valueType; + for (const key in ctx.data) { + pairs.push({ + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)), + value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)), + }); + } + if (ctx.common.async) { + return ParseStatus.mergeObjectAsync(status, pairs); + } + else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get element() { + return this._def.valueType; + } + static create(first, second, third) { + if (second instanceof ZodType) { + return new ZodRecord({ + keyType: first, + valueType: second, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(third), + }); + } + return new ZodRecord({ + keyType: ZodString.create(), + valueType: first, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(second), + }); + } +} +class ZodMap extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.map) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.map, + received: ctx.parsedType, + }); + return INVALID; + } + const keyType = this._def.keyType; + const valueType = this._def.valueType; + const pairs = [...ctx.data.entries()].map(([key, value], index) => { + return { + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, "key"])), + value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"])), + }; + }); + if (ctx.common.async) { + const finalMap = new Map(); + return Promise.resolve().then(async () => { + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + }); + } + else { + const finalMap = new Map(); + for (const pair of pairs) { + const key = pair.key; + const value = pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + } + } +} +ZodMap.create = (keyType, valueType, params) => { + return new ZodMap({ + valueType, + keyType, + typeName: ZodFirstPartyTypeKind.ZodMap, + ...processCreateParams(params), + }); +}; +class ZodSet extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.set) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.set, + received: ctx.parsedType, + }); + return INVALID; + } + const def = this._def; + if (def.minSize !== null) { + if (ctx.data.size < def.minSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.minSize.message, + }); + status.dirty(); + } + } + if (def.maxSize !== null) { + if (ctx.data.size > def.maxSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.maxSize.message, + }); + status.dirty(); + } + } + const valueType = this._def.valueType; + function finalizeSet(elements) { + const parsedSet = new Set(); + for (const element of elements) { + if (element.status === "aborted") + return INVALID; + if (element.status === "dirty") + status.dirty(); + parsedSet.add(element.value); + } + return { status: status.value, value: parsedSet }; + } + const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i))); + if (ctx.common.async) { + return Promise.all(elements).then((elements) => finalizeSet(elements)); + } + else { + return finalizeSet(elements); + } + } + min(minSize, message) { + return new ZodSet({ + ...this._def, + minSize: { value: minSize, message: errorUtil.toString(message) }, + }); + } + max(maxSize, message) { + return new ZodSet({ + ...this._def, + maxSize: { value: maxSize, message: errorUtil.toString(message) }, + }); + } + size(size, message) { + return this.min(size, message).max(size, message); + } + nonempty(message) { + return this.min(1, message); + } +} +ZodSet.create = (valueType, params) => { + return new ZodSet({ + valueType, + minSize: null, + maxSize: null, + typeName: ZodFirstPartyTypeKind.ZodSet, + ...processCreateParams(params), + }); +}; +class ZodFunction extends ZodType { + constructor() { + super(...arguments); + this.validate = this.implement; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.function) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.function, + received: ctx.parsedType, + }); + return INVALID; + } + function makeArgsIssue(args, error) { + return makeIssue({ + data: args, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + getErrorMap(), + errorMap, + ].filter((x) => !!x), + issueData: { + code: ZodIssueCode.invalid_arguments, + argumentsError: error, + }, + }); + } + function makeReturnsIssue(returns, error) { + return makeIssue({ + data: returns, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + getErrorMap(), + errorMap, + ].filter((x) => !!x), + issueData: { + code: ZodIssueCode.invalid_return_type, + returnTypeError: error, + }, + }); + } + const params = { errorMap: ctx.common.contextualErrorMap }; + const fn = ctx.data; + if (this._def.returns instanceof ZodPromise) { + return OK(async (...args) => { + const error = new ZodError([]); + const parsedArgs = await this._def.args + .parseAsync(args, params) + .catch((e) => { + error.addIssue(makeArgsIssue(args, e)); + throw error; + }); + const result = await fn(...parsedArgs); + const parsedReturns = await this._def.returns._def.type + .parseAsync(result, params) + .catch((e) => { + error.addIssue(makeReturnsIssue(result, e)); + throw error; + }); + return parsedReturns; + }); + } + else { + return OK((...args) => { + const parsedArgs = this._def.args.safeParse(args, params); + if (!parsedArgs.success) { + throw new ZodError([makeArgsIssue(args, parsedArgs.error)]); + } + const result = fn(...parsedArgs.data); + const parsedReturns = this._def.returns.safeParse(result, params); + if (!parsedReturns.success) { + throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]); + } + return parsedReturns.data; + }); + } + } + parameters() { + return this._def.args; + } + returnType() { + return this._def.returns; + } + args(...items) { + return new ZodFunction({ + ...this._def, + args: ZodTuple.create(items).rest(ZodUnknown.create()), + }); + } + returns(returnType) { + return new ZodFunction({ + ...this._def, + returns: returnType, + }); + } + implement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + strictImplement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + static create(args, returns, params) { + return new ZodFunction({ + args: (args + ? args + : ZodTuple.create([]).rest(ZodUnknown.create())), + returns: returns || ZodUnknown.create(), + typeName: ZodFirstPartyTypeKind.ZodFunction, + ...processCreateParams(params), + }); + } +} +class ZodLazy extends ZodType { + get schema() { + return this._def.getter(); + } + _parse(input) { + const { ctx } = this._processInputParams(input); + const lazySchema = this._def.getter(); + return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx }); + } +} +ZodLazy.create = (getter, params) => { + return new ZodLazy({ + getter: getter, + typeName: ZodFirstPartyTypeKind.ZodLazy, + ...processCreateParams(params), + }); +}; +class ZodLiteral extends ZodType { + _parse(input) { + if (input.data !== this._def.value) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_literal, + expected: this._def.value, + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } + get value() { + return this._def.value; + } +} +ZodLiteral.create = (value, params) => { + return new ZodLiteral({ + value: value, + typeName: ZodFirstPartyTypeKind.ZodLiteral, + ...processCreateParams(params), + }); +}; +function createZodEnum(values, params) { + return new ZodEnum({ + values: values, + typeName: ZodFirstPartyTypeKind.ZodEnum, + ...processCreateParams(params), + }); +} +class ZodEnum extends ZodType { + _parse(input) { + if (typeof input.data !== "string") { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + expected: util.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type, + }); + return INVALID; + } + if (this._def.values.indexOf(input.data) === -1) { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues, + }); + return INVALID; + } + return OK(input.data); + } + get options() { + return this._def.values; + } + get enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Values() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + extract(values) { + return ZodEnum.create(values); + } + exclude(values) { + return ZodEnum.create(this.options.filter((opt) => !values.includes(opt))); + } +} +ZodEnum.create = createZodEnum; +class ZodNativeEnum extends ZodType { + _parse(input) { + const nativeEnumValues = util.getValidEnumValues(this._def.values); + const ctx = this._getOrReturnCtx(input); + if (ctx.parsedType !== ZodParsedType.string && + ctx.parsedType !== ZodParsedType.number) { + const expectedValues = util.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + expected: util.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type, + }); + return INVALID; + } + if (nativeEnumValues.indexOf(input.data) === -1) { + const expectedValues = util.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues, + }); + return INVALID; + } + return OK(input.data); + } + get enum() { + return this._def.values; + } +} +ZodNativeEnum.create = (values, params) => { + return new ZodNativeEnum({ + values: values, + typeName: ZodFirstPartyTypeKind.ZodNativeEnum, + ...processCreateParams(params), + }); +}; +class ZodPromise extends ZodType { + unwrap() { + return this._def.type; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.promise && + ctx.common.async === false) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.promise, + received: ctx.parsedType, + }); + return INVALID; + } + const promisified = ctx.parsedType === ZodParsedType.promise + ? ctx.data + : Promise.resolve(ctx.data); + return OK(promisified.then((data) => { + return this._def.type.parseAsync(data, { + path: ctx.path, + errorMap: ctx.common.contextualErrorMap, + }); + })); + } +} +ZodPromise.create = (schema, params) => { + return new ZodPromise({ + type: schema, + typeName: ZodFirstPartyTypeKind.ZodPromise, + ...processCreateParams(params), + }); +}; +class ZodEffects extends ZodType { + innerType() { + return this._def.schema; + } + sourceType() { + return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects + ? this._def.schema.sourceType() + : this._def.schema; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const effect = this._def.effect || null; + if (effect.type === "preprocess") { + const processed = effect.transform(ctx.data); + if (ctx.common.async) { + return Promise.resolve(processed).then((processed) => { + return this._def.schema._parseAsync({ + data: processed, + path: ctx.path, + parent: ctx, + }); + }); + } + else { + return this._def.schema._parseSync({ + data: processed, + path: ctx.path, + parent: ctx, + }); + } + } + const checkCtx = { + addIssue: (arg) => { + addIssueToContext(ctx, arg); + if (arg.fatal) { + status.abort(); + } + else { + status.dirty(); + } + }, + get path() { + return ctx.path; + }, + }; + checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx); + if (effect.type === "refinement") { + const executeRefinement = (acc + // effect: RefinementEffect + ) => { + const result = effect.refinement(acc, checkCtx); + if (ctx.common.async) { + return Promise.resolve(result); + } + if (result instanceof Promise) { + throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead."); + } + return acc; + }; + if (ctx.common.async === false) { + const inner = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + // return value is ignored + executeRefinement(inner.value); + return { status: status.value, value: inner.value }; + } + else { + return this._def.schema + ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }) + .then((inner) => { + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + return executeRefinement(inner.value).then(() => { + return { status: status.value, value: inner.value }; + }); + }); + } + } + if (effect.type === "transform") { + if (ctx.common.async === false) { + const base = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (!isValid(base)) + return base; + const result = effect.transform(base.value, checkCtx); + if (result instanceof Promise) { + throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`); + } + return { status: status.value, value: result }; + } + else { + return this._def.schema + ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }) + .then((base) => { + if (!isValid(base)) + return base; + return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ status: status.value, value: result })); + }); + } + } + util.assertNever(effect); + } +} +ZodEffects.create = (schema, effect, params) => { + return new ZodEffects({ + schema, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect, + ...processCreateParams(params), + }); +}; +ZodEffects.createWithPreprocess = (preprocess, schema, params) => { + return new ZodEffects({ + schema, + effect: { type: "preprocess", transform: preprocess }, + typeName: ZodFirstPartyTypeKind.ZodEffects, + ...processCreateParams(params), + }); +}; +class ZodOptional extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.undefined) { + return OK(undefined); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +} +ZodOptional.create = (type, params) => { + return new ZodOptional({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodOptional, + ...processCreateParams(params), + }); +}; +class ZodNullable extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.null) { + return OK(null); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +} +ZodNullable.create = (type, params) => { + return new ZodNullable({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodNullable, + ...processCreateParams(params), + }); +}; +class ZodDefault extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + let data = ctx.data; + if (ctx.parsedType === ZodParsedType.undefined) { + data = this._def.defaultValue(); + } + return this._def.innerType._parse({ + data, + path: ctx.path, + parent: ctx, + }); + } + removeDefault() { + return this._def.innerType; + } +} +ZodDefault.create = (type, params) => { + return new ZodDefault({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodDefault, + defaultValue: typeof params.default === "function" + ? params.default + : () => params.default, + ...processCreateParams(params), + }); +}; +class ZodCatch extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + // newCtx is used to not collect issues from inner types in ctx + const newCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [], + }, + }; + const result = this._def.innerType._parse({ + data: newCtx.data, + path: newCtx.path, + parent: { + ...newCtx, + }, + }); + if (isAsync(result)) { + return result.then((result) => { + return { + status: "valid", + value: result.status === "valid" + ? result.value + : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data, + }), + }; + }); + } + else { + return { + status: "valid", + value: result.status === "valid" + ? result.value + : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data, + }), + }; + } + } + removeCatch() { + return this._def.innerType; + } +} +ZodCatch.create = (type, params) => { + return new ZodCatch({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodCatch, + catchValue: typeof params.catch === "function" ? params.catch : () => params.catch, + ...processCreateParams(params), + }); +}; +class ZodNaN extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.nan) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.nan, + received: ctx.parsedType, + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } +} +ZodNaN.create = (params) => { + return new ZodNaN({ + typeName: ZodFirstPartyTypeKind.ZodNaN, + ...processCreateParams(params), + }); +}; +const BRAND = Symbol("zod_brand"); +class ZodBranded extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const data = ctx.data; + return this._def.type._parse({ + data, + path: ctx.path, + parent: ctx, + }); + } + unwrap() { + return this._def.type; + } +} +class ZodPipeline extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.common.async) { + const handleAsync = async () => { + const inResult = await this._def.in._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return DIRTY(inResult.value); + } + else { + return this._def.out._parseAsync({ + data: inResult.value, + path: ctx.path, + parent: ctx, + }); + } + }; + return handleAsync(); + } + else { + const inResult = this._def.in._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return { + status: "dirty", + value: inResult.value, + }; + } + else { + return this._def.out._parseSync({ + data: inResult.value, + path: ctx.path, + parent: ctx, + }); + } + } + } + static create(a, b) { + return new ZodPipeline({ + in: a, + out: b, + typeName: ZodFirstPartyTypeKind.ZodPipeline, + }); + } +} +const custom = (check, params = {}, +/* + * @deprecated + * + * Pass `fatal` into the params object instead: + * + * ```ts + * z.string().custom((val) => val.length > 5, { fatal: false }) + * ``` + * + */ +fatal) => { + if (check) + return ZodAny.create().superRefine((data, ctx) => { + var _a, _b; + if (!check(data)) { + const p = typeof params === "function" + ? params(data) + : typeof params === "string" + ? { message: params } + : params; + const _fatal = (_b = (_a = p.fatal) !== null && _a !== void 0 ? _a : fatal) !== null && _b !== void 0 ? _b : true; + const p2 = typeof p === "string" ? { message: p } : p; + ctx.addIssue({ code: "custom", ...p2, fatal: _fatal }); + } + }); + return ZodAny.create(); +}; +const late = { + object: ZodObject.lazycreate, +}; +var ZodFirstPartyTypeKind; +(function (ZodFirstPartyTypeKind) { + ZodFirstPartyTypeKind["ZodString"] = "ZodString"; + ZodFirstPartyTypeKind["ZodNumber"] = "ZodNumber"; + ZodFirstPartyTypeKind["ZodNaN"] = "ZodNaN"; + ZodFirstPartyTypeKind["ZodBigInt"] = "ZodBigInt"; + ZodFirstPartyTypeKind["ZodBoolean"] = "ZodBoolean"; + ZodFirstPartyTypeKind["ZodDate"] = "ZodDate"; + ZodFirstPartyTypeKind["ZodSymbol"] = "ZodSymbol"; + ZodFirstPartyTypeKind["ZodUndefined"] = "ZodUndefined"; + ZodFirstPartyTypeKind["ZodNull"] = "ZodNull"; + ZodFirstPartyTypeKind["ZodAny"] = "ZodAny"; + ZodFirstPartyTypeKind["ZodUnknown"] = "ZodUnknown"; + ZodFirstPartyTypeKind["ZodNever"] = "ZodNever"; + ZodFirstPartyTypeKind["ZodVoid"] = "ZodVoid"; + ZodFirstPartyTypeKind["ZodArray"] = "ZodArray"; + ZodFirstPartyTypeKind["ZodObject"] = "ZodObject"; + ZodFirstPartyTypeKind["ZodUnion"] = "ZodUnion"; + ZodFirstPartyTypeKind["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion"; + ZodFirstPartyTypeKind["ZodIntersection"] = "ZodIntersection"; + ZodFirstPartyTypeKind["ZodTuple"] = "ZodTuple"; + ZodFirstPartyTypeKind["ZodRecord"] = "ZodRecord"; + ZodFirstPartyTypeKind["ZodMap"] = "ZodMap"; + ZodFirstPartyTypeKind["ZodSet"] = "ZodSet"; + ZodFirstPartyTypeKind["ZodFunction"] = "ZodFunction"; + ZodFirstPartyTypeKind["ZodLazy"] = "ZodLazy"; + ZodFirstPartyTypeKind["ZodLiteral"] = "ZodLiteral"; + ZodFirstPartyTypeKind["ZodEnum"] = "ZodEnum"; + ZodFirstPartyTypeKind["ZodEffects"] = "ZodEffects"; + ZodFirstPartyTypeKind["ZodNativeEnum"] = "ZodNativeEnum"; + ZodFirstPartyTypeKind["ZodOptional"] = "ZodOptional"; + ZodFirstPartyTypeKind["ZodNullable"] = "ZodNullable"; + ZodFirstPartyTypeKind["ZodDefault"] = "ZodDefault"; + ZodFirstPartyTypeKind["ZodCatch"] = "ZodCatch"; + ZodFirstPartyTypeKind["ZodPromise"] = "ZodPromise"; + ZodFirstPartyTypeKind["ZodBranded"] = "ZodBranded"; + ZodFirstPartyTypeKind["ZodPipeline"] = "ZodPipeline"; +})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {})); +const instanceOfType = ( +// const instanceOfType = any>( +cls, params = { + message: `Input not instance of ${cls.name}`, +}) => custom((data) => data instanceof cls, params); +const stringType = ZodString.create; +const numberType = ZodNumber.create; +const nanType = ZodNaN.create; +const bigIntType = ZodBigInt.create; +const booleanType = ZodBoolean.create; +const dateType = ZodDate.create; +const symbolType = ZodSymbol.create; +const undefinedType = ZodUndefined.create; +const nullType = ZodNull.create; +const anyType = ZodAny.create; +const unknownType = ZodUnknown.create; +const neverType = ZodNever.create; +const voidType = ZodVoid.create; +const arrayType = ZodArray.create; +const objectType = ZodObject.create; +const strictObjectType = ZodObject.strictCreate; +const unionType = ZodUnion.create; +const discriminatedUnionType = ZodDiscriminatedUnion.create; +const intersectionType = ZodIntersection.create; +const tupleType = ZodTuple.create; +const recordType = ZodRecord.create; +const mapType = ZodMap.create; +const setType = ZodSet.create; +const functionType = ZodFunction.create; +const lazyType = ZodLazy.create; +const literalType = ZodLiteral.create; +const enumType = ZodEnum.create; +const nativeEnumType = ZodNativeEnum.create; +const promiseType = ZodPromise.create; +const effectsType = ZodEffects.create; +const optionalType = ZodOptional.create; +const nullableType = ZodNullable.create; +const preprocessType = ZodEffects.createWithPreprocess; +const pipelineType = ZodPipeline.create; +const ostring = () => stringType().optional(); +const onumber = () => numberType().optional(); +const oboolean = () => booleanType().optional(); +const coerce = { + string: ((arg) => ZodString.create({ ...arg, coerce: true })), + number: ((arg) => ZodNumber.create({ ...arg, coerce: true })), + boolean: ((arg) => ZodBoolean.create({ + ...arg, + coerce: true, + })), + bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })), + date: ((arg) => ZodDate.create({ ...arg, coerce: true })), +}; +const NEVER = INVALID; + +var z = /*#__PURE__*/Object.freeze({ + __proto__: null, + defaultErrorMap: errorMap, + setErrorMap: setErrorMap, + getErrorMap: getErrorMap, + makeIssue: makeIssue, + EMPTY_PATH: EMPTY_PATH, + addIssueToContext: addIssueToContext, + ParseStatus: ParseStatus, + INVALID: INVALID, + DIRTY: DIRTY, + OK: OK, + isAborted: isAborted, + isDirty: isDirty, + isValid: isValid, + isAsync: isAsync, + get util () { return util; }, + get objectUtil () { return objectUtil; }, + ZodParsedType: ZodParsedType, + getParsedType: getParsedType, + ZodType: ZodType, + ZodString: ZodString, + ZodNumber: ZodNumber, + ZodBigInt: ZodBigInt, + ZodBoolean: ZodBoolean, + ZodDate: ZodDate, + ZodSymbol: ZodSymbol, + ZodUndefined: ZodUndefined, + ZodNull: ZodNull, + ZodAny: ZodAny, + ZodUnknown: ZodUnknown, + ZodNever: ZodNever, + ZodVoid: ZodVoid, + ZodArray: ZodArray, + ZodObject: ZodObject, + ZodUnion: ZodUnion, + ZodDiscriminatedUnion: ZodDiscriminatedUnion, + ZodIntersection: ZodIntersection, + ZodTuple: ZodTuple, + ZodRecord: ZodRecord, + ZodMap: ZodMap, + ZodSet: ZodSet, + ZodFunction: ZodFunction, + ZodLazy: ZodLazy, + ZodLiteral: ZodLiteral, + ZodEnum: ZodEnum, + ZodNativeEnum: ZodNativeEnum, + ZodPromise: ZodPromise, + ZodEffects: ZodEffects, + ZodTransformer: ZodEffects, + ZodOptional: ZodOptional, + ZodNullable: ZodNullable, + ZodDefault: ZodDefault, + ZodCatch: ZodCatch, + ZodNaN: ZodNaN, + BRAND: BRAND, + ZodBranded: ZodBranded, + ZodPipeline: ZodPipeline, + custom: custom, + Schema: ZodType, + ZodSchema: ZodType, + late: late, + get ZodFirstPartyTypeKind () { return ZodFirstPartyTypeKind; }, + coerce: coerce, + any: anyType, + array: arrayType, + bigint: bigIntType, + boolean: booleanType, + date: dateType, + discriminatedUnion: discriminatedUnionType, + effect: effectsType, + 'enum': enumType, + 'function': functionType, + 'instanceof': instanceOfType, + intersection: intersectionType, + lazy: lazyType, + literal: literalType, + map: mapType, + nan: nanType, + nativeEnum: nativeEnumType, + never: neverType, + 'null': nullType, + nullable: nullableType, + number: numberType, + object: objectType, + oboolean: oboolean, + onumber: onumber, + optional: optionalType, + ostring: ostring, + pipeline: pipelineType, + preprocess: preprocessType, + promise: promiseType, + record: recordType, + set: setType, + strictObject: strictObjectType, + string: stringType, + symbol: symbolType, + transformer: effectsType, + tuple: tupleType, + 'undefined': undefinedType, + union: unionType, + unknown: unknownType, + 'void': voidType, + NEVER: NEVER, + ZodIssueCode: ZodIssueCode, + quotelessJson: quotelessJson, + ZodError: ZodError +}); + +export { BRAND, DIRTY, EMPTY_PATH, INVALID, NEVER, OK, ParseStatus, ZodType as Schema, ZodAny, ZodArray, ZodBigInt, ZodBoolean, ZodBranded, ZodCatch, ZodDate, ZodDefault, ZodDiscriminatedUnion, ZodEffects, ZodEnum, ZodError, ZodFirstPartyTypeKind, ZodFunction, ZodIntersection, ZodIssueCode, ZodLazy, ZodLiteral, ZodMap, ZodNaN, ZodNativeEnum, ZodNever, ZodNull, ZodNullable, ZodNumber, ZodObject, ZodOptional, ZodParsedType, ZodPipeline, ZodPromise, ZodRecord, ZodType as ZodSchema, ZodSet, ZodString, ZodSymbol, ZodEffects as ZodTransformer, ZodTuple, ZodType, ZodUndefined, ZodUnion, ZodUnknown, ZodVoid, addIssueToContext, anyType as any, arrayType as array, bigIntType as bigint, booleanType as boolean, coerce, custom, dateType as date, z as default, errorMap as defaultErrorMap, discriminatedUnionType as discriminatedUnion, effectsType as effect, enumType as enum, functionType as function, getErrorMap, getParsedType, instanceOfType as instanceof, intersectionType as intersection, isAborted, isAsync, isDirty, isValid, late, lazyType as lazy, literalType as literal, makeIssue, mapType as map, nanType as nan, nativeEnumType as nativeEnum, neverType as never, nullType as null, nullableType as nullable, numberType as number, objectType as object, objectUtil, oboolean, onumber, optionalType as optional, ostring, pipelineType as pipeline, preprocessType as preprocess, promiseType as promise, quotelessJson, recordType as record, setType as set, setErrorMap, strictObjectType as strictObject, stringType as string, symbolType as symbol, effectsType as transformer, tupleType as tuple, undefinedType as undefined, unionType as union, unknownType as unknown, util, voidType as void, z }; diff --git a/extras/zod/lib/locales/en.d.ts b/extras/zod/lib/locales/en.d.ts new file mode 100644 index 0000000..e5a3871 --- /dev/null +++ b/extras/zod/lib/locales/en.d.ts @@ -0,0 +1,3 @@ +import { ZodErrorMap } from "../ZodError"; +declare const errorMap: ZodErrorMap; +export default errorMap; diff --git a/extras/zod/lib/locales/en.js b/extras/zod/lib/locales/en.js new file mode 100644 index 0000000..98342a0 --- /dev/null +++ b/extras/zod/lib/locales/en.js @@ -0,0 +1,129 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const util_1 = require("../helpers/util"); +const ZodError_1 = require("../ZodError"); +const errorMap = (issue, _ctx) => { + let message; + switch (issue.code) { + case ZodError_1.ZodIssueCode.invalid_type: + if (issue.received === util_1.ZodParsedType.undefined) { + message = "Required"; + } + else { + message = `Expected ${issue.expected}, received ${issue.received}`; + } + break; + case ZodError_1.ZodIssueCode.invalid_literal: + message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util_1.util.jsonStringifyReplacer)}`; + break; + case ZodError_1.ZodIssueCode.unrecognized_keys: + message = `Unrecognized key(s) in object: ${util_1.util.joinValues(issue.keys, ", ")}`; + break; + case ZodError_1.ZodIssueCode.invalid_union: + message = `Invalid input`; + break; + case ZodError_1.ZodIssueCode.invalid_union_discriminator: + message = `Invalid discriminator value. Expected ${util_1.util.joinValues(issue.options)}`; + break; + case ZodError_1.ZodIssueCode.invalid_enum_value: + message = `Invalid enum value. Expected ${util_1.util.joinValues(issue.options)}, received '${issue.received}'`; + break; + case ZodError_1.ZodIssueCode.invalid_arguments: + message = `Invalid function arguments`; + break; + case ZodError_1.ZodIssueCode.invalid_return_type: + message = `Invalid function return type`; + break; + case ZodError_1.ZodIssueCode.invalid_date: + message = `Invalid date`; + break; + case ZodError_1.ZodIssueCode.invalid_string: + if (typeof issue.validation === "object") { + if ("includes" in issue.validation) { + message = `Invalid input: must include "${issue.validation.includes}"`; + if (typeof issue.validation.position === "number") { + message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`; + } + } + else if ("startsWith" in issue.validation) { + message = `Invalid input: must start with "${issue.validation.startsWith}"`; + } + else if ("endsWith" in issue.validation) { + message = `Invalid input: must end with "${issue.validation.endsWith}"`; + } + else { + util_1.util.assertNever(issue.validation); + } + } + else if (issue.validation !== "regex") { + message = `Invalid ${issue.validation}`; + } + else { + message = "Invalid"; + } + break; + case ZodError_1.ZodIssueCode.too_small: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact + ? `exactly equal to ` + : issue.inclusive + ? `greater than or equal to ` + : `greater than `}${issue.minimum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact + ? `exactly equal to ` + : issue.inclusive + ? `greater than or equal to ` + : `greater than `}${new Date(Number(issue.minimum))}`; + else + message = "Invalid input"; + break; + case ZodError_1.ZodIssueCode.too_big: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `less than or equal to` + : `less than`} ${issue.maximum}`; + else if (issue.type === "bigint") + message = `BigInt must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `less than or equal to` + : `less than`} ${issue.maximum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `smaller than or equal to` + : `smaller than`} ${new Date(Number(issue.maximum))}`; + else + message = "Invalid input"; + break; + case ZodError_1.ZodIssueCode.custom: + message = `Invalid input`; + break; + case ZodError_1.ZodIssueCode.invalid_intersection_types: + message = `Intersection results could not be merged`; + break; + case ZodError_1.ZodIssueCode.not_multiple_of: + message = `Number must be a multiple of ${issue.multipleOf}`; + break; + case ZodError_1.ZodIssueCode.not_finite: + message = "Number must be finite"; + break; + default: + message = _ctx.defaultError; + util_1.util.assertNever(issue); + } + return { message }; +}; +exports.default = errorMap; diff --git a/extras/zod/lib/types.d.ts b/extras/zod/lib/types.d.ts new file mode 100644 index 0000000..e8b7ecd --- /dev/null +++ b/extras/zod/lib/types.d.ts @@ -0,0 +1,1012 @@ +import { enumUtil } from "./helpers/enumUtil"; +import { errorUtil } from "./helpers/errorUtil"; +import { AsyncParseReturnType, ParseContext, ParseInput, ParseParams, ParseReturnType, ParseStatus, SyncParseReturnType } from "./helpers/parseUtil"; +import { partialUtil } from "./helpers/partialUtil"; +import { Primitive } from "./helpers/typeAliases"; +import { objectUtil, util } from "./helpers/util"; +import { IssueData, StringValidation, ZodCustomIssue, ZodError, ZodErrorMap } from "./ZodError"; +export declare type RefinementCtx = { + addIssue: (arg: IssueData) => void; + path: (string | number)[]; +}; +export declare type ZodRawShape = { + [k: string]: ZodTypeAny; +}; +export declare type ZodTypeAny = ZodType; +export declare type TypeOf> = T["_output"]; +export declare type input> = T["_input"]; +export declare type output> = T["_output"]; +export type { TypeOf as infer }; +export declare type CustomErrorParams = Partial>; +export interface ZodTypeDef { + errorMap?: ZodErrorMap; + description?: string; +} +export declare type RawCreateParams = { + errorMap?: ZodErrorMap; + invalid_type_error?: string; + required_error?: string; + description?: string; +} | undefined; +export declare type ProcessedCreateParams = { + errorMap?: ZodErrorMap; + description?: string; +}; +export declare type SafeParseSuccess = { + success: true; + data: Output; +}; +export declare type SafeParseError = { + success: false; + error: ZodError; +}; +export declare type SafeParseReturnType = SafeParseSuccess | SafeParseError; +export declare abstract class ZodType { + readonly _type: Output; + readonly _output: Output; + readonly _input: Input; + readonly _def: Def; + get description(): string | undefined; + abstract _parse(input: ParseInput): ParseReturnType; + _getType(input: ParseInput): string; + _getOrReturnCtx(input: ParseInput, ctx?: ParseContext | undefined): ParseContext; + _processInputParams(input: ParseInput): { + status: ParseStatus; + ctx: ParseContext; + }; + _parseSync(input: ParseInput): SyncParseReturnType; + _parseAsync(input: ParseInput): AsyncParseReturnType; + parse(data: unknown, params?: Partial): Output; + safeParse(data: unknown, params?: Partial): SafeParseReturnType; + parseAsync(data: unknown, params?: Partial): Promise; + safeParseAsync(data: unknown, params?: Partial): Promise>; + spa: (data: unknown, params?: Partial | undefined) => Promise>; + refine(check: (arg: Output) => arg is RefinedOutput, message?: string | CustomErrorParams | ((arg: Output) => CustomErrorParams)): ZodEffects; + refine(check: (arg: Output) => unknown | Promise, message?: string | CustomErrorParams | ((arg: Output) => CustomErrorParams)): ZodEffects; + refinement(check: (arg: Output) => arg is RefinedOutput, refinementData: IssueData | ((arg: Output, ctx: RefinementCtx) => IssueData)): ZodEffects; + refinement(check: (arg: Output) => boolean, refinementData: IssueData | ((arg: Output, ctx: RefinementCtx) => IssueData)): ZodEffects; + _refinement(refinement: RefinementEffect["refinement"]): ZodEffects; + superRefine(refinement: (arg: Output, ctx: RefinementCtx) => arg is RefinedOutput): ZodEffects; + superRefine(refinement: (arg: Output, ctx: RefinementCtx) => void): ZodEffects; + constructor(def: Def); + optional(): ZodOptional; + nullable(): ZodNullable; + nullish(): ZodOptional>; + array(): ZodArray; + promise(): ZodPromise; + or(option: T): ZodUnion<[this, T]>; + and(incoming: T): ZodIntersection; + transform(transform: (arg: Output, ctx: RefinementCtx) => NewOut | Promise): ZodEffects; + default(def: util.noUndefined): ZodDefault; + default(def: () => util.noUndefined): ZodDefault; + brand(brand?: B): ZodBranded; + catch(def: Output): ZodCatch; + catch(def: (ctx: { + error: ZodError; + input: Input; + }) => Output): ZodCatch; + describe(description: string): this; + pipe(target: T): ZodPipeline; + isOptional(): boolean; + isNullable(): boolean; +} +export declare type IpVersion = "v4" | "v6"; +export declare type ZodStringCheck = { + kind: "min"; + value: number; + message?: string; +} | { + kind: "max"; + value: number; + message?: string; +} | { + kind: "length"; + value: number; + message?: string; +} | { + kind: "email"; + message?: string; +} | { + kind: "url"; + message?: string; +} | { + kind: "emoji"; + message?: string; +} | { + kind: "uuid"; + message?: string; +} | { + kind: "cuid"; + message?: string; +} | { + kind: "includes"; + value: string; + position?: number; + message?: string; +} | { + kind: "cuid2"; + message?: string; +} | { + kind: "ulid"; + message?: string; +} | { + kind: "startsWith"; + value: string; + message?: string; +} | { + kind: "endsWith"; + value: string; + message?: string; +} | { + kind: "regex"; + regex: RegExp; + message?: string; +} | { + kind: "trim"; + message?: string; +} | { + kind: "toLowerCase"; + message?: string; +} | { + kind: "toUpperCase"; + message?: string; +} | { + kind: "datetime"; + offset: boolean; + precision: number | null; + message?: string; +} | { + kind: "ip"; + version?: IpVersion; + message?: string; +}; +export interface ZodStringDef extends ZodTypeDef { + checks: ZodStringCheck[]; + typeName: ZodFirstPartyTypeKind.ZodString; + coerce: boolean; +} +export declare class ZodString extends ZodType { + _parse(input: ParseInput): ParseReturnType; + protected _regex: (regex: RegExp, validation: StringValidation, message?: errorUtil.ErrMessage | undefined) => ZodEffects; + _addCheck(check: ZodStringCheck): ZodString; + email(message?: errorUtil.ErrMessage): ZodString; + url(message?: errorUtil.ErrMessage): ZodString; + emoji(message?: errorUtil.ErrMessage): ZodString; + uuid(message?: errorUtil.ErrMessage): ZodString; + cuid(message?: errorUtil.ErrMessage): ZodString; + cuid2(message?: errorUtil.ErrMessage): ZodString; + ulid(message?: errorUtil.ErrMessage): ZodString; + ip(options?: string | { + version?: "v4" | "v6"; + message?: string; + }): ZodString; + datetime(options?: string | { + message?: string | undefined; + precision?: number | null; + offset?: boolean; + }): ZodString; + regex(regex: RegExp, message?: errorUtil.ErrMessage): ZodString; + includes(value: string, options?: { + message?: string; + position?: number; + }): ZodString; + startsWith(value: string, message?: errorUtil.ErrMessage): ZodString; + endsWith(value: string, message?: errorUtil.ErrMessage): ZodString; + min(minLength: number, message?: errorUtil.ErrMessage): ZodString; + max(maxLength: number, message?: errorUtil.ErrMessage): ZodString; + length(len: number, message?: errorUtil.ErrMessage): ZodString; + nonempty: (message?: errorUtil.ErrMessage | undefined) => ZodString; + trim: () => ZodString; + toLowerCase: () => ZodString; + toUpperCase: () => ZodString; + get isDatetime(): boolean; + get isEmail(): boolean; + get isURL(): boolean; + get isEmoji(): boolean; + get isUUID(): boolean; + get isCUID(): boolean; + get isCUID2(): boolean; + get isULID(): boolean; + get isIP(): boolean; + get minLength(): number | null; + get maxLength(): number | null; + static create: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + coerce?: true | undefined; + }) | undefined) => ZodString; +} +export declare type ZodNumberCheck = { + kind: "min"; + value: number; + inclusive: boolean; + message?: string; +} | { + kind: "max"; + value: number; + inclusive: boolean; + message?: string; +} | { + kind: "int"; + message?: string; +} | { + kind: "multipleOf"; + value: number; + message?: string; +} | { + kind: "finite"; + message?: string; +}; +export interface ZodNumberDef extends ZodTypeDef { + checks: ZodNumberCheck[]; + typeName: ZodFirstPartyTypeKind.ZodNumber; + coerce: boolean; +} +export declare class ZodNumber extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + coerce?: boolean | undefined; + }) | undefined) => ZodNumber; + gte(value: number, message?: errorUtil.ErrMessage): ZodNumber; + min: (value: number, message?: errorUtil.ErrMessage | undefined) => ZodNumber; + gt(value: number, message?: errorUtil.ErrMessage): ZodNumber; + lte(value: number, message?: errorUtil.ErrMessage): ZodNumber; + max: (value: number, message?: errorUtil.ErrMessage | undefined) => ZodNumber; + lt(value: number, message?: errorUtil.ErrMessage): ZodNumber; + protected setLimit(kind: "min" | "max", value: number, inclusive: boolean, message?: string): ZodNumber; + _addCheck(check: ZodNumberCheck): ZodNumber; + int(message?: errorUtil.ErrMessage): ZodNumber; + positive(message?: errorUtil.ErrMessage): ZodNumber; + negative(message?: errorUtil.ErrMessage): ZodNumber; + nonpositive(message?: errorUtil.ErrMessage): ZodNumber; + nonnegative(message?: errorUtil.ErrMessage): ZodNumber; + multipleOf(value: number, message?: errorUtil.ErrMessage): ZodNumber; + step: (value: number, message?: errorUtil.ErrMessage | undefined) => ZodNumber; + finite(message?: errorUtil.ErrMessage): ZodNumber; + safe(message?: errorUtil.ErrMessage): ZodNumber; + get minValue(): number | null; + get maxValue(): number | null; + get isInt(): boolean; + get isFinite(): boolean; +} +export declare type ZodBigIntCheck = { + kind: "min"; + value: bigint; + inclusive: boolean; + message?: string; +} | { + kind: "max"; + value: bigint; + inclusive: boolean; + message?: string; +} | { + kind: "multipleOf"; + value: bigint; + message?: string; +}; +export interface ZodBigIntDef extends ZodTypeDef { + checks: ZodBigIntCheck[]; + typeName: ZodFirstPartyTypeKind.ZodBigInt; + coerce: boolean; +} +export declare class ZodBigInt extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + coerce?: boolean | undefined; + }) | undefined) => ZodBigInt; + gte(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + min: (value: bigint, message?: errorUtil.ErrMessage | undefined) => ZodBigInt; + gt(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + lte(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + max: (value: bigint, message?: errorUtil.ErrMessage | undefined) => ZodBigInt; + lt(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + protected setLimit(kind: "min" | "max", value: bigint, inclusive: boolean, message?: string): ZodBigInt; + _addCheck(check: ZodBigIntCheck): ZodBigInt; + positive(message?: errorUtil.ErrMessage): ZodBigInt; + negative(message?: errorUtil.ErrMessage): ZodBigInt; + nonpositive(message?: errorUtil.ErrMessage): ZodBigInt; + nonnegative(message?: errorUtil.ErrMessage): ZodBigInt; + multipleOf(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + get minValue(): bigint | null; + get maxValue(): bigint | null; +} +export interface ZodBooleanDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodBoolean; + coerce: boolean; +} +export declare class ZodBoolean extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + coerce?: boolean | undefined; + }) | undefined) => ZodBoolean; +} +export declare type ZodDateCheck = { + kind: "min"; + value: number; + message?: string; +} | { + kind: "max"; + value: number; + message?: string; +}; +export interface ZodDateDef extends ZodTypeDef { + checks: ZodDateCheck[]; + coerce: boolean; + typeName: ZodFirstPartyTypeKind.ZodDate; +} +export declare class ZodDate extends ZodType { + _parse(input: ParseInput): ParseReturnType; + _addCheck(check: ZodDateCheck): ZodDate; + min(minDate: Date, message?: errorUtil.ErrMessage): ZodDate; + max(maxDate: Date, message?: errorUtil.ErrMessage): ZodDate; + get minDate(): Date | null; + get maxDate(): Date | null; + static create: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + coerce?: boolean | undefined; + }) | undefined) => ZodDate; +} +export interface ZodSymbolDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodSymbol; +} +export declare class ZodSymbol extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodSymbol; +} +export interface ZodUndefinedDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodUndefined; +} +export declare class ZodUndefined extends ZodType { + _parse(input: ParseInput): ParseReturnType; + params?: RawCreateParams; + static create: (params?: RawCreateParams) => ZodUndefined; +} +export interface ZodNullDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodNull; +} +export declare class ZodNull extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodNull; +} +export interface ZodAnyDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodAny; +} +export declare class ZodAny extends ZodType { + _any: true; + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodAny; +} +export interface ZodUnknownDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodUnknown; +} +export declare class ZodUnknown extends ZodType { + _unknown: true; + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodUnknown; +} +export interface ZodNeverDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodNever; +} +export declare class ZodNever extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodNever; +} +export interface ZodVoidDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodVoid; +} +export declare class ZodVoid extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodVoid; +} +export interface ZodArrayDef extends ZodTypeDef { + type: T; + typeName: ZodFirstPartyTypeKind.ZodArray; + exactLength: { + value: number; + message?: string; + } | null; + minLength: { + value: number; + message?: string; + } | null; + maxLength: { + value: number; + message?: string; + } | null; +} +export declare type ArrayCardinality = "many" | "atleastone"; +export declare type arrayOutputType = Cardinality extends "atleastone" ? [T["_output"], ...T["_output"][]] : T["_output"][]; +export declare class ZodArray extends ZodType, ZodArrayDef, Cardinality extends "atleastone" ? [T["_input"], ...T["_input"][]] : T["_input"][]> { + _parse(input: ParseInput): ParseReturnType; + get element(): T; + min(minLength: number, message?: errorUtil.ErrMessage): this; + max(maxLength: number, message?: errorUtil.ErrMessage): this; + length(len: number, message?: errorUtil.ErrMessage): this; + nonempty(message?: errorUtil.ErrMessage): ZodArray; + static create: (schema: T_1, params?: RawCreateParams) => ZodArray; +} +export declare type ZodNonEmptyArray = ZodArray; +export declare type UnknownKeysParam = "passthrough" | "strict" | "strip"; +export interface ZodObjectDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodObject; + shape: () => T; + catchall: Catchall; + unknownKeys: UnknownKeys; +} +export declare type mergeTypes = { + [k in keyof A | keyof B]: k extends keyof B ? B[k] : k extends keyof A ? A[k] : never; +}; +export declare type objectOutputType = objectUtil.flatten>> & CatchallOutput & PassthroughType; +export declare type baseObjectOutputType = { + [k in keyof Shape]: Shape[k]["_output"]; +}; +export declare type objectInputType = objectUtil.flatten> & CatchallInput & PassthroughType; +export declare type baseObjectInputType = objectUtil.addQuestionMarks<{ + [k in keyof Shape]: Shape[k]["_input"]; +}>; +export declare type CatchallOutput = ZodTypeAny extends T ? unknown : { + [k: string]: T["_output"]; +}; +export declare type CatchallInput = ZodTypeAny extends T ? unknown : { + [k: string]: T["_input"]; +}; +export declare type PassthroughType = T extends "passthrough" ? { + [k: string]: unknown; +} : unknown; +export declare type deoptional = T extends ZodOptional ? deoptional : T extends ZodNullable ? ZodNullable> : T; +export declare type SomeZodObject = ZodObject; +export declare type noUnrecognized = { + [k in keyof Obj]: k extends keyof Shape ? Obj[k] : never; +}; +export declare class ZodObject, Input = objectInputType> extends ZodType, Input> { + private _cached; + _getCached(): { + shape: T; + keys: string[]; + }; + _parse(input: ParseInput): ParseReturnType; + get shape(): T; + strict(message?: errorUtil.ErrMessage): ZodObject; + strip(): ZodObject; + passthrough(): ZodObject; + nonstrict: () => ZodObject; + extend(augmentation: Augmentation): ZodObject, UnknownKeys, Catchall>; + augment: (augmentation: Augmentation) => ZodObject<{ [k in keyof (Omit & Augmentation)]: (Omit & Augmentation)[k]; }, UnknownKeys, Catchall, objectOutputType<{ [k in keyof (Omit & Augmentation)]: (Omit & Augmentation)[k]; }, Catchall, UnknownKeys>, objectInputType<{ [k in keyof (Omit & Augmentation)]: (Omit & Augmentation)[k]; }, Catchall, UnknownKeys>>; + merge(merging: Incoming): ZodObject, Incoming["_def"]["unknownKeys"], Incoming["_def"]["catchall"]>; + setKey(key: Key, schema: Schema): ZodObject; + catchall(index: Index): ZodObject; + pick(mask: Mask): ZodObject>, UnknownKeys, Catchall>; + omit(mask: Mask): ZodObject, UnknownKeys, Catchall>; + deepPartial(): partialUtil.DeepPartial; + partial(): ZodObject<{ + [k in keyof T]: ZodOptional; + }, UnknownKeys, Catchall>; + partial(mask: Mask): ZodObject : T[k]; + }>, UnknownKeys, Catchall>; + required(): ZodObject<{ + [k in keyof T]: deoptional; + }, UnknownKeys, Catchall>; + required(mask: Mask): ZodObject : T[k]; + }>, UnknownKeys, Catchall>; + keyof(): ZodEnum>; + static create: (shape: T_1, params?: RawCreateParams) => ZodObject, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T_1]>]: objectUtil.addQuestionMarks, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T_1]>[k_1]; }, { [k_2 in keyof baseObjectInputType]: baseObjectInputType[k_2]; }>; + static strictCreate: (shape: T_1, params?: RawCreateParams) => ZodObject, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T_1]>]: objectUtil.addQuestionMarks, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T_1]>[k_1]; }, { [k_2 in keyof baseObjectInputType]: baseObjectInputType[k_2]; }>; + static lazycreate: (shape: () => T_1, params?: RawCreateParams) => ZodObject, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T_1]>]: objectUtil.addQuestionMarks, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T_1]>[k_1]; }, { [k_2 in keyof baseObjectInputType]: baseObjectInputType[k_2]; }>; +} +export declare type AnyZodObject = ZodObject; +export declare type ZodUnionOptions = Readonly<[ZodTypeAny, ...ZodTypeAny[]]>; +export interface ZodUnionDef> extends ZodTypeDef { + options: T; + typeName: ZodFirstPartyTypeKind.ZodUnion; +} +export declare class ZodUnion extends ZodType, T[number]["_input"]> { + _parse(input: ParseInput): ParseReturnType; + get options(): T; + static create: (types: T_1, params?: RawCreateParams) => ZodUnion; +} +export declare type ZodDiscriminatedUnionOption = ZodObject<{ + [key in Discriminator]: ZodTypeAny; +} & ZodRawShape, UnknownKeysParam, ZodTypeAny>; +export interface ZodDiscriminatedUnionDef[] = ZodDiscriminatedUnionOption[]> extends ZodTypeDef { + discriminator: Discriminator; + options: Options; + optionsMap: Map>; + typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion; +} +export declare class ZodDiscriminatedUnion[]> extends ZodType, ZodDiscriminatedUnionDef, input> { + _parse(input: ParseInput): ParseReturnType; + get discriminator(): Discriminator; + get options(): Options; + get optionsMap(): Map>; + static create, + ...ZodDiscriminatedUnionOption[] + ]>(discriminator: Discriminator, options: Types, params?: RawCreateParams): ZodDiscriminatedUnion; +} +export interface ZodIntersectionDef extends ZodTypeDef { + left: T; + right: U; + typeName: ZodFirstPartyTypeKind.ZodIntersection; +} +export declare class ZodIntersection extends ZodType, T["_input"] & U["_input"]> { + _parse(input: ParseInput): ParseReturnType; + static create: (left: T_1, right: U_1, params?: RawCreateParams) => ZodIntersection; +} +export declare type ZodTupleItems = [ZodTypeAny, ...ZodTypeAny[]]; +export declare type AssertArray = T extends any[] ? T : never; +export declare type OutputTypeOfTuple = AssertArray<{ + [k in keyof T]: T[k] extends ZodType ? T[k]["_output"] : never; +}>; +export declare type OutputTypeOfTupleWithRest = Rest extends ZodTypeAny ? [...OutputTypeOfTuple, ...Rest["_output"][]] : OutputTypeOfTuple; +export declare type InputTypeOfTuple = AssertArray<{ + [k in keyof T]: T[k] extends ZodType ? T[k]["_input"] : never; +}>; +export declare type InputTypeOfTupleWithRest = Rest extends ZodTypeAny ? [...InputTypeOfTuple, ...Rest["_input"][]] : InputTypeOfTuple; +export interface ZodTupleDef extends ZodTypeDef { + items: T; + rest: Rest; + typeName: ZodFirstPartyTypeKind.ZodTuple; +} +export declare type AnyZodTuple = ZodTuple<[ + ZodTypeAny, + ...ZodTypeAny[] +] | [], ZodTypeAny | null>; +export declare class ZodTuple extends ZodType, ZodTupleDef, InputTypeOfTupleWithRest> { + _parse(input: ParseInput): ParseReturnType; + get items(): T; + rest(rest: Rest): ZodTuple; + static create: (schemas: T_1, params?: RawCreateParams) => ZodTuple; +} +export interface ZodRecordDef extends ZodTypeDef { + valueType: Value; + keyType: Key; + typeName: ZodFirstPartyTypeKind.ZodRecord; +} +export declare type KeySchema = ZodType; +export declare type RecordType = [ + string +] extends [K] ? Record : [number] extends [K] ? Record : [symbol] extends [K] ? Record : [BRAND] extends [K] ? Record : Partial>; +export declare class ZodRecord extends ZodType, ZodRecordDef, RecordType> { + get keySchema(): Key; + get valueSchema(): Value; + _parse(input: ParseInput): ParseReturnType; + get element(): Value; + static create(valueType: Value, params?: RawCreateParams): ZodRecord; + static create(keySchema: Keys, valueType: Value, params?: RawCreateParams): ZodRecord; +} +export interface ZodMapDef extends ZodTypeDef { + valueType: Value; + keyType: Key; + typeName: ZodFirstPartyTypeKind.ZodMap; +} +export declare class ZodMap extends ZodType, ZodMapDef, Map> { + _parse(input: ParseInput): ParseReturnType; + static create: (keyType: Key_1, valueType: Value_1, params?: RawCreateParams) => ZodMap; +} +export interface ZodSetDef extends ZodTypeDef { + valueType: Value; + typeName: ZodFirstPartyTypeKind.ZodSet; + minSize: { + value: number; + message?: string; + } | null; + maxSize: { + value: number; + message?: string; + } | null; +} +export declare class ZodSet extends ZodType, ZodSetDef, Set> { + _parse(input: ParseInput): ParseReturnType; + min(minSize: number, message?: errorUtil.ErrMessage): this; + max(maxSize: number, message?: errorUtil.ErrMessage): this; + size(size: number, message?: errorUtil.ErrMessage): this; + nonempty(message?: errorUtil.ErrMessage): ZodSet; + static create: (valueType: Value_1, params?: RawCreateParams) => ZodSet; +} +export interface ZodFunctionDef = ZodTuple, Returns extends ZodTypeAny = ZodTypeAny> extends ZodTypeDef { + args: Args; + returns: Returns; + typeName: ZodFirstPartyTypeKind.ZodFunction; +} +export declare type OuterTypeOfFunction, Returns extends ZodTypeAny> = Args["_input"] extends Array ? (...args: Args["_input"]) => Returns["_output"] : never; +export declare type InnerTypeOfFunction, Returns extends ZodTypeAny> = Args["_output"] extends Array ? (...args: Args["_output"]) => Returns["_input"] : never; +export declare class ZodFunction, Returns extends ZodTypeAny> extends ZodType, ZodFunctionDef, InnerTypeOfFunction> { + _parse(input: ParseInput): ParseReturnType; + parameters(): Args; + returnType(): Returns; + args[0]>(...items: Items): ZodFunction, Returns>; + returns>(returnType: NewReturnType): ZodFunction; + implement>(func: F): ReturnType extends Returns["_output"] ? (...args: Args["_input"]) => ReturnType : OuterTypeOfFunction; + strictImplement(func: InnerTypeOfFunction): InnerTypeOfFunction; + validate: >(func: F) => ReturnType extends Returns["_output"] ? (...args: Args["_input"]) => ReturnType : OuterTypeOfFunction; + static create(): ZodFunction, ZodUnknown>; + static create>(args: T): ZodFunction; + static create(args: T, returns: U): ZodFunction; + static create, U extends ZodTypeAny = ZodUnknown>(args: T, returns: U, params?: RawCreateParams): ZodFunction; +} +export interface ZodLazyDef extends ZodTypeDef { + getter: () => T; + typeName: ZodFirstPartyTypeKind.ZodLazy; +} +export declare class ZodLazy extends ZodType, ZodLazyDef, input> { + get schema(): T; + _parse(input: ParseInput): ParseReturnType; + static create: (getter: () => T_1, params?: RawCreateParams) => ZodLazy; +} +export interface ZodLiteralDef extends ZodTypeDef { + value: T; + typeName: ZodFirstPartyTypeKind.ZodLiteral; +} +export declare class ZodLiteral extends ZodType> { + _parse(input: ParseInput): ParseReturnType; + get value(): T; + static create: (value: T_1, params?: RawCreateParams) => ZodLiteral; +} +export declare type ArrayKeys = keyof any[]; +export declare type Indices = Exclude; +export declare type EnumValues = [string, ...string[]]; +export declare type Values = { + [k in T[number]]: k; +}; +export interface ZodEnumDef extends ZodTypeDef { + values: T; + typeName: ZodFirstPartyTypeKind.ZodEnum; +} +export declare type Writeable = { + -readonly [P in keyof T]: T[P]; +}; +export declare type FilterEnum = Values extends [] ? [] : Values extends [infer Head, ...infer Rest] ? Head extends ToExclude ? FilterEnum : [Head, ...FilterEnum] : never; +export declare type typecast = A extends T ? A : never; +declare function createZodEnum>(values: T, params?: RawCreateParams): ZodEnum>; +declare function createZodEnum(values: T, params?: RawCreateParams): ZodEnum; +export declare class ZodEnum extends ZodType> { + _parse(input: ParseInput): ParseReturnType; + get options(): T; + get enum(): Values; + get Values(): Values; + get Enum(): Values; + extract(values: ToExtract): ZodEnum>; + exclude(values: ToExclude): ZodEnum>, [string, ...string[]]>>; + static create: typeof createZodEnum; +} +export interface ZodNativeEnumDef extends ZodTypeDef { + values: T; + typeName: ZodFirstPartyTypeKind.ZodNativeEnum; +} +export declare type EnumLike = { + [k: string]: string | number; + [nu: number]: string; +}; +export declare class ZodNativeEnum extends ZodType> { + _parse(input: ParseInput): ParseReturnType; + get enum(): T; + static create: (values: T_1, params?: RawCreateParams) => ZodNativeEnum; +} +export interface ZodPromiseDef extends ZodTypeDef { + type: T; + typeName: ZodFirstPartyTypeKind.ZodPromise; +} +export declare class ZodPromise extends ZodType, ZodPromiseDef, Promise> { + unwrap(): T; + _parse(input: ParseInput): ParseReturnType; + static create: (schema: T_1, params?: RawCreateParams) => ZodPromise; +} +export declare type Refinement = (arg: T, ctx: RefinementCtx) => any; +export declare type SuperRefinement = (arg: T, ctx: RefinementCtx) => void; +export declare type RefinementEffect = { + type: "refinement"; + refinement: (arg: T, ctx: RefinementCtx) => any; +}; +export declare type TransformEffect = { + type: "transform"; + transform: (arg: T, ctx: RefinementCtx) => any; +}; +export declare type PreprocessEffect = { + type: "preprocess"; + transform: (arg: T) => any; +}; +export declare type Effect = RefinementEffect | TransformEffect | PreprocessEffect; +export interface ZodEffectsDef extends ZodTypeDef { + schema: T; + typeName: ZodFirstPartyTypeKind.ZodEffects; + effect: Effect; +} +export declare class ZodEffects, Input = input> extends ZodType, Input> { + innerType(): T; + sourceType(): T; + _parse(input: ParseInput): ParseReturnType; + static create: (schema: I, effect: Effect, params?: RawCreateParams) => ZodEffects>; + static createWithPreprocess: (preprocess: (arg: unknown) => unknown, schema: I, params?: RawCreateParams) => ZodEffects; +} +export { ZodEffects as ZodTransformer }; +export interface ZodOptionalDef extends ZodTypeDef { + innerType: T; + typeName: ZodFirstPartyTypeKind.ZodOptional; +} +export declare type ZodOptionalType = ZodOptional; +export declare class ZodOptional extends ZodType, T["_input"] | undefined> { + _parse(input: ParseInput): ParseReturnType; + unwrap(): T; + static create: (type: T_1, params?: RawCreateParams) => ZodOptional; +} +export interface ZodNullableDef extends ZodTypeDef { + innerType: T; + typeName: ZodFirstPartyTypeKind.ZodNullable; +} +export declare type ZodNullableType = ZodNullable; +export declare class ZodNullable extends ZodType, T["_input"] | null> { + _parse(input: ParseInput): ParseReturnType; + unwrap(): T; + static create: (type: T_1, params?: RawCreateParams) => ZodNullable; +} +export interface ZodDefaultDef extends ZodTypeDef { + innerType: T; + defaultValue: () => util.noUndefined; + typeName: ZodFirstPartyTypeKind.ZodDefault; +} +export declare class ZodDefault extends ZodType, ZodDefaultDef, T["_input"] | undefined> { + _parse(input: ParseInput): ParseReturnType; + removeDefault(): T; + static create: (type: T_1, params: { + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + default: T_1["_input"] | (() => util.noUndefined); + }) => ZodDefault; +} +export interface ZodCatchDef extends ZodTypeDef { + innerType: T; + catchValue: (ctx: { + error: ZodError; + input: unknown; + }) => T["_input"]; + typeName: ZodFirstPartyTypeKind.ZodCatch; +} +export declare class ZodCatch extends ZodType, unknown> { + _parse(input: ParseInput): ParseReturnType; + removeCatch(): T; + static create: (type: T_1, params: { + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + catch: T_1["_output"] | (() => T_1["_output"]); + }) => ZodCatch; +} +export interface ZodNaNDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodNaN; +} +export declare class ZodNaN extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodNaN; +} +export interface ZodBrandedDef extends ZodTypeDef { + type: T; + typeName: ZodFirstPartyTypeKind.ZodBranded; +} +export declare const BRAND: unique symbol; +export declare type BRAND = { + [BRAND]: { + [k in T]: true; + }; +}; +export declare class ZodBranded extends ZodType, ZodBrandedDef, T["_input"]> { + _parse(input: ParseInput): ParseReturnType; + unwrap(): T; +} +export interface ZodPipelineDef extends ZodTypeDef { + in: A; + out: B; + typeName: ZodFirstPartyTypeKind.ZodPipeline; +} +export declare class ZodPipeline extends ZodType, A["_input"]> { + _parse(input: ParseInput): ParseReturnType; + static create(a: A, b: B): ZodPipeline; +} +declare type CustomParams = CustomErrorParams & { + fatal?: boolean; +}; +export declare const custom: (check?: ((data: unknown) => any) | undefined, params?: string | CustomParams | ((input: any) => CustomParams), fatal?: boolean | undefined) => ZodType; +export { ZodType as Schema, ZodType as ZodSchema }; +export declare const late: { + object: (shape: () => T, params?: RawCreateParams) => ZodObject, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T]>]: objectUtil.addQuestionMarks, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T]>[k_1]; }, { [k_2 in keyof baseObjectInputType]: baseObjectInputType[k_2]; }>; +}; +export declare enum ZodFirstPartyTypeKind { + ZodString = "ZodString", + ZodNumber = "ZodNumber", + ZodNaN = "ZodNaN", + ZodBigInt = "ZodBigInt", + ZodBoolean = "ZodBoolean", + ZodDate = "ZodDate", + ZodSymbol = "ZodSymbol", + ZodUndefined = "ZodUndefined", + ZodNull = "ZodNull", + ZodAny = "ZodAny", + ZodUnknown = "ZodUnknown", + ZodNever = "ZodNever", + ZodVoid = "ZodVoid", + ZodArray = "ZodArray", + ZodObject = "ZodObject", + ZodUnion = "ZodUnion", + ZodDiscriminatedUnion = "ZodDiscriminatedUnion", + ZodIntersection = "ZodIntersection", + ZodTuple = "ZodTuple", + ZodRecord = "ZodRecord", + ZodMap = "ZodMap", + ZodSet = "ZodSet", + ZodFunction = "ZodFunction", + ZodLazy = "ZodLazy", + ZodLiteral = "ZodLiteral", + ZodEnum = "ZodEnum", + ZodEffects = "ZodEffects", + ZodNativeEnum = "ZodNativeEnum", + ZodOptional = "ZodOptional", + ZodNullable = "ZodNullable", + ZodDefault = "ZodDefault", + ZodCatch = "ZodCatch", + ZodPromise = "ZodPromise", + ZodBranded = "ZodBranded", + ZodPipeline = "ZodPipeline" +} +export declare type ZodFirstPartySchemaTypes = ZodString | ZodNumber | ZodNaN | ZodBigInt | ZodBoolean | ZodDate | ZodUndefined | ZodNull | ZodAny | ZodUnknown | ZodNever | ZodVoid | ZodArray | ZodObject | ZodUnion | ZodDiscriminatedUnion | ZodIntersection | ZodTuple | ZodRecord | ZodMap | ZodSet | ZodFunction | ZodLazy | ZodLiteral | ZodEnum | ZodEffects | ZodNativeEnum | ZodOptional | ZodNullable | ZodDefault | ZodCatch | ZodPromise | ZodBranded | ZodPipeline; +declare abstract class Class { + constructor(..._: any[]); +} +declare const instanceOfType: (cls: T, params?: CustomParams) => ZodType, ZodTypeDef, InstanceType>; +declare const stringType: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; +} & { + coerce?: true | undefined; +}) | undefined) => ZodString; +declare const numberType: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; +} & { + coerce?: boolean | undefined; +}) | undefined) => ZodNumber; +declare const nanType: (params?: RawCreateParams) => ZodNaN; +declare const bigIntType: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; +} & { + coerce?: boolean | undefined; +}) | undefined) => ZodBigInt; +declare const booleanType: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; +} & { + coerce?: boolean | undefined; +}) | undefined) => ZodBoolean; +declare const dateType: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; +} & { + coerce?: boolean | undefined; +}) | undefined) => ZodDate; +declare const symbolType: (params?: RawCreateParams) => ZodSymbol; +declare const undefinedType: (params?: RawCreateParams) => ZodUndefined; +declare const nullType: (params?: RawCreateParams) => ZodNull; +declare const anyType: (params?: RawCreateParams) => ZodAny; +declare const unknownType: (params?: RawCreateParams) => ZodUnknown; +declare const neverType: (params?: RawCreateParams) => ZodNever; +declare const voidType: (params?: RawCreateParams) => ZodVoid; +declare const arrayType: (schema: T, params?: RawCreateParams) => ZodArray; +declare const objectType: (shape: T, params?: RawCreateParams) => ZodObject, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T]>]: objectUtil.addQuestionMarks, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T]>[k_1]; }, { [k_2 in keyof baseObjectInputType]: baseObjectInputType[k_2]; }>; +declare const strictObjectType: (shape: T, params?: RawCreateParams) => ZodObject, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T]>]: objectUtil.addQuestionMarks, { [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] ? never : k; }[keyof T]>[k_1]; }, { [k_2 in keyof baseObjectInputType]: baseObjectInputType[k_2]; }>; +declare const unionType: (types: T, params?: RawCreateParams) => ZodUnion; +declare const discriminatedUnionType: typeof ZodDiscriminatedUnion.create; +declare const intersectionType: (left: T, right: U, params?: RawCreateParams) => ZodIntersection; +declare const tupleType: (schemas: T, params?: RawCreateParams) => ZodTuple; +declare const recordType: typeof ZodRecord.create; +declare const mapType: (keyType: Key, valueType: Value, params?: RawCreateParams) => ZodMap; +declare const setType: (valueType: Value, params?: RawCreateParams) => ZodSet; +declare const functionType: typeof ZodFunction.create; +declare const lazyType: (getter: () => T, params?: RawCreateParams) => ZodLazy; +declare const literalType: (value: T, params?: RawCreateParams) => ZodLiteral; +declare const enumType: typeof createZodEnum; +declare const nativeEnumType: (values: T, params?: RawCreateParams) => ZodNativeEnum; +declare const promiseType: (schema: T, params?: RawCreateParams) => ZodPromise; +declare const effectsType: (schema: I, effect: Effect, params?: RawCreateParams) => ZodEffects>; +declare const optionalType: (type: T, params?: RawCreateParams) => ZodOptional; +declare const nullableType: (type: T, params?: RawCreateParams) => ZodNullable; +declare const preprocessType: (preprocess: (arg: unknown) => unknown, schema: I, params?: RawCreateParams) => ZodEffects; +declare const pipelineType: typeof ZodPipeline.create; +declare const ostring: () => ZodOptional; +declare const onumber: () => ZodOptional; +declare const oboolean: () => ZodOptional; +export declare const coerce: { + string: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + coerce?: true | undefined; + }) | undefined) => ZodString; + number: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + coerce?: boolean | undefined; + }) | undefined) => ZodNumber; + boolean: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + coerce?: boolean | undefined; + }) | undefined) => ZodBoolean; + bigint: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + coerce?: boolean | undefined; + }) | undefined) => ZodBigInt; + date: (params?: ({ + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + description?: string | undefined; + } & { + coerce?: boolean | undefined; + }) | undefined) => ZodDate; +}; +export { anyType as any, arrayType as array, bigIntType as bigint, booleanType as boolean, dateType as date, discriminatedUnionType as discriminatedUnion, effectsType as effect, enumType as enum, functionType as function, instanceOfType as instanceof, intersectionType as intersection, lazyType as lazy, literalType as literal, mapType as map, nanType as nan, nativeEnumType as nativeEnum, neverType as never, nullType as null, nullableType as nullable, numberType as number, objectType as object, oboolean, onumber, optionalType as optional, ostring, pipelineType as pipeline, preprocessType as preprocess, promiseType as promise, recordType as record, setType as set, strictObjectType as strictObject, stringType as string, symbolType as symbol, effectsType as transformer, tupleType as tuple, undefinedType as undefined, unionType as union, unknownType as unknown, voidType as void, }; +export declare const NEVER: never; diff --git a/extras/zod/package.json b/extras/zod/package.json new file mode 100644 index 0000000..2f47ce4 --- /dev/null +++ b/extras/zod/package.json @@ -0,0 +1,105 @@ +{ + "name": "zod", + "version": "3.21.4", + "author": "Colin McDonnell ", + "repository": { + "type": "git", + "url": "https://github.com/colinhacks/zod" + }, + "main": "./lib/index.js", + "module": "./lib/index.mjs", + "devDependencies": { + "@rollup/plugin-typescript": "^8.2.0", + "@types/benchmark": "^2.1.0", + "@types/jest": "^29.2.2", + "@types/node": "14", + "@typescript-eslint/eslint-plugin": "^5.15.0", + "@typescript-eslint/parser": "^5.15.0", + "benchmark": "^2.1.4", + "dependency-cruiser": "^9.19.0", + "eslint": "^8.11.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-ban": "^1.6.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-simple-import-sort": "^7.0.0", + "eslint-plugin-unused-imports": "^2.0.0", + "husky": "^7.0.4", + "jest": "^29.3.1", + "lint-staged": "^12.3.7", + "nodemon": "^2.0.15", + "prettier": "^2.6.0", + "pretty-quick": "^3.1.3", + "rollup": "^2.70.1", + "ts-jest": "^29.0.3", + "ts-morph": "^14.0.0", + "ts-node": "^10.9.1", + "tslib": "^2.3.1", + "tsx": "^3.8.0", + "typescript": "~4.5.0" + }, + "exports": { + ".": { + "require": "./lib/index.js", + "import": "./lib/index.mjs", + "types": "./index.d.ts" + }, + "./package.json": "./package.json", + "./locales/*": "./lib/locales/*" + }, + "bugs": { + "url": "https://github.com/colinhacks/zod/issues" + }, + "description": "TypeScript-first schema declaration and validation library with static type inference", + "files": [ + "/lib", + "/index.d.ts" + ], + "funding": "https://github.com/sponsors/colinhacks", + "homepage": "https://zod.dev", + "keywords": [ + "typescript", + "schema", + "validation", + "type", + "inference" + ], + "license": "MIT", + "lint-staged": { + "src/*.ts": [ + "eslint --cache --fix", + "prettier --ignore-unknown --write" + ] + }, + "scripts": { + "prettier:check": "prettier --check src/**/*.ts deno/lib/**/*.ts --no-error-on-unmatched-pattern", + "prettier:fix": "prettier --write src/**/*.ts deno/lib/**/*.ts --ignore-unknown --no-error-on-unmatched-pattern", + "lint:check": "eslint --cache --ext .ts ./src", + "lint:fix": "eslint --cache --fix --ext .ts ./src", + "check": "yarn lint:check && yarn prettier:check", + "fix": "yarn lint:fix && yarn prettier:fix", + "clean": "rm -rf lib/* deno/lib/*", + "build": "yarn run clean && npm run build:cjs && npm run build:esm && npm run build:deno", + "build:deno": "node ./deno/build.mjs && cp ./README.md ./deno/lib", + "build:esm": "rollup --config rollup.config.js", + "build:cjs": "tsc -p tsconfig.cjs.json", + "build:types": "tsc -p tsconfig.types.json", + "build:test": "tsc -p tsconfig.test.json", + "rollup": "rollup --config rollup.config.js", + "test:watch": "jest --watch", + "test": "jest --coverage", + "test:deno": "cd deno && deno test", + "prepublishOnly": "npm run test && npm run build && npm run build:deno", + "play": "nodemon -e ts -w . -x tsx playground.ts", + "depcruise": "depcruise -c .dependency-cruiser.js src", + "benchmark": "tsx src/benchmarks/index.ts", + "prepare": "husky install" + }, + "sideEffects": false, + "support": { + "backing": { + "npm-funding": true + } + }, + "types": "./index.d.ts", + "dependencies": {} +} diff --git a/images/eye-dashed.png b/images/eye-dashed.png new file mode 100644 index 0000000000000000000000000000000000000000..e3eebf305bda8f12e9cdb6a09c97ff1c373825e4 GIT binary patch literal 1358 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|{*v!#iv znVGpO5SbbpT9~*xxi}dcxVf0SIXb&q!py*?*TmG>#KpxAZ!`CVki~Z*2MviWdW=76H7l8cj zWZ~v!VPFjOj*GLKiHV64RBsA7VWtmsj6NuFASD)<5HJOTnDC?zSDYIO}$Z!DQ;^3Qp~5;2{Y7wjF1 z&)8L3BQzp3!px>|Ikqgw$V^@MNO{Q#SZS~bG8@YZLrF@=`^BhA%FPpGB?zo$GF+(I{!r!|6%Q9Dn zcui6U?$4&XU_P5`* zauaRlpD)|J_x4*2k%AblsSBo`ei|`N_2hZAgyzH<1r|P+zoc!J>_7hGX_4ph%X!sX zqpoIceSdmO)Y_LNubvfT-!{AY`fCES)Jz|>*=O6F6i;tbn|!it_fe0H5o==fl9SsK zdDxo0mVTP`u)rc_{q?&2?|;SVO+T9!JniYtoM-zEa58isW%~J=?}FW2zZK6vS8j}W zQfW8;{BE;^9dYZK+xrV-zQ5+{cv!%}Vlmh6IBVk3h>(Q=KOPtSfA;*hU;=|o-A$M6 SQ!lRsm2jS}elF{r5}E+fJLa+g literal 0 HcmV?d00001 diff --git a/images/eye-dashed.svg b/images/eye-dashed.svg new file mode 100644 index 0000000..107e3dd --- /dev/null +++ b/images/eye-dashed.svg @@ -0,0 +1,85 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/images/gion/edit-paste.png b/images/gion/edit-paste.png new file mode 100644 index 0000000000000000000000000000000000000000..6fb77755682ac2a442b5923f59ae9b27a76ae2ee GIT binary patch literal 412 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 literal 0 HcmV?d00001 diff --git a/images/page.png b/images/page.png new file mode 100644 index 0000000000000000000000000000000000000000..db60fed4a83da4016d30efb8fc9f48ac7a023a28 GIT binary patch literal 1452 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|q8i>tAz znWdYPo3pEtp`nGDv6G{Pqmi?zrKPiri!00wY`gLD2*8txIZAW?5>ATTyO0Qv}Q!|4ZjLF)%RR@pN$vskpTy z*ft|LP~_jc+V7?Z=euf@Y&3l!%@ige{_@F%l<9r4yIaM$)cE}RvNZgfn%}Wr4tg2X z)a~51Mm2arFw?>d9vq&g@84IQKUsZVe9Ns%%+Kc*%YFZSul$^?f5zHESz(t8FH1_+ zUwn^?Nr>7W&EuFdhu3pYk1zqkh zoeDe;f7_`|o_{R%$>&PDa2>J0l_4ctqeQi)`A$2Vwr~osChO+Y3JSe#E(e1c=WB{^ z$z*Spa#!o9R%6msZ4ux&d;Mh7fu`Ks7nbF1zr7}<@~7jXs%5Ozd3-?~917)}^jgQI}~}{*z(yE12!TZSl)Prc%5$_x?Af xN3Swv*!<>)CYd`K}z6&ZpJzf1=);T3K0RUcN6`BA5 literal 0 HcmV?d00001 diff --git a/includes/AmeAutoloader.php b/includes/AmeAutoloader.php new file mode 100644 index 0000000..29408a6 --- /dev/null +++ b/includes/AmeAutoloader.php @@ -0,0 +1,47 @@ + $namespacePrefixes + */ + public function __construct($namespacePrefixes) { + //Ensure that each prefix ends with a backslash and each path ends + //with a forward slash. + $this->prefixes = array(); + foreach ($namespacePrefixes as $prefix => $path) { + $prefix = trim($prefix, '\\') . '\\'; + $path = rtrim($path, '/\\') . '/'; + $this->prefixes[$prefix] = $path; + } + } + + public function register() { + spl_autoload_register([$this, 'loadClass']); + } + + public function loadClass($class) { + foreach ($this->prefixes as $prefix => $baseDirectory) { + //Does the full class name start with this namespace prefix? + $len = strlen($prefix); + if ( strncmp($prefix, $class, $len) !== 0 ) { + continue; + } + + $relativeClassName = substr($class, $len); + + //Replace the prefix with the base directory, replace namespace separators + //with directory separators, and append the ".php" extension. + $fileName = $baseDirectory . str_replace('\\', '/', $relativeClassName) . '.php'; + + if ( file_exists($fileName) ) { + require $fileName; + } + } + } +} \ No newline at end of file diff --git a/includes/ame-option.php b/includes/ame-option.php index 4d2d1cf..9b12121 100644 --- a/includes/ame-option.php +++ b/includes/ame-option.php @@ -107,6 +107,22 @@ public static function fromValue($value, $emptyValue = null) { public static function fromCallable($callable, $arguments = array()) { return new LazyOption($callable, $arguments); } + + /** + * @template A + * @param A $value + * @return Option + */ + public static function some($value) { + return new Some($value); + } + + /** + * @return Option + */ + public static function none() { + return None::getInstance(); + } } /** diff --git a/includes/ame-utils.php b/includes/ame-utils.php index d05406d..1d2bebf 100644 --- a/includes/ame-utils.php +++ b/includes/ame-utils.php @@ -234,6 +234,58 @@ public static function getFirstItem($collection, $defaultValue = null) { } return $defaultValue; } + + /** + * Get the first key of an iterable collection. + * + * @param iterable $collection + * @param iterable $defaultValue + * @return int|string|null + */ + public static function getFirstKey($collection, $defaultValue = null) { + foreach ($collection as $key => $value) { + return $key; + } + return $defaultValue; + } + + /** + * Send HTTP caching headers. + * + * @param int|null $lastModified Unix timestamp for the last modification time. + * @param int $cacheLifetime Cache lifetime in seconds. + * @return bool True if the response body should be omitted because an If-Modified-Since header + * was sent and the resource hasn't changed. + */ + public static function sendCachingHeaders($lastModified, $cacheLifetime = 30 * 24 * 3600) { + //Support the If-Modified-Since header. + $omitResponseBody = false; + if ( + !empty($lastModified) + && !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) + && is_string($_SERVER['HTTP_IF_MODIFIED_SINCE']) + ) { + //strtotime() should be able to handle invalid strings safely. + //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $threshold = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']); + if ( $threshold >= $lastModified ) { + header('HTTP/1.1 304 Not Modified'); + $omitResponseBody = true; + } + } + + //Enable browser caching. + //Note that admin-ajax.php always adds HTTP headers that prevent caching, so we will + //override all of them even though we don't actually need some of them, like "Expires". + if ( !empty($lastModified) ) { + header('Last-Modified: ' . gmdate('D, d M Y H:i:s ', $lastModified) . 'GMT'); + } + $expires = !empty($lastModified) ? ($lastModified + $cacheLifetime) : (time() + $cacheLifetime); + header('Expires: ' . gmdate('D, d M Y H:i:s ', $expires) . 'GMT'); + header('Cache-Control: public, max-age=' . $cacheLifetime); + + return $omitResponseBody; + } } /** diff --git a/includes/basic-dependencies.php b/includes/basic-dependencies.php index 122732f..2a9e678 100644 --- a/includes/basic-dependencies.php +++ b/includes/basic-dependencies.php @@ -2,15 +2,36 @@ if ( !defined('AME_ROOT_DIR') ) { define('AME_ROOT_DIR', dirname(dirname(__FILE__))); } +if ( !defined('WS_AME_USE_BUNDLES') ) { + /** + * If set to true, the plugin will use Webpack bundles when available. If set + * to false, it will load individual JS files instead. + */ + define('WS_AME_USE_BUNDLES', !(defined('WP_DEBUG') && WP_DEBUG)); +} $thisDirectory = dirname(__FILE__); require_once $thisDirectory . '/shadow_plugin_framework.php'; require_once $thisDirectory . '/role-utils.php'; require_once $thisDirectory . '/ame-utils.php'; +require_once $thisDirectory . '/ame-option.php'; require_once $thisDirectory . '/menu-item.php'; require_once $thisDirectory . '/menu.php'; require_once $thisDirectory . '/auto-versioning.php'; require_once $thisDirectory . '/../ajax-wrapper/AjaxWrapper.php'; +require_once $thisDirectory . '/AmeAutoloader.php'; + +//Customizable library. +$wsAmeFreeAutoloader = new YahnisElsts\AdminMenuEditor\AmeAutoloader([ + 'YahnisElsts\\AdminMenuEditor\\Customizable\\' => AME_ROOT_DIR . '/customizables', +]); +$wsAmeFreeAutoloader->register(); +require_once $thisDirectory . '/../customizables/constants.php'; + +if ( file_exists($thisDirectory . '/../extras/pro-autoloader.php') ) { + require_once $thisDirectory . '/../extras/pro-autoloader.php'; +} + require_once $thisDirectory . '/module.php'; require_once $thisDirectory . '/persistent-module.php'; require_once $thisDirectory . '/shortcodes.php'; diff --git a/includes/editor-page.php b/includes/editor-page.php index fff777b..02d9709 100644 --- a/includes/editor-page.php +++ b/includes/editor-page.php @@ -183,7 +183,6 @@ function ame_output_toolbar_row($buttons, $icons, $classes = array()) { include dirname(__FILE__) . '/../modules/access-editor/access-editor-template.php'; $extrasDirectory = dirname(__FILE__) . '/../extras'; if ( $is_pro_version ) { - include $extrasDirectory . '/menu-color-dialog.php'; include $extrasDirectory . '/copy-permissions-dialog.php'; } diff --git a/includes/menu-editor-core.php b/includes/menu-editor-core.php index ba2ae53..68eda70 100644 --- a/includes/menu-editor-core.php +++ b/includes/menu-editor-core.php @@ -107,6 +107,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { */ private $loaded_modules = array(); private $are_modules_loaded = false; + private $is_loading_modules = false; /** * @var array List of capabilities that are used in the default admin menu. Used to detect meta capabilities. @@ -342,6 +343,13 @@ function init(){ 'admin.php?page=wpgb-facet-settings' => true, //Google Analytics for WordPress by MonsterInsights 8.4.0 'index.php?page=monsterinsights-getting-started' => true, + //WPForms Lite 1.7.8 (and possibly the paid version) + 'index.php?page=wpforms-getting-started' => true, + //WPFunnels 2.7.6 + 'admin.php?page=edit_funnel' => true, + 'admin.php?page=email-builder' => true, + //Email Marketing Automation - Mail Mint 1.2.5 + 'admin.php?page=mint-mail-automation-editor' => true, ); //AJAXify screen options @@ -472,13 +480,19 @@ function init_finish() { } public function load_modules() { + //Prevent indirect recursion. This can happen if, for example, a module + //immediately tries to check user capabilities when it's loaded. + if ( $this->is_loading_modules ) { + return; + } + $this->is_loading_modules = true; + //Load any active modules that haven't been loaded yet. foreach($this->get_active_modules() as $id => $module) { if ( array_key_exists($id, $this->loaded_modules) ) { continue; } - /** @noinspection PhpIncludeInspection */ include ($module['path']); if ( !empty($module['className']) ) { $instance = new $module['className']($this); @@ -498,6 +512,8 @@ public function load_modules() { $this->tabs = apply_filters('admin_menu_editor-tabs', $firstTabs); //The "Settings" tab is always last. $this->tabs['settings'] = 'Settings'; + + $this->is_loading_modules = false; } /** @@ -882,6 +898,36 @@ private function filter_global_menu() { // phpcs:enable } + public function register_safe_js_libraries() { + static $isDone = false; + if ( $isDone ) { + return; + } + $isDone = true; + + //Lodash library + wp_register_auto_versioned_script('ame-lodash', plugins_url('js/lodash.min.js', $this->plugin_file)); + + //Knockout + foreach(array('ame-knockout', 'knockout') as $koAlias) { + wp_register_auto_versioned_script($koAlias, plugins_url('js/knockout.js', $this->plugin_file)); + } + + //Knockout bindings for the jQuery UI sortable functionality. + wp_register_auto_versioned_script( + 'ame-knockout-sortable', + plugins_url('js/knockout-sortable.js', $this->plugin_file), + ['ame-knockout', 'jquery', 'jquery-ui-sortable', 'jquery-ui-draggable', 'jquery-ui-droppable'] + ); + + //Mini utilities for more functional programming. + wp_register_auto_versioned_script('ame-mini-functional-lib', plugins_url('js/mini-func.js', $this->plugin_file)); + + if ( function_exists('ws_ame_register_customizable_js_lib') ) { + ws_ame_register_customizable_js_lib(); + } + } + public function register_base_dependencies() { static $done = false; if ( $done ) { @@ -890,16 +936,11 @@ public function register_base_dependencies() { $done = true; $this->register_jquery_plugins(); + $this->register_safe_js_libraries(); //Base styles. wp_register_auto_versioned_style('menu-editor-base-style', plugins_url('css/menu-editor.css', $this->plugin_file)); - //Lodash library - wp_register_auto_versioned_script('ame-lodash', plugins_url('js/lodash.min.js', $this->plugin_file)); - - //Knockout - wp_register_auto_versioned_script('knockout', plugins_url('js/knockout.js', $this->plugin_file)); - //Actor manager. wp_register_auto_versioned_script( 'ame-actor-manager', @@ -1217,6 +1258,10 @@ function enqueue_scripts() { 'expandSelectedSubmenu' => isset($this->get['expand_submenu']) && ($this->get['expand_submenu'] === '1'), 'deepNestingEnabled' => $this->options['deep_nesting_enabled'], + 'auxDataConfig' => apply_filters( + 'admin_menu_editor-aux_data_config', + array('keys' => array('color_presets' => null), 'settingIdMap' => array(), 'prefixMap' => array()) + ), 'isDemoMode' => defined('IS_DEMO_MODE'), 'isMasterMode' => defined('IS_MASTER_MODE'), @@ -1387,7 +1432,7 @@ function set_custom_menu($custom_menu, $config_id = null) { $config_id = $this->guess_menu_config_id(); } - $custom_menu = apply_filters('ame_pre_set_custom_menu', $custom_menu); + $custom_menu = apply_filters('ame_pre_set_custom_menu', $custom_menu, $config_id); $previous_custom_menu = $this->load_custom_menu($config_id); if ( !empty($this->options['wpml_support_enabled']) ) { @@ -2576,6 +2621,9 @@ function page_menu_editor(){ } $action = isset($this->post['action']) ? $this->post['action'] : (isset($this->get['action']) ? $this->get['action'] : ''); + if ( !empty($action) ) { + do_action('admin_menu_editor-page_action-' . $action, $this->post); + } do_action('admin_menu_editor-header', $action, $this->post); if ( !empty($action) ) { @@ -2984,10 +3032,6 @@ private function display_editor_ui() { $custom_menu = $default_menu; } - //The editor doesn't use the color CSS. Including it would just make the page bigger and waste bandwidth. - unset($custom_menu['color_css']); - unset($custom_menu['color_css_modified']); - //Encode both menus as JSON $editor_data['default_menu_js'] = ameMenu::to_json($default_menu); $editor_data['custom_menu_js'] = ameMenu::to_json($custom_menu); @@ -4046,6 +4090,15 @@ public function set_many_plugin_options($options) { $this->save_options(); } + /** + * Get the default configuration options. + * + * @return array + */ + public function get_default_options() { + return $this->defaults; + } + /** * Log a security-related message. * @@ -4593,11 +4646,22 @@ public function on_user_metadata_changed($unused_meta_id, $user_id, $meta_key) { * @return WP_User|null */ private function get_user_by_id($user_id) { + static $isGettingCurrentUser = false; + //Usually, pluggable functions will already be loaded by this point, //but there is at least one plugin that indirectly triggers this method //before wp_get_current_user() is available by checking user caps early. - if ( function_exists('wp_get_current_user') ) { - $current_user = wp_get_current_user(); + // + //At least one plugin can enter infinite recursion if we call wp_get_current_user() + //here. To prevent that, avoid nested calls and fall back to get_user_by(). + if ( function_exists('wp_get_current_user') && !$isGettingCurrentUser ) { + $isGettingCurrentUser = true; + try { + $current_user = wp_get_current_user(); + } finally { + $isGettingCurrentUser = false; + } + if ( $current_user && ($current_user->ID == $user_id) ) { return $current_user; } diff --git a/includes/menu-item.php b/includes/menu-item.php index 103700f..9421ff3 100644 --- a/includes/menu-item.php +++ b/includes/menu-item.php @@ -40,7 +40,7 @@ abstract class ameMenuItem { * Convert a WP menu structure to an associative array. * * @param array $item An menu item. - * @param int|string $position The position (index) of the the menu item. + * @param int|string $position The position (index) of the menu item. * @param string|null $parent The slug of the parent menu that owns this item. Null for top level menus. * @return array */ @@ -49,12 +49,12 @@ public static function fromWpItem($item, $position = 0, $parent = null) { $default_css_class = ($parent === null) ? 'menu-top' : ''; $item = array( 'menu_title' => strval($item[0]), - 'access_level' => strval($item[1]), //= required capability - 'file' => $item[2], - 'page_title' => (isset($item[3]) ? strval($item[3]) : ''), - 'css_class' => (isset($item[4]) ? strval($item[4]) : $default_css_class), - 'hookname' => (isset($item[5]) ? strval($item[5]) : ''), //Used as the ID attr. of the generated HTML tag. - 'icon_url' => (isset($item[6]) ? strval($item[6]) : 'dashicons-admin-generic'), + 'access_level' => (isset($item[1]) ? self::sanitize_string_prop($item[1]) : ''), //= required capability + 'file' => (array_key_exists(2, $item) ? $item[2] : ''), + 'page_title' => (isset($item[3]) ? self::sanitize_string_prop($item[3]) : ''), + 'css_class' => (isset($item[4]) ? self::sanitize_string_prop($item[4]) : $default_css_class), + 'hookname' => (isset($item[5]) ? self::sanitize_string_prop($item[5]) : ''), //Used as the ID attr. of the generated HTML tag. + 'icon_url' => (isset($item[6]) ? self::sanitize_string_prop($item[6]) : 'dashicons-admin-generic'), 'position' => $position, 'parent' => $parent, ); @@ -88,6 +88,34 @@ public static function fromWpItem($item, $position = 0, $parent = null) { return array_merge(self::basic_defaults(), $item); } + /** + * Sanitize a menu property value that is supposed to be a string. + * + * There are certain admin menu properties that are supposed to be strings, but + * in practice some plugins and themes set them to arrays, objects, true/false, + * etc. This function will convert those values to strings, potentially losing + * information in the process. + * + * strval() is not used because it will trigger a PHP warning if the value is + * an array or an object that doesn't have a `__toString()` method. + * + * + * @param mixed $value + * @return string + */ + protected static function sanitize_string_prop($value) { + if ( is_scalar($value) ) { + return strval($value); + } else if ( is_array($value) ) { + if ( empty($value) ) { + return ''; + } + return 'array[' . count($value) . ']'; + } else { + return ''; + } + } + public static function basic_defaults() { static $basic_defaults = null; if ( $basic_defaults !== null ) { @@ -685,8 +713,10 @@ public static function remove_update_count($menuTitle) { if ( $result->length > 0 ) { //Remove all matched nodes. We must iterate backwards to prevent messing up the DOMNodeList. for ($i = $result->length - 1; $i >= 0; $i--) { - $span = $result->item(0); - $span->parentNode->removeChild($span); + $span = $result->item($i); + if ( $span->parentNode ) { + $span->parentNode->removeChild($span); + } } $innerHtml = ''; diff --git a/includes/menu.php b/includes/menu.php index d465624..a66a1f0 100644 --- a/includes/menu.php +++ b/includes/menu.php @@ -86,10 +86,11 @@ public static function load_array($arr, $assume_correct_format = false, $always_ $menu['format']['is_normalized'] = true; } - if ( isset($arr['color_css']) && is_string($arr['color_css']) ) { - $menu['color_css'] = $arr['color_css']; - $menu['color_css_modified'] = isset($arr['color_css_modified']) ? intval($arr['color_css_modified']) : 0; - $menu['icon_color_overrides'] = isset($arr['icon_color_overrides']) ? $arr['icon_color_overrides'] : null; + if ( isset($arr['color_css_modified']) ) { + $menu['color_css_modified'] = intval($arr['color_css_modified']); + } + if ( isset($arr['icon_color_overrides']) ) { + $menu['icon_color_overrides'] = $arr['icon_color_overrides']; } //Sanitize color presets. @@ -407,11 +408,13 @@ public static function compress($menu) { 'custom_item_defaults' => ameMenuItem::custom_item_defaults(), ); - $menu['tree'] = self::map_items( - $menu['tree'], - array(__CLASS__, 'compress_item'), - array($common) - ); + if ( !empty($menu['tree']) ) { + $menu['tree'] = self::map_items( + $menu['tree'], + array(__CLASS__, 'compress_item'), + array($common) + ); + } $menu = self::add_format_header($menu); $menu['format']['compressed'] = true; diff --git a/includes/module.php b/includes/module.php index a8197e5..e076ed6 100644 --- a/includes/module.php +++ b/includes/module.php @@ -1,4 +1,5 @@ moduleDir = dirname($reflector->getFileName()); $this->moduleId = basename($this->moduleDir); @@ -41,7 +41,10 @@ public function __construct($menuEditor) { //Optionally, handle settings form submission. if ( $this->settingsFormAction !== '' ) { - add_action('admin_menu_editor-header', array($this, '_processAction'), 10, 2); + add_action( + 'admin_menu_editor-page_action-' . $this->settingsFormAction, + array($this, '_processAction') + ); } } } @@ -90,7 +93,6 @@ protected function outputMainTemplate() { protected function outputTemplate($name) { $templateFile = $this->moduleDir . '/' . $name . '-template.php'; if ( file_exists($templateFile) ) { - /** @noinspection PhpUnusedLocalVariableInspection Used in some templates. */ $moduleTabUrl = $this->getTabUrl(); $templateVariables = $this->getTemplateVariables($name); @@ -98,14 +100,13 @@ protected function outputTemplate($name) { extract($templateVariables, EXTR_SKIP); } - /** @noinspection PhpIncludeInspection */ require $templateFile; return true; } return false; } - protected function getTemplateVariables(/** @noinspection PhpUnusedParameterInspection */ $templateName) { + protected function getTemplateVariables($templateName) { //Override this method to pass variables to a template. return array(); } @@ -124,14 +125,11 @@ public function enqueueTabStyles() { /** * @access private - * @param string $action * @param array $post */ - public function _processAction($action, $post = array()) { - if ( $action === $this->settingsFormAction ) { - check_admin_referer($action); - $this->handleSettingsForm($post); - } + public function _processAction($post = array()) { + check_admin_referer($this->settingsFormAction); + $this->handleSettingsForm($post); } public function handleSettingsForm($post = array()) { diff --git a/includes/persistent-module.php b/includes/persistent-module.php index 1c1463d..c2a49a4 100644 --- a/includes/persistent-module.php +++ b/includes/persistent-module.php @@ -32,6 +32,9 @@ public function loadSettings() { $json = $this->getScopedOption($this->optionName, null); if ( is_string($json) && !empty($json) ) { $settings = json_decode($json, true); + if ( !is_array($settings) ) { + $settings = array(); //JSON decoding failed, fall back to an empty array. + } } else { $settings = array(); } diff --git a/js/actor-manager.js b/js/actor-manager.js index 9fea149..1d65f29 100644 --- a/js/actor-manager.js +++ b/js/actor-manager.js @@ -1,24 +1,11 @@ +"use strict"; /// /// /// -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var AmeBaseActor = /** @class */ (function () { - function AmeBaseActor(id, displayName, capabilities, metaCapabilities) { - if (metaCapabilities === void 0) { metaCapabilities = {}; } +// noinspection ES6ConvertVarToLetConst -- Intentionally global variable +var AmeActors; +class AmeBaseActor { + constructor(id, displayName, capabilities, metaCapabilities = {}) { this.displayName = '[Error: No displayName set]'; this.groupActors = []; this.id = id; @@ -35,7 +22,7 @@ var AmeBaseActor = /** @class */ (function () { * @param {string} capability * @returns {boolean|null} */ - AmeBaseActor.prototype.hasOwnCap = function (capability) { + hasOwnCap(capability) { if (this.capabilities.hasOwnProperty(capability)) { return this.capabilities[capability]; } @@ -43,9 +30,9 @@ var AmeBaseActor = /** @class */ (function () { return this.metaCapabilities[capability]; } return null; - }; - AmeBaseActor.getActorSpecificity = function (actorId) { - var actorType = actorId.substring(0, actorId.indexOf(':')), specificity; + } + static getActorSpecificity(actorId) { + let actorType = actorId.substring(0, actorId.indexOf(':')), specificity; switch (actorType) { case 'role': specificity = 1; @@ -60,84 +47,78 @@ var AmeBaseActor = /** @class */ (function () { specificity = 0; } return specificity; - }; - AmeBaseActor.prototype.toString = function () { + } + toString() { return this.displayName + ' [' + this.id + ']'; - }; - AmeBaseActor.prototype.getId = function () { + } + getId() { return this.id; - }; - AmeBaseActor.prototype.getDisplayName = function () { + } + getDisplayName() { return this.displayName; - }; - return AmeBaseActor; -}()); -var AmeRole = /** @class */ (function (_super) { - __extends(AmeRole, _super); - function AmeRole(roleId, displayName, capabilities, metaCapabilities) { - if (metaCapabilities === void 0) { metaCapabilities = {}; } - var _this = _super.call(this, 'role:' + roleId, displayName, capabilities, metaCapabilities) || this; - _this.name = roleId; - return _this; - } - AmeRole.prototype.hasOwnCap = function (capability) { + } + isUser() { + return false; + } +} +class AmeRole extends AmeBaseActor { + constructor(roleId, displayName, capabilities, metaCapabilities = {}) { + super('role:' + roleId, displayName, capabilities, metaCapabilities); + this.name = roleId; + } + hasOwnCap(capability) { //In WordPress, a role name is also a capability name. Users that have the role "foo" always //have the "foo" capability. It's debatable whether the role itself actually has that capability //(WP_Role says no), but it's convenient to treat it that way. if (capability === this.name) { return true; } - return _super.prototype.hasOwnCap.call(this, capability); - }; - return AmeRole; -}(AmeBaseActor)); -var AmeUser = /** @class */ (function (_super) { - __extends(AmeUser, _super); - function AmeUser(userLogin, displayName, capabilities, roles, isSuperAdmin, userId, metaCapabilities) { - if (isSuperAdmin === void 0) { isSuperAdmin = false; } - if (metaCapabilities === void 0) { metaCapabilities = {}; } - var _this = _super.call(this, 'user:' + userLogin, displayName, capabilities, metaCapabilities) || this; - _this.userId = 0; - _this.isSuperAdmin = false; - _this.avatarHTML = ''; - _this.userLogin = userLogin; - _this.roles = roles; - _this.isSuperAdmin = isSuperAdmin; - _this.userId = userId || 0; - if (_this.isSuperAdmin) { - _this.groupActors.push(AmeSuperAdmin.permanentActorId); - } - for (var i = 0; i < _this.roles.length; i++) { - _this.groupActors.push('role:' + _this.roles[i]); - } - return _this; - } - AmeUser.createFromProperties = function (properties) { - var user = new AmeUser(properties.user_login, properties.display_name, properties.capabilities, properties.roles, properties.is_super_admin, properties.hasOwnProperty('id') ? properties.id : null, properties.meta_capabilities); + return super.hasOwnCap(capability); + } +} +class AmeUser extends AmeBaseActor { + constructor(userLogin, displayName, capabilities, roles, isSuperAdmin = false, userId, metaCapabilities = {}) { + super('user:' + userLogin, displayName, capabilities, metaCapabilities); + this.userId = 0; + this.isSuperAdmin = false; + this.avatarHTML = ''; + this.userLogin = userLogin; + this.roles = roles; + this.isSuperAdmin = isSuperAdmin; + this.userId = userId || 0; + if (this.isSuperAdmin) { + this.groupActors.push(AmeSuperAdmin.permanentActorId); + } + for (let i = 0; i < this.roles.length; i++) { + this.groupActors.push('role:' + this.roles[i]); + } + } + static createFromProperties(properties) { + let user = new AmeUser(properties.user_login, properties.display_name, properties.capabilities, properties.roles, properties.is_super_admin, properties.id, properties.meta_capabilities); if (properties.avatar_html) { user.avatarHTML = properties.avatar_html; } return user; - }; - return AmeUser; -}(AmeBaseActor)); -var AmeSuperAdmin = /** @class */ (function (_super) { - __extends(AmeSuperAdmin, _super); - function AmeSuperAdmin() { - return _super.call(this, AmeSuperAdmin.permanentActorId, 'Super Admin', {}) || this; - } - AmeSuperAdmin.prototype.hasOwnCap = function (capability) { + } + isUser() { + return true; + } + getRoleIds() { + return this.roles; + } +} +class AmeSuperAdmin extends AmeBaseActor { + constructor() { + super(AmeSuperAdmin.permanentActorId, 'Super Admin', {}); + } + hasOwnCap(capability) { //The Super Admin has all possible capabilities except the special "do_not_allow" flag. return (capability !== 'do_not_allow'); - }; - AmeSuperAdmin.permanentActorId = 'special:super_admin'; - return AmeSuperAdmin; -}(AmeBaseActor)); -var AmeActorManager = /** @class */ (function () { - function AmeActorManager(roles, users, isMultisite, suspectedMetaCaps) { - if (isMultisite === void 0) { isMultisite = false; } - if (suspectedMetaCaps === void 0) { suspectedMetaCaps = {}; } - var _this = this; + } +} +AmeSuperAdmin.permanentActorId = 'special:super_admin'; +class AmeActorManager { + constructor(roles, users, isMultisite = false, suspectedMetaCaps = {}) { this.roles = {}; this.users = {}; this.grantedCapabilities = {}; @@ -146,35 +127,37 @@ var AmeActorManager = /** @class */ (function () { this.tagMetaCaps = {}; this.suggestedCapabilities = []; this.isMultisite = !!isMultisite; - AmeActorManager._.forEach(roles, function (roleDetails, id) { - var role = new AmeRole(id, roleDetails.name, roleDetails.capabilities, AmeActorManager._.get(roleDetails, 'meta_capabilities', {})); - _this.roles[role.name] = role; + AmeActorManager._.forEach(roles, (roleDetails, id) => { + if (typeof id === 'undefined') { + return; + } + const role = new AmeRole(id, roleDetails.name, roleDetails.capabilities, AmeActorManager._.get(roleDetails, 'meta_capabilities', {})); + this.roles[role.name] = role; }); - AmeActorManager._.forEach(users, function (userDetails) { - var user = AmeUser.createFromProperties(userDetails); - _this.users[user.userLogin] = user; + AmeActorManager._.forEach(users, (userDetails) => { + const user = AmeUser.createFromProperties(userDetails); + this.users[user.userLogin] = user; }); this.superAdmin = new AmeSuperAdmin(); this.suspectedMetaCaps = suspectedMetaCaps; - var exclusiveCaps = [ + const exclusiveCaps = [ 'update_core', 'update_plugins', 'delete_plugins', 'install_plugins', 'upload_plugins', 'update_themes', 'delete_themes', 'install_themes', 'upload_themes', 'update_core', 'edit_css', 'unfiltered_html', 'edit_files', 'edit_plugins', 'edit_themes', 'delete_user', 'delete_users' ]; - for (var i = 0; i < exclusiveCaps.length; i++) { + for (let i = 0; i < exclusiveCaps.length; i++) { this.exclusiveSuperAdminCapabilities[exclusiveCaps[i]] = true; } - var tagMetaCaps = [ + const tagMetaCaps = [ 'manage_post_tags', 'edit_categories', 'edit_post_tags', 'delete_categories', 'delete_post_tags' ]; - for (var i = 0; i < tagMetaCaps.length; i++) { + for (let i = 0; i < tagMetaCaps.length; i++) { this.tagMetaCaps[tagMetaCaps[i]] = true; } } // noinspection JSUnusedGlobalSymbols - AmeActorManager.prototype.actorCanAccess = function (actorId, grantAccess, defaultCapability) { - if (defaultCapability === void 0) { defaultCapability = null; } + actorCanAccess(actorId, grantAccess, defaultCapability = null) { if (grantAccess.hasOwnProperty(actorId)) { return grantAccess[actorId]; } @@ -182,12 +165,12 @@ var AmeActorManager = /** @class */ (function () { return this.hasCap(actorId, defaultCapability, grantAccess); } return true; - }; - AmeActorManager.prototype.getActor = function (actorId) { + } + getActor(actorId) { if (actorId === AmeSuperAdmin.permanentActorId) { return this.superAdmin; } - var separator = actorId.indexOf(':'), actorType = actorId.substring(0, separator), actorKey = actorId.substring(separator + 1); + const separator = actorId.indexOf(':'), actorType = actorId.substring(0, separator), actorKey = actorId.substring(separator + 1); if (actorType === 'role') { return this.roles.hasOwnProperty(actorKey) ? this.roles[actorKey] : null; } @@ -199,41 +182,45 @@ var AmeActorManager = /** @class */ (function () { message: "There is no actor with that ID, or the ID is invalid.", value: actorId }; - }; - AmeActorManager.prototype.actorExists = function (actorId) { + } + actorExists(actorId) { try { return (this.getActor(actorId) !== null); } catch (exception) { - if (exception.hasOwnProperty('name') && (exception.name === 'InvalidActorException')) { + const exceptionAsAny = exception; + if ((typeof exceptionAsAny === 'object') + && (exceptionAsAny !== null) + && (typeof exceptionAsAny.name === 'string') + && (exceptionAsAny.name === 'InvalidActorException')) { return false; } else { throw exception; } } - }; - AmeActorManager.prototype.hasCap = function (actorId, capability, context) { + } + hasCap(actorId, capability, context) { context = context || {}; return this.actorHasCap(actorId, capability, [context, this.grantedCapabilities]); - }; - AmeActorManager.prototype.hasCapByDefault = function (actorId, capability) { + } + hasCapByDefault(actorId, capability) { return this.actorHasCap(actorId, capability); - }; - AmeActorManager.prototype.actorHasCap = function (actorId, capability, contextList) { + } + actorHasCap(actorId, capability, contextList) { //It's like the chain-of-responsibility pattern. - //Everybody has the "exist" cap and it can't be removed or overridden by plugins. + //Everybody has the "exist" cap, and it can't be removed or overridden by plugins. if (capability === 'exist') { return true; } capability = this.mapMetaCap(capability); - var result = null; + let result = null; //Step #1: Check temporary context - unsaved caps, etc. Optional. //Step #2: Check granted capabilities. Default on, but can be skipped. if (contextList) { //Check for explicit settings first. - var actorValue = void 0, len = contextList.length; - for (var i = 0; i < len; i++) { + let actorValue, len = contextList.length; + for (let i = 0; i < len; i++) { if (contextList[i].hasOwnProperty(actorId)) { actorValue = contextList[i][actorId]; if (typeof actorValue === 'boolean') { @@ -250,11 +237,11 @@ var AmeActorManager = /** @class */ (function () { } } //Step #3: Check owned/default capabilities. Always checked. - var actor = this.getActor(actorId); + let actor = this.getActor(actorId); if (actor === null) { return false; } - var hasOwnCap = actor.hasOwnCap(capability); + let hasOwnCap = actor.hasOwnCap(capability); if (hasOwnCap !== null) { return hasOwnCap; } @@ -267,8 +254,8 @@ var AmeActorManager = /** @class */ (function () { } //Check if any of the user's roles have the capability. result = null; - for (var index = 0; index < actor.roles.length; index++) { - var roleHasCap = this.actorHasCap('role:' + actor.roles[index], capability, contextList); + for (let index = 0; index < actor.roles.length; index++) { + let roleHasCap = this.actorHasCap('role:' + actor.roles[index], capability, contextList); if (roleHasCap !== null) { result = result || roleHasCap; } @@ -281,8 +268,8 @@ var AmeActorManager = /** @class */ (function () { return null; } return false; - }; - AmeActorManager.prototype.mapMetaCap = function (capability) { + } + mapMetaCap(capability) { if (capability === 'customize') { return 'edit_theme_options'; } @@ -300,98 +287,96 @@ var AmeActorManager = /** @class */ (function () { return 'edit_posts'; } return capability; - }; + } /* ------------------------------- * Roles * ------------------------------- */ - AmeActorManager.prototype.getRoles = function () { + getRoles() { return this.roles; - }; - AmeActorManager.prototype.roleExists = function (roleId) { + } + roleExists(roleId) { return this.roles.hasOwnProperty(roleId); - }; + } ; - AmeActorManager.prototype.getSuperAdmin = function () { + getSuperAdmin() { return this.superAdmin; - }; + } /* ------------------------------- * Users * ------------------------------- */ - AmeActorManager.prototype.getUsers = function () { + getUsers() { return this.users; - }; - AmeActorManager.prototype.getUser = function (login) { + } + getUser(login) { return this.users.hasOwnProperty(login) ? this.users[login] : null; - }; - AmeActorManager.prototype.addUsers = function (newUsers) { - var _this = this; - AmeActorManager._.forEach(newUsers, function (user) { - _this.users[user.userLogin] = user; + } + addUsers(newUsers) { + AmeActorManager._.forEach(newUsers, (user) => { + this.users[user.userLogin] = user; }); - }; - AmeActorManager.prototype.getGroupActorsFor = function (userLogin) { + } + getGroupActorsFor(userLogin) { return this.users[userLogin].groupActors; - }; + } /* ------------------------------- * Granted capability manipulation * ------------------------------- */ - AmeActorManager.prototype.setGrantedCapabilities = function (newGrants) { + setGrantedCapabilities(newGrants) { this.grantedCapabilities = AmeActorManager._.cloneDeep(newGrants); - }; - AmeActorManager.prototype.getGrantedCapabilities = function () { + } + getGrantedCapabilities() { return this.grantedCapabilities; - }; + } /** * Grant or deny a capability to an actor. */ - AmeActorManager.prototype.setCap = function (actor, capability, hasCap, sourceType, sourceName) { + setCap(actor, capability, hasCap, sourceType, sourceName) { this.setCapInContext(this.grantedCapabilities, actor, capability, hasCap, sourceType, sourceName); - }; - AmeActorManager.prototype.setCapInContext = function (context, actor, capability, hasCap, sourceType, sourceName) { + } + setCapInContext(context, actor, capability, hasCap, sourceType, sourceName) { capability = this.mapMetaCap(capability); - var grant = sourceType ? [hasCap, sourceType, sourceName || null] : hasCap; + const grant = sourceType ? [hasCap, sourceType, sourceName || null] : hasCap; AmeActorManager._.set(context, [actor, capability], grant); - }; - AmeActorManager.prototype.resetCapInContext = function (context, actor, capability) { + } + resetCapInContext(context, actor, capability) { capability = this.mapMetaCap(capability); if (AmeActorManager._.has(context, [actor, capability])) { delete context[actor][capability]; } - }; + } /** * Reset all capabilities granted to an actor. * @param actor * @return boolean TRUE if anything was reset or FALSE if the actor didn't have any granted capabilities. */ - AmeActorManager.prototype.resetActorCaps = function (actor) { + resetActorCaps(actor) { if (AmeActorManager._.has(this.grantedCapabilities, actor)) { delete this.grantedCapabilities[actor]; return true; } return false; - }; + } /** * Remove redundant granted capabilities. * * For example, if user "jane" has been granted the "edit_posts" capability both directly and via the Editor role, * the direct grant is redundant. We can remove it. Jane will still have "edit_posts" because she's an editor. */ - AmeActorManager.prototype.pruneGrantedUserCapabilities = function () { - var _this = this; - var _ = AmeActorManager._, pruned = _.cloneDeep(this.grantedCapabilities), context = [pruned]; - var actorKeys = _(pruned).keys().filter(function (actorId) { + pruneGrantedUserCapabilities() { + let _ = AmeActorManager._, pruned = _.cloneDeep(this.grantedCapabilities), context = [pruned]; + let actorKeys = _(pruned).keys().filter((actorId) => { //Skip users that are not loaded. - var actor = _this.getActor(actorId); + const actor = this.getActor(actorId); if (actor === null) { return false; } return (actor instanceof AmeUser); }).value(); - _.forEach(actorKeys, function (actor) { - _.forEach(_.keys(pruned[actor]), function (capability) { - var grant = pruned[actor][capability]; + _.forEach(actorKeys, (actor) => { + _.forEach(_.keys(pruned[actor]), (capability) => { + const grant = pruned[actor][capability]; delete pruned[actor][capability]; - var hasCap = _.isArray(grant) ? grant[0] : grant, hasCapWhenPruned = !!_this.actorHasCap(actor, capability, context); + const hasCap = _.isArray(grant) ? grant[0] : grant, hasCapWhenPruned = !!this.actorHasCap(actor, capability, context); if (hasCap !== hasCapWhenPruned) { pruned[actor][capability] = grant; //Restore. } @@ -399,7 +384,7 @@ var AmeActorManager = /** @class */ (function () { }); this.setGrantedCapabilities(pruned); return pruned; - }; + } ; /** * Compare the specificity of two actors. @@ -409,18 +394,18 @@ var AmeActorManager = /** @class */ (function () { * * @return {Number} */ - AmeActorManager.compareActorSpecificity = function (actor1, actor2) { - var delta = AmeBaseActor.getActorSpecificity(actor1) - AmeBaseActor.getActorSpecificity(actor2); + static compareActorSpecificity(actor1, actor2) { + let delta = AmeBaseActor.getActorSpecificity(actor1) - AmeBaseActor.getActorSpecificity(actor2); if (delta !== 0) { delta = (delta > 0) ? 1 : -1; } return delta; - }; + } ; - AmeActorManager.prototype.generateCapabilitySuggestions = function (capPower) { - var _ = AmeActorManager._; - var capsByPower = _.memoize(function (role) { - var sortedCaps = _.reduce(role.capabilities, function (result, hasCap, capability) { + generateCapabilitySuggestions(capPower) { + let _ = AmeActorManager._; + let capsByPower = _.memoize((role) => { + let sortedCaps = _.reduce(role.capabilities, (result, hasCap, capability) => { if (hasCap) { result.push({ capability: capability, @@ -429,21 +414,21 @@ var AmeActorManager = /** @class */ (function () { } return result; }, []); - sortedCaps = _.sortBy(sortedCaps, function (item) { return -item.power; }); + sortedCaps = _.sortBy(sortedCaps, (item) => -item.power); return sortedCaps; }); - var rolesByPower = _.values(this.getRoles()).sort(function (a, b) { - var aCaps = capsByPower(a), bCaps = capsByPower(b); + let rolesByPower = _.values(this.getRoles()).sort(function (a, b) { + let aCaps = capsByPower(a), bCaps = capsByPower(b); //Prioritise roles with the highest number of the most powerful capabilities. - var i = 0, limit = Math.min(aCaps.length, bCaps.length); + let i = 0, limit = Math.min(aCaps.length, bCaps.length); for (; i < limit; i++) { - var delta_1 = bCaps[i].power - aCaps[i].power; - if (delta_1 !== 0) { - return delta_1; + let delta = bCaps[i].power - aCaps[i].power; + if (delta !== 0) { + return delta; } } //Give a tie to the role that has more capabilities. - var delta = bCaps.length - aCaps.length; + let delta = bCaps.length - aCaps.length; if (delta !== 0) { return delta; } @@ -456,7 +441,7 @@ var AmeActorManager = /** @class */ (function () { } return 0; }); - var preferredCaps = [ + let preferredCaps = [ 'manage_network_options', 'install_plugins', 'edit_plugins', 'delete_users', 'manage_options', 'switch_themes', @@ -465,31 +450,31 @@ var AmeActorManager = /** @class */ (function () { 'publish_posts', 'edit_posts', 'read' ]; - var deprecatedCaps = _(_.range(0, 10)).map(function (level) { return 'level_' + level; }).value(); + let deprecatedCaps = _(_.range(0, 10)).map((level) => 'level_' + level).value(); deprecatedCaps.push('edit_files'); - var findDiscriminant = function (caps, includeRoles, excludeRoles) { - var getEnabledCaps = function (role) { + let findDiscriminant = (caps, includeRoles, excludeRoles) => { + let getEnabledCaps = (role) => { return _.keys(_.pick(role.capabilities, _.identity)); }; - //Find caps that all of the includeRoles have and excludeRoles don't. - var includeCaps = _.intersection.apply(_, _.map(includeRoles, getEnabledCaps)), excludeCaps = _.union.apply(_, _.map(excludeRoles, getEnabledCaps)), possibleCaps = _.without.apply(_, [includeCaps].concat(excludeCaps).concat(deprecatedCaps)); - var bestCaps = _.intersection(preferredCaps, possibleCaps); + //Find caps that all the includeRoles have and excludeRoles don't. + let includeCaps = _.intersection(..._.map(includeRoles, getEnabledCaps)), excludeCaps = _.union(..._.map(excludeRoles, getEnabledCaps)), possibleCaps = _.without(includeCaps, ...excludeCaps, ...deprecatedCaps); + let bestCaps = _.intersection(preferredCaps, possibleCaps); if (bestCaps.length > 0) { return bestCaps[0]; } else if (possibleCaps.length > 0) { return possibleCaps[0]; } - return null; + return ''; }; - var suggestedCapabilities = []; - for (var i = 0; i < rolesByPower.length; i++) { - var role = rolesByPower[i]; - var cap = findDiscriminant(preferredCaps, _.slice(rolesByPower, 0, i + 1), _.slice(rolesByPower, i + 1, rolesByPower.length)); + let suggestedCapabilities = []; + for (let i = 0; i < rolesByPower.length; i++) { + let role = rolesByPower[i]; + let cap = findDiscriminant(preferredCaps, _.slice(rolesByPower, 0, i + 1), _.slice(rolesByPower, i + 1, rolesByPower.length)); suggestedCapabilities.push({ role: role, capability: cap }); } - var previousSuggestion = null; - for (var i = suggestedCapabilities.length - 1; i >= 0; i--) { + let previousSuggestion = null; + for (let i = suggestedCapabilities.length - 1; i >= 0; i--) { if (suggestedCapabilities[i].capability === null) { suggestedCapabilities[i].capability = previousSuggestion ? previousSuggestion : 'exist'; @@ -499,28 +484,26 @@ var AmeActorManager = /** @class */ (function () { } } this.suggestedCapabilities = suggestedCapabilities; - }; - AmeActorManager.prototype.getSuggestedCapabilities = function () { + } + getSuggestedCapabilities() { return this.suggestedCapabilities; - }; - AmeActorManager.prototype.createUserFromProperties = function (properties) { + } + createUserFromProperties(properties) { return AmeUser.createFromProperties(properties); - }; - AmeActorManager._ = wsAmeLodash; - return AmeActorManager; -}()); -var AmeObservableActorSettings = /** @class */ (function () { - function AmeObservableActorSettings(initialData) { + } +} +AmeActorManager._ = wsAmeLodash; +class AmeObservableActorFeatureMap { + constructor(initialData) { this.items = {}; this.numberOfObservables = ko.observable(0); if (initialData) { this.setAll(initialData); } } - AmeObservableActorSettings.prototype.get = function (actor, defaultValue) { - if (defaultValue === void 0) { defaultValue = null; } + get(actor, defaultValue = null) { if (this.items.hasOwnProperty(actor)) { - var value = this.items[actor](); + const value = this.items[actor](); if (value === null) { return defaultValue; } @@ -528,8 +511,8 @@ var AmeObservableActorSettings = /** @class */ (function () { } this.numberOfObservables(); //Establish a dependency. return defaultValue; - }; - AmeObservableActorSettings.prototype.set = function (actor, value) { + } + set(actor, value) { if (!this.items.hasOwnProperty(actor)) { this.items[actor] = ko.observable(value); this.numberOfObservables(this.numberOfObservables() + 1); @@ -537,55 +520,55 @@ var AmeObservableActorSettings = /** @class */ (function () { else { this.items[actor](value); } - }; - AmeObservableActorSettings.prototype.getAll = function () { - var result = {}; - for (var actorId in this.items) { + } + getAll() { + let result = {}; + for (let actorId in this.items) { if (this.items.hasOwnProperty(actorId)) { - var value = this.items[actorId](); + const value = this.items[actorId](); if (value !== null) { result[actorId] = value; } } } return result; - }; - AmeObservableActorSettings.prototype.setAll = function (values) { - for (var actorId in values) { + } + setAll(values) { + for (let actorId in values) { if (values.hasOwnProperty(actorId)) { this.set(actorId, values[actorId]); } } - }; + } + reset(actorId) { + if (this.items.hasOwnProperty(actorId)) { + this.items[actorId](null); + } + } /** * Reset all values to null. */ - AmeObservableActorSettings.prototype.resetAll = function () { - for (var actorId in this.items) { + resetAll() { + for (let actorId in this.items) { if (this.items.hasOwnProperty(actorId)) { this.items[actorId](null); } } - }; - AmeObservableActorSettings.prototype.isEnabledFor = function (selectedActor, allActors, roleDefault, superAdminDefault, noValueDefault, outIsIndeterminate) { - if (allActors === void 0) { allActors = null; } - if (roleDefault === void 0) { roleDefault = false; } - if (superAdminDefault === void 0) { superAdminDefault = null; } - if (noValueDefault === void 0) { noValueDefault = false; } - if (outIsIndeterminate === void 0) { outIsIndeterminate = null; } - if ((selectedActor === null) && (allActors === null)) { - throw 'When the selected actor is NULL, you must provide ' + - 'a list of all visible actors to determine if the item is enabled for all/any of them'; - } + } + isEnabledFor(selectedActor, allActors = null, roleDefault = false, superAdminDefault = null, noValueDefault = false, outIsIndeterminate = null) { if (selectedActor === null) { + if (allActors === null) { + throw 'When the selected actor is NULL, you must provide ' + + 'a list of all visible actors to determine if the item is enabled for all/any of them'; + } //All: Enabled only if it's enabled for all actors. //Handle the theoretically impossible case where the actor list is empty. - var actorCount = allActors.length; + const actorCount = allActors.length; if (actorCount <= 0) { return noValueDefault; } - var isEnabledForSome = false, isDisabledForSome = false; - for (var index = 0; index < actorCount; index++) { + let isEnabledForSome = false, isDisabledForSome = false; + for (let index = 0; index < actorCount; index++) { if (this.isEnabledFor(allActors[index], allActors, roleDefault, superAdminDefault, noValueDefault)) { isEnabledForSome = true; } @@ -599,14 +582,14 @@ var AmeObservableActorSettings = /** @class */ (function () { return isEnabledForSome && (!isDisabledForSome); } //Is there an explicit setting for this actor? - var ownSetting = this.get(selectedActor.getId(), null); + let ownSetting = this.get(selectedActor.getId(), null); if (ownSetting !== null) { return ownSetting; } if (selectedActor instanceof AmeUser) { //The "Super Admin" setting takes precedence over regular roles. if (selectedActor.isSuperAdmin) { - var superAdminSetting = this.get(AmeSuperAdmin.permanentActorId, superAdminDefault); + let superAdminSetting = this.get(AmeSuperAdmin.permanentActorId, superAdminDefault); if (superAdminSetting !== null) { return superAdminSetting; } @@ -614,9 +597,9 @@ var AmeObservableActorSettings = /** @class */ (function () { //Use role settings. //Enabled for at least one role = enabled. //Disabled for at least one role and no settings for other roles = disabled. - var isEnabled = null; - for (var i = 0; i < selectedActor.roles.length; i++) { - var roleSetting = this.get('role:' + selectedActor.roles[i], roleDefault); + let isEnabled = null; + for (let i = 0; i < selectedActor.roles.length; i++) { + let roleSetting = this.get('role:' + selectedActor.roles[i], roleDefault); if (roleSetting !== null) { if (isEnabled === null) { isEnabled = roleSetting; @@ -633,15 +616,13 @@ var AmeObservableActorSettings = /** @class */ (function () { //a setting for this item. Fall through to the final default. } return noValueDefault; - }; - AmeObservableActorSettings.prototype.setEnabledFor = function (selectedActor, enabled, allActors, defaultValue) { - if (allActors === void 0) { allActors = null; } - if (defaultValue === void 0) { defaultValue = null; } - if ((selectedActor === null) && (allActors === null)) { - throw 'When the selected actor is NULL, you must provide ' + - 'a list of all visible actors so that the item can be enabled or disabled for all of them'; - } + } + setEnabledFor(selectedActor, enabled, allActors = null, defaultValue = null) { if (selectedActor === null) { + if (allActors === null) { + throw 'When the selected actor is NULL, you must provide ' + + 'a list of all visible actors so that the item can be enabled or disabled for all of them'; + } //Enable/disable the item for all actors. if (enabled === defaultValue) { //Since the new value is the same as the default, @@ -649,7 +630,7 @@ var AmeObservableActorSettings = /** @class */ (function () { this.resetAll(); } else { - for (var i = 0; i < allActors.length; i++) { + for (let i = 0; i < allActors.length; i++) { this.set(allActors[i].getId(), enabled); } } @@ -657,9 +638,189 @@ var AmeObservableActorSettings = /** @class */ (function () { else { this.set(selectedActor.getId(), enabled); } - }; - return AmeObservableActorSettings; -}()); + } +} +var AmeRoleCombinationMode; +(function (AmeRoleCombinationMode) { + /** + * Enabled if enabled for every role the user has. + */ + AmeRoleCombinationMode[AmeRoleCombinationMode["Every"] = 0] = "Every"; + /** + * Enabled if enabled for at least one role. + */ + AmeRoleCombinationMode[AmeRoleCombinationMode["Some"] = 1] = "Some"; + /** + * As "Some", except when at least role one has a custom setting that is `false` + * (i.e. disabled) and none of the other roles have custom settings. + * + * This way explicit "disable"/"deny" settings take precedence over settings + * or permissions that are enabled by default. + */ + AmeRoleCombinationMode[AmeRoleCombinationMode["CustomOrSome"] = 2] = "CustomOrSome"; +})(AmeRoleCombinationMode || (AmeRoleCombinationMode = {})); +const AmeActorFeatureStrategyDefaults = { + superAdminDefault: null, + roleDefault: null, + roleCombinationMode: AmeRoleCombinationMode.CustomOrSome, + noValueDefault: false, + autoResetAll: true, +}; +class AmeActorFeatureStrategy { + constructor(settings) { + this.settings = Object.assign({}, AmeActorFeatureStrategyDefaults, settings); + } + isFeatureEnabled(actorFeatureMap, outIsIndeterminate = null) { + return this.isFeatureEnabledForActor(actorFeatureMap, this.settings.getSelectedActor(), outIsIndeterminate); + } + isFeatureEnabledForActor(actorFeatureMap, actor, outIsIndeterminate = null) { + if (actor === null) { + return this.checkAllActors(actorFeatureMap, outIsIndeterminate); + } + if (outIsIndeterminate !== null) { + //The result can only be indeterminate if there are multiple actors. + outIsIndeterminate(false); + } + //Is there an explicit setting for this actor? + const ownSetting = actorFeatureMap.get(actor.getId(), null); + if (ownSetting !== null) { + return ownSetting; + } + if (actor.isUser()) { + //The "Super Admin" setting takes precedence over regular roles. + if (actor.isSuperAdmin) { + const superAdminSetting = actorFeatureMap.get(AmeSuperAdmin.permanentActorId, this.settings.superAdminDefault); + if (superAdminSetting !== null) { + return superAdminSetting; + } + } + const isEnabledForRoles = this.checkRoles(actorFeatureMap, actor.getRoleIds()); + if (isEnabledForRoles !== null) { + return isEnabledForRoles; + } + //If we get this far, it means that none of the user's roles have + //a setting for this item. Fall through to the final default. + } + return this.settings.noValueDefault; + } + checkAllActors(actorFeatureMap, outIsIndeterminate = null) { + if (this.settings.getAllActors === null) { + throw ('When the selected actor is NULL, you must provide ' + + 'a callback that retrieves all actors so that it is possible to determine if ' + + 'the item is enabled for all/any of them'); + } + const allActors = this.settings.getAllActors(); + //Handle the theoretically impossible case where the actor list is empty. + const actorCount = allActors.length; + if (actorCount <= 0) { + return this.settings.noValueDefault; + } + let isEnabledForSome = false, isDisabledForSome = false; + for (let i = 0; i < actorCount; i++) { + const actor = allActors[i]; + if (this.isFeatureEnabledForActor(actorFeatureMap, actor)) { + isEnabledForSome = true; + } + else { + isDisabledForSome = true; + } + } + if (outIsIndeterminate !== null) { + outIsIndeterminate(isEnabledForSome && isDisabledForSome); + } + return isEnabledForSome && !isDisabledForSome; + } + checkRoles(actorFeatureMap, roles) { + const length = roles.length; + if (length === 0) { + return null; + } + //Check role settings. + let foundAnySettings = false; + let areAllTrue = true; + let areSomeTrue = false; + let foundAnyCustomSettings = false; + let areAllCustomTrue = true; + let areSomeCustomTrue = false; + for (let i = 0; i < length; i++) { + let roleSetting = actorFeatureMap.get('role:' + roles[i], null); + if (roleSetting !== null) { + foundAnyCustomSettings = true; + areSomeCustomTrue = areSomeCustomTrue || roleSetting; + areAllCustomTrue = areAllCustomTrue && roleSetting; + } + else { + roleSetting = (typeof this.settings.roleDefault === 'function') + ? this.settings.roleDefault(roles[i]) + : this.settings.roleDefault; + } + if (roleSetting !== null) { + foundAnySettings = true; + areAllTrue = areAllTrue && roleSetting; + areSomeTrue = areSomeTrue || roleSetting; + } + } + if (!foundAnySettings) { + return null; + } + switch (this.settings.roleCombinationMode) { + case AmeRoleCombinationMode.Every: + return areAllTrue; + case AmeRoleCombinationMode.Some: + return areSomeTrue; + case AmeRoleCombinationMode.CustomOrSome: + return foundAnyCustomSettings ? areSomeCustomTrue : areSomeTrue; + } + } + setFeatureEnabled(actorFeatureMap, enabled) { + this.setFeatureEnabledForActor(actorFeatureMap, this.settings.getSelectedActor(), enabled); + } + setFeatureEnabledForActor(actorFeatureMap, actor, enabled) { + if (actor === null) { + this.setAllActorStates(actorFeatureMap, enabled); + return; + } + actorFeatureMap.set(actor.getId(), enabled); + } + setAllActorStates(actorFeatureMap, enabled) { + if (this.settings.getAllActors === null) { + throw ('When the selected actor is NULL, you must provide a callback that retrieves ' + + 'a list of all actors so that the item can be enabled or disabled for all of them'); + } + //Enable/disable the feature for all actors. + if (this.settings.autoResetAll && (enabled === this.settings.noValueDefault)) { + //Since the new value is the same as the configured default, + //this is equivalent to removing all settings. + actorFeatureMap.resetAll(); + } + else { + const allActors = this.settings.getAllActors(); + for (let i = 0; i < allActors.length; i++) { + actorFeatureMap.set(allActors[i].getId(), enabled); + } + } + } +} +class AmeActorFeatureState { + constructor(actorFeatureMap, strategy) { + this.actorFeatureMap = actorFeatureMap; + this.strategy = strategy; + const _isIndeterminate = ko.observable(false); + this.isIndeterminate = ko.pureComputed(() => _isIndeterminate()); + this.isEnabled = ko.computed({ + read: () => { + return this.strategy.isFeatureEnabled(this.actorFeatureMap, _isIndeterminate); + }, + write: (value) => { + const enabled = !!value; + this.strategy.setFeatureEnabled(this.actorFeatureMap, enabled); + } + }); + } + toJs() { + return this.actorFeatureMap.getAll(); + } +} if (typeof wsAmeActorData !== 'undefined') { AmeActors = new AmeActorManager(wsAmeActorData.roles, wsAmeActorData.users, wsAmeActorData.isMultisite, wsAmeActorData.suspectedMetaCaps); if (typeof wsAmeActorData['capPower'] !== 'undefined') { diff --git a/js/actor-manager.js.map b/js/actor-manager.js.map index 0084038..12dfced 100644 --- a/js/actor-manager.js.map +++ b/js/actor-manager.js.map @@ -1 +1 @@ -{"version":3,"file":"actor-manager.js","sourceRoot":"","sources":["actor-manager.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"actor-manager.js","sourceRoot":"","sources":["actor-manager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/js/actor-manager.ts b/js/actor-manager.ts index dab491c..fa620a5 100644 --- a/js/actor-manager.ts +++ b/js/actor-manager.ts @@ -4,23 +4,34 @@ declare let wsAmeActorData: any; declare var wsAmeLodash: _.LoDashStatic; -declare let AmeActors: AmeActorManager; +// noinspection ES6ConvertVarToLetConst -- Intentionally global variable +var AmeActors: AmeActorManager; type Falsy = false | null | '' | undefined | 0; type Truthy = true | string | 1; interface CapabilityMap { - [capabilityName: string] : boolean; + [capabilityName: string]: boolean; } interface IAmeActor { getId(): string; + getDisplayName(): string; + + isUser(): this is IAmeUser; } interface IAmeUser extends IAmeActor { userLogin: string; isSuperAdmin: boolean; + + /** + * Get all roles that this user has. + * + * Note that this returns role IDs, not role objects or actor IDs. + */ + getRoleIds(): string[]; } abstract class AmeBaseActor implements IAmeActor { @@ -31,7 +42,12 @@ abstract class AmeBaseActor implements IAmeActor { groupActors: string[] = []; - protected constructor(id: string, displayName: string, capabilities: CapabilityMap, metaCapabilities: CapabilityMap = {}) { + protected constructor( + id: string, + displayName: string, + capabilities: CapabilityMap, + metaCapabilities: CapabilityMap = {} + ) { this.id = id; this.displayName = displayName; this.capabilities = capabilities; @@ -47,7 +63,7 @@ abstract class AmeBaseActor implements IAmeActor { * @param {string} capability * @returns {boolean|null} */ - hasOwnCap(capability: string): boolean { + hasOwnCap(capability: string): boolean | null { if (this.capabilities.hasOwnProperty(capability)) { return this.capabilities[capability]; } @@ -87,17 +103,26 @@ abstract class AmeBaseActor implements IAmeActor { getDisplayName(): string { return this.displayName; } + + isUser(): this is IAmeUser { + return false; + } } class AmeRole extends AmeBaseActor { name: string; - constructor(roleId: string, displayName: string, capabilities: CapabilityMap, metaCapabilities: CapabilityMap = {}) { + constructor( + roleId: string, + displayName: string, + capabilities: CapabilityMap, + metaCapabilities: CapabilityMap = {} + ) { super('role:' + roleId, displayName, capabilities, metaCapabilities); this.name = roleId; } - hasOwnCap(capability: string): boolean { + hasOwnCap(capability: string): boolean | null { //In WordPress, a role name is also a capability name. Users that have the role "foo" always //have the "foo" capability. It's debatable whether the role itself actually has that capability //(WP_Role says no), but it's convenient to treat it that way. @@ -113,7 +138,7 @@ interface AmeUserPropertyMap { display_name: string; capabilities: CapabilityMap; meta_capabilities: CapabilityMap; - roles : string[]; + roles: string[]; is_super_admin: boolean; id?: number; avatar_html?: string; @@ -124,7 +149,6 @@ class AmeUser extends AmeBaseActor implements IAmeUser { userId: number = 0; roles: string[]; isSuperAdmin: boolean = false; - groupActors: string[]; avatarHTML: string = ''; constructor( @@ -133,7 +157,7 @@ class AmeUser extends AmeBaseActor implements IAmeUser { capabilities: CapabilityMap, roles: string[], isSuperAdmin: boolean = false, - userId?: number, + userId?: number, metaCapabilities: CapabilityMap = {} ) { super('user:' + userLogin, displayName, capabilities, metaCapabilities); @@ -158,7 +182,7 @@ class AmeUser extends AmeBaseActor implements IAmeUser { properties.capabilities, properties.roles, properties.is_super_admin, - properties.hasOwnProperty('id') ? properties.id : null, + properties.id, properties.meta_capabilities ); @@ -168,6 +192,14 @@ class AmeUser extends AmeBaseActor implements IAmeUser { return user; } + + isUser(): this is IAmeUser { + return true; + } + + getRoleIds(): string[] { + return this.roles; + } } class AmeSuperAdmin extends AmeBaseActor { @@ -185,7 +217,7 @@ class AmeSuperAdmin extends AmeBaseActor { interface AmeGrantedCapabilityMap { [actorId: string]: { - [capability: string] : any + [capability: string]: any } } @@ -197,23 +229,32 @@ interface AmeCapabilitySuggestion { class AmeActorManager implements AmeActorManagerInterface { private static _ = wsAmeLodash; - private roles: {[roleId: string] : AmeRole} = {}; - private users: {[userLogin: string] : AmeUser} = {}; + private roles: { [roleId: string]: AmeRole } = {}; + private users: { [userLogin: string]: AmeUser } = {}; private grantedCapabilities: AmeGrantedCapabilityMap = {}; public readonly isMultisite: boolean = false; private readonly superAdmin: AmeSuperAdmin; - private exclusiveSuperAdminCapabilities = {}; + private exclusiveSuperAdminCapabilities: Record = {}; - private tagMetaCaps = {}; + private tagMetaCaps: Record = {}; private suspectedMetaCaps: CapabilityMap; private suggestedCapabilities: AmeCapabilitySuggestion[] = []; - constructor(roles, users, isMultisite: Truthy | Falsy = false, suspectedMetaCaps: CapabilityMap = {}) { + constructor( + roles: Record, + users: Record, + isMultisite: Truthy | Falsy = false, + suspectedMetaCaps: CapabilityMap = {} + ) { this.isMultisite = !!isMultisite; AmeActorManager._.forEach(roles, (roleDetails, id) => { + if (typeof id === 'undefined') { + return; + } + const role = new AmeRole( id, roleDetails.name, @@ -253,9 +294,9 @@ class AmeActorManager implements AmeActorManagerInterface { // noinspection JSUnusedGlobalSymbols actorCanAccess( actorId: string, - grantAccess: {[actorId: string] : boolean}, - defaultCapability: string = null - ): boolean { + grantAccess: { [actorId: string]: boolean }, + defaultCapability: string | null = null + ): boolean | null { if (grantAccess.hasOwnProperty(actorId)) { return grantAccess[actorId]; } @@ -265,7 +306,7 @@ class AmeActorManager implements AmeActorManagerInterface { return true; } - getActor(actorId): AmeBaseActor { + getActor(actorId: string): AmeBaseActor | null { if (actorId === AmeSuperAdmin.permanentActorId) { return this.superAdmin; } @@ -291,7 +332,13 @@ class AmeActorManager implements AmeActorManagerInterface { try { return (this.getActor(actorId) !== null); } catch (exception) { - if (exception.hasOwnProperty('name') && (exception.name === 'InvalidActorException')) { + const exceptionAsAny = exception as any; + if ( + (typeof exceptionAsAny === 'object') + && (exceptionAsAny !== null) + && (typeof exceptionAsAny.name === 'string') + && (exceptionAsAny.name === 'InvalidActorException') + ) { return false; } else { throw exception; @@ -299,19 +346,23 @@ class AmeActorManager implements AmeActorManagerInterface { } } - hasCap(actorId: string, capability, context?: {[actor: string] : any}): boolean { + hasCap(actorId: string, capability: string, context?: { [actor: string]: any }): boolean | null { context = context || {}; return this.actorHasCap(actorId, capability, [context, this.grantedCapabilities]); } - hasCapByDefault(actorId, capability) { + hasCapByDefault(actorId: string, capability: string) { return this.actorHasCap(actorId, capability); } - private actorHasCap(actorId: string, capability: string, contextList?: Array): (boolean | null) { + private actorHasCap( + actorId: string, + capability: string, + contextList?: Array> + ): (boolean | null) { //It's like the chain-of-responsibility pattern. - //Everybody has the "exist" cap and it can't be removed or overridden by plugins. + //Everybody has the "exist" cap, and it can't be removed or overridden by plugins. if (capability === 'exist') { return true; } @@ -408,7 +459,7 @@ class AmeActorManager implements AmeActorManagerInterface { return this.roles.hasOwnProperty(roleId); }; - getSuperAdmin() : AmeSuperAdmin { + getSuperAdmin(): AmeSuperAdmin { return this.superAdmin; } @@ -420,7 +471,7 @@ class AmeActorManager implements AmeActorManagerInterface { return this.users; } - getUser(login: string) { + getUser(login: string): IAmeUser | null { return this.users.hasOwnProperty(login) ? this.users[login] : null; } @@ -438,7 +489,7 @@ class AmeActorManager implements AmeActorManagerInterface { * Granted capability manipulation * ------------------------------- */ - setGrantedCapabilities(newGrants) { + setGrantedCapabilities(newGrants: AmeGrantedCapabilityMap) { this.grantedCapabilities = AmeActorManager._.cloneDeep(newGrants); } @@ -449,7 +500,7 @@ class AmeActorManager implements AmeActorManagerInterface { /** * Grant or deny a capability to an actor. */ - setCap(actor: string, capability: string, hasCap: boolean, sourceType?, sourceName?) { + setCap(actor: string, capability: string, hasCap: boolean, sourceType?: string, sourceName?: string) { this.setCapInContext(this.grantedCapabilities, actor, capability, hasCap, sourceType, sourceName); } @@ -543,11 +594,16 @@ class AmeActorManager implements AmeActorManagerInterface { return delta; }; - generateCapabilitySuggestions(capPower): void { + generateCapabilitySuggestions(capPower: Record): void { let _ = AmeActorManager._; - let capsByPower = _.memoize((role: AmeRole): {capability: string, power: number}[] => { - let sortedCaps = _.reduce(role.capabilities, (result, hasCap, capability) => { + interface CapAndPower { + capability: string; + power: number; + } + + let capsByPower = _.memoize((role: AmeRole): CapAndPower[] => { + let sortedCaps = _.reduce(role.capabilities, (result: CapAndPower[], hasCap, capability) => { if (hasCap) { result.push({ capability: capability, @@ -561,7 +617,7 @@ class AmeActorManager implements AmeActorManagerInterface { return sortedCaps; }); - let rolesByPower: AmeRole[] = _.values(this.getRoles()).sort(function(a: AmeRole, b: AmeRole) { + let rolesByPower: AmeRole[] = _.values(this.getRoles()).sort(function (a: AmeRole, b: AmeRole) { let aCaps = capsByPower(a), bCaps = capsByPower(b); @@ -602,15 +658,15 @@ class AmeActorManager implements AmeActorManagerInterface { let deprecatedCaps = _(_.range(0, 10)).map((level) => 'level_' + level).value(); deprecatedCaps.push('edit_files'); - let findDiscriminant = (caps: string[], includeRoles: AmeRole[], excludeRoles): string => { + let findDiscriminant = (caps: string[], includeRoles: AmeRole[], excludeRoles: AmeRole[]): string => { let getEnabledCaps = (role: AmeRole): string[] => { return _.keys(_.pick(role.capabilities, _.identity)); }; - //Find caps that all of the includeRoles have and excludeRoles don't. - let includeCaps = _.intersection.apply(_, _.map(includeRoles, getEnabledCaps)), - excludeCaps = _.union.apply(_, _.map(excludeRoles, getEnabledCaps)), - possibleCaps = _.without.apply(_, [includeCaps].concat(excludeCaps).concat(deprecatedCaps)); + //Find caps that all the includeRoles have and excludeRoles don't. + let includeCaps = _.intersection(..._.map(includeRoles, getEnabledCaps)), + excludeCaps = _.union(..._.map(excludeRoles, getEnabledCaps)), + possibleCaps = _.without(includeCaps, ...excludeCaps, ...deprecatedCaps); let bestCaps = _.intersection(preferredCaps, possibleCaps); @@ -619,7 +675,7 @@ class AmeActorManager implements AmeActorManagerInterface { } else if (possibleCaps.length > 0) { return possibleCaps[0]; } - return null; + return ''; }; let suggestedCapabilities = []; @@ -658,31 +714,52 @@ class AmeActorManager implements AmeActorManagerInterface { interface AmeActorManagerInterface { getUsers(): AmeDictionary; - getUser(login: string): IAmeUser; - addUsers(newUsers: IAmeUser[]); + + getUser(login: string): IAmeUser | null; + + addUsers(newUsers: IAmeUser[]): void; + createUserFromProperties(properties: AmeUserPropertyMap): IAmeUser; getRoles(): AmeDictionary; + getSuperAdmin(): IAmeActor; - getActor(actorId): IAmeActor; + getActor(actorId: string): IAmeActor | null; + actorExists(actorId: string): boolean; } -class AmeObservableActorSettings { - private items: { [actorId: string] : KnockoutObservable; } = {}; +type AmeActorFeatureMapData = { [actorId: string]: boolean }; + +interface AmeActorFeatureMap { + get(actorId: string, defaultValue: boolean | null): boolean | null; + + set(actorId: string, value: boolean): void; + + setAll(data: AmeActorFeatureMapData): void; + + getAll(): AmeActorFeatureMapData; + + reset(actorId: string): void; + + resetAll(): void; +} + +class AmeObservableActorFeatureMap implements AmeActorFeatureMap { + private items: { [actorId: string]: KnockoutObservable; } = {}; private readonly numberOfObservables: KnockoutObservable; - constructor(initialData?: AmeDictionary) { + constructor(initialData?: AmeDictionary | null) { this.numberOfObservables = ko.observable(0); if (initialData) { this.setAll(initialData); } } - get(actor: string, defaultValue = null): boolean { + get(actor: string, defaultValue: boolean | null = null): boolean | null { if (this.items.hasOwnProperty(actor)) { - let value = this.items[actor](); + const value = this.items[actor](); if (value === null) { return defaultValue; } @@ -694,18 +771,18 @@ class AmeObservableActorSettings { set(actor: string, value: boolean) { if (!this.items.hasOwnProperty(actor)) { - this.items[actor] = ko.observable(value); + this.items[actor] = ko.observable(value); this.numberOfObservables(this.numberOfObservables() + 1); } else { this.items[actor](value); } } - getAll(): AmeDictionary { - let result: AmeDictionary = {}; + getAll(): AmeActorFeatureMapData { + let result: AmeActorFeatureMapData = {}; for (let actorId in this.items) { if (this.items.hasOwnProperty(actorId)) { - let value = this.items[actorId](); + const value = this.items[actorId](); if (value !== null) { result[actorId] = value; } @@ -714,7 +791,7 @@ class AmeObservableActorSettings { return result; } - setAll(values: AmeDictionary) { + setAll(values: AmeActorFeatureMapData) { for (let actorId in values) { if (values.hasOwnProperty(actorId)) { this.set(actorId, values[actorId]); @@ -722,6 +799,12 @@ class AmeObservableActorSettings { } } + reset(actorId: string): void { + if (this.items.hasOwnProperty(actorId)) { + this.items[actorId](null); + } + } + /** * Reset all values to null. */ @@ -739,14 +822,14 @@ class AmeObservableActorSettings { roleDefault: boolean | null = false, superAdminDefault: boolean | null = null, noValueDefault: boolean = false, - outIsIndeterminate: KnockoutObservable = null + outIsIndeterminate: KnockoutObservable | null = null ): boolean { - if ((selectedActor === null) && (allActors === null)) { - throw 'When the selected actor is NULL, you must provide ' + - 'a list of all visible actors to determine if the item is enabled for all/any of them'; - } - if (selectedActor === null) { + if (allActors === null) { + throw 'When the selected actor is NULL, you must provide ' + + 'a list of all visible actors to determine if the item is enabled for all/any of them'; + } + //All: Enabled only if it's enabled for all actors. //Handle the theoretically impossible case where the actor list is empty. @@ -789,7 +872,7 @@ class AmeObservableActorSettings { //Use role settings. //Enabled for at least one role = enabled. //Disabled for at least one role and no settings for other roles = disabled. - let isEnabled: boolean|null = null; + let isEnabled: boolean | null = null; for (let i = 0; i < selectedActor.roles.length; i++) { let roleSetting = this.get('role:' + selectedActor.roles[i], roleDefault); if (roleSetting !== null) { @@ -813,17 +896,17 @@ class AmeObservableActorSettings { } setEnabledFor( - selectedActor: IAmeActor|null, + selectedActor: IAmeActor | null, enabled: boolean, - allActors: IAmeActor[]|null = null, - defaultValue: boolean|null = null + allActors: IAmeActor[] | null = null, + defaultValue: boolean | null = null ) { - if ((selectedActor === null) && (allActors === null)) { - throw 'When the selected actor is NULL, you must provide ' + - 'a list of all visible actors so that the item can be enabled or disabled for all of them'; - } - if (selectedActor === null) { + if (allActors === null) { + throw 'When the selected actor is NULL, you must provide ' + + 'a list of all visible actors so that the item can be enabled or disabled for all of them'; + } + //Enable/disable the item for all actors. if (enabled === defaultValue) { //Since the new value is the same as the default, @@ -840,6 +923,275 @@ class AmeObservableActorSettings { } } +enum AmeRoleCombinationMode { + /** + * Enabled if enabled for every role the user has. + */ + Every, + /** + * Enabled if enabled for at least one role. + */ + Some, + /** + * As "Some", except when at least role one has a custom setting that is `false` + * (i.e. disabled) and none of the other roles have custom settings. + * + * This way explicit "disable"/"deny" settings take precedence over settings + * or permissions that are enabled by default. + */ + CustomOrSome +} + +interface AmeActorFeatureStrategySettings { + getSelectedActor: () => IAmeActor | null; + getAllActors: () => IAmeActor[]; + superAdminDefault: boolean | null; + roleDefault: boolean | null | ((roleName: string) => boolean | null); + roleCombinationMode: AmeRoleCombinationMode + noValueDefault: boolean; + + /** + * Whether to automatically reset (i.e. remove) all settings when changing + * all actors to a new value that is the same as the default. + */ + autoResetAll: boolean; +} + +type AmeRequiredFeatureStrategyKeys = 'getSelectedActor' | 'getAllActors'; + +/** + * Most of the settings are optional when creating a new strategy. + */ +type AmeFeatureStrategyConstructorSettings = + Partial> + & Pick; + +const AmeActorFeatureStrategyDefaults: Omit = { + superAdminDefault: null, + roleDefault: null, + roleCombinationMode: AmeRoleCombinationMode.CustomOrSome, + noValueDefault: false, + autoResetAll: true, +} + +class AmeActorFeatureStrategy { + private readonly settings: AmeActorFeatureStrategySettings; + + constructor(settings: AmeFeatureStrategyConstructorSettings) { + this.settings = Object.assign({}, AmeActorFeatureStrategyDefaults, settings); + } + + isFeatureEnabled( + actorFeatureMap: AmeActorFeatureMap, + outIsIndeterminate: KnockoutObservable | null = null + ): boolean { + return this.isFeatureEnabledForActor( + actorFeatureMap, + this.settings.getSelectedActor(), + outIsIndeterminate + ); + } + + private isFeatureEnabledForActor( + actorFeatureMap: AmeActorFeatureMap, + actor: IAmeActor | null, + outIsIndeterminate: KnockoutObservable | null = null + ): boolean { + if (actor === null) { + return this.checkAllActors(actorFeatureMap, outIsIndeterminate); + } + + if (outIsIndeterminate !== null) { + //The result can only be indeterminate if there are multiple actors. + outIsIndeterminate(false); + } + + //Is there an explicit setting for this actor? + const ownSetting = actorFeatureMap.get(actor.getId(), null); + if (ownSetting !== null) { + return ownSetting; + } + + if (actor.isUser()) { + //The "Super Admin" setting takes precedence over regular roles. + if (actor.isSuperAdmin) { + const superAdminSetting = actorFeatureMap.get( + AmeSuperAdmin.permanentActorId, + this.settings.superAdminDefault + ); + if (superAdminSetting !== null) { + return superAdminSetting; + } + } + + const isEnabledForRoles = this.checkRoles(actorFeatureMap, actor.getRoleIds()); + if (isEnabledForRoles !== null) { + return isEnabledForRoles; + } + + //If we get this far, it means that none of the user's roles have + //a setting for this item. Fall through to the final default. + } + + return this.settings.noValueDefault; + } + + private checkAllActors( + actorFeatureMap: AmeActorFeatureMap, + outIsIndeterminate: KnockoutObservable | null = null + ): boolean { + if (this.settings.getAllActors === null) { + throw ( + 'When the selected actor is NULL, you must provide ' + + 'a callback that retrieves all actors so that it is possible to determine if ' + + 'the item is enabled for all/any of them' + ); + } + const allActors = this.settings.getAllActors(); + //Handle the theoretically impossible case where the actor list is empty. + const actorCount = allActors.length; + if (actorCount <= 0) { + return this.settings.noValueDefault; + } + + let isEnabledForSome = false, isDisabledForSome = false; + for (let i = 0; i < actorCount; i++) { + const actor = allActors[i]; + if (this.isFeatureEnabledForActor(actorFeatureMap, actor)) { + isEnabledForSome = true; + } else { + isDisabledForSome = true; + } + } + + if (outIsIndeterminate !== null) { + outIsIndeterminate(isEnabledForSome && isDisabledForSome); + } + return isEnabledForSome && !isDisabledForSome; + } + + private checkRoles(actorFeatureMap: AmeActorFeatureMap, roles: string[]): boolean | null { + const length = roles.length; + if (length === 0) { + return null; + } + + //Check role settings. + let foundAnySettings = false; + let areAllTrue = true; + let areSomeTrue = false; + + let foundAnyCustomSettings = false; + let areAllCustomTrue = true; + let areSomeCustomTrue = false; + + for (let i = 0; i < length; i++) { + let roleSetting = actorFeatureMap.get('role:' + roles[i], null); + + if (roleSetting !== null) { + foundAnyCustomSettings = true; + areSomeCustomTrue = areSomeCustomTrue || roleSetting; + areAllCustomTrue = areAllCustomTrue && roleSetting; + } else { + roleSetting = (typeof this.settings.roleDefault === 'function') + ? this.settings.roleDefault(roles[i]) + : this.settings.roleDefault; + } + + if (roleSetting !== null) { + foundAnySettings = true; + areAllTrue = areAllTrue && roleSetting; + areSomeTrue = areSomeTrue || roleSetting; + } + } + + if (!foundAnySettings) { + return null; + } + + switch (this.settings.roleCombinationMode) { + case AmeRoleCombinationMode.Every: + return areAllTrue; + case AmeRoleCombinationMode.Some: + return areSomeTrue; + case AmeRoleCombinationMode.CustomOrSome: + return foundAnyCustomSettings ? areSomeCustomTrue : areSomeTrue; + } + } + + setFeatureEnabled( + actorFeatureMap: AmeActorFeatureMap, + enabled: boolean + ) { + this.setFeatureEnabledForActor( + actorFeatureMap, + this.settings.getSelectedActor(), + enabled + ); + } + + private setFeatureEnabledForActor( + actorFeatureMap: AmeActorFeatureMap, + actor: IAmeActor | null, + enabled: boolean + ) { + if (actor === null) { + this.setAllActorStates(actorFeatureMap, enabled); + return; + } + + actorFeatureMap.set(actor.getId(), enabled); + } + + private setAllActorStates(actorFeatureMap: AmeActorFeatureMap, enabled: boolean) { + if (this.settings.getAllActors === null) { + throw ( + 'When the selected actor is NULL, you must provide a callback that retrieves ' + + 'a list of all actors so that the item can be enabled or disabled for all of them' + ); + } + + //Enable/disable the feature for all actors. + if (this.settings.autoResetAll && (enabled === this.settings.noValueDefault)) { + //Since the new value is the same as the configured default, + //this is equivalent to removing all settings. + actorFeatureMap.resetAll(); + } else { + const allActors = this.settings.getAllActors(); + for (let i = 0; i < allActors.length; i++) { + actorFeatureMap.set(allActors[i].getId(), enabled); + } + } + } +} + +class AmeActorFeatureState { + public readonly isEnabled: KnockoutComputed; + public readonly isIndeterminate: KnockoutComputed; + + constructor( + public readonly actorFeatureMap: AmeActorFeatureMap, + public readonly strategy: AmeActorFeatureStrategy + ) { + const _isIndeterminate = ko.observable(false); + this.isIndeterminate = ko.pureComputed(() => _isIndeterminate()); + + this.isEnabled = ko.computed({ + read: () => { + return this.strategy.isFeatureEnabled(this.actorFeatureMap, _isIndeterminate); + }, + write: (value: any) => { + const enabled = !!value; + this.strategy.setFeatureEnabled(this.actorFeatureMap, enabled); + } + }) + } + + toJs(): AmeActorFeatureMapData { + return this.actorFeatureMap.getAll(); + } +} + if (typeof wsAmeActorData !== 'undefined') { AmeActors = new AmeActorManager( wsAmeActorData.roles, diff --git a/js/common.d.ts b/js/common.d.ts index 932778a..6258fc3 100644 --- a/js/common.d.ts +++ b/js/common.d.ts @@ -23,3 +23,61 @@ type AmeRecursiveObservablePropertiesOf = { [P in keyof T]: T[P] extends object ? AmeRecursiveObservablePropertiesOf : KnockoutObservable; } +type Constructor = new (...args: any[]) => T; + +type WithRequiredKey = Omit & Required>; + +interface JQuery { + //My jQuery typings are out of date, and are missing this signature for the "off" method + //where the callback takes additional arguments. + off(events: string, handler: (eventObject: JQueryEventObject, ...args: any[]) => any): JQuery; +} + +/** + * Partial type definition for the WordPress "wp" global. + * Sure would be nice if WordPress provided this. + */ +declare const wp: { + codeEditor: { + //See /wp-admin/js/code-editor.js for basic method documentation. + initialize: (textarea: string|JQuery|Element, options: object) => any; + }; + media: { + (attributes: { + button: { text: string }; + library: { type: string }; + multiple: boolean; + title: string + }): any; + + attachment(id: number): any; + }; + editor: { + remove: (id: string) => void; + initialize: (id: string, settings: Partial) => any; + }; +}; + +/** + * Incomplete type definition for the settings object that can be passed to wp.editor.initialize(). + */ +interface WpEditorInitSettings { + tinymce: boolean | { + wpautop: boolean; + toolbar1?: string, + toolbar2?: string, + toolbar3?: string, + toolbar4?: string, + }; + quicktags: boolean | { + buttons?: string; + }; + mediaButtons: boolean; +} + +/** + * Webpack will replace this with a boolean literal. Note that + * when not using Webpack, it will be undefined, so a typeof check + * is recommended. + */ +declare const AME_IS_PRODUCTION: boolean; \ No newline at end of file diff --git a/js/jquery.biscuit.d.ts b/js/jquery.biscuit.d.ts index 4839271..97a4911 100644 --- a/js/jquery.biscuit.d.ts +++ b/js/jquery.biscuit.d.ts @@ -2,4 +2,12 @@ interface JQueryStatic { //These methods are added by the jquery-cookie plugin. cookie: (name: string, value?: string, options?: {}) => string; removeCookie: (name: string, options?: {}) => boolean; +} + +declare class WsAmePreferenceCookie { + constructor(name: string, expirationInDays: number, jsonEncodingEnabled: boolean); + read(defaultValue: any): any; + write(value: any): void; + removeCookie(): boolean; + readAndRefresh(defaultValue: D): D|any; } \ No newline at end of file diff --git a/js/jquery.biscuit.js b/js/jquery.biscuit.js index ec4a7b6..59e6ce3 100644 --- a/js/jquery.biscuit.js +++ b/js/jquery.biscuit.js @@ -118,3 +118,86 @@ }; })); + + +/** + * A wrapper object for a user preference stored in a cookie. + * + * Created by Jānis Elsts. Added to the same file as the cookie library + * to avoid a separate HTTP request. + * + * @param {string} name + * @param {number} [expirationInDays] + * @param {boolean} [jsonEncodingEnabled] + * @constructor + */ +function WsAmePreferenceCookie(name, expirationInDays, jsonEncodingEnabled) { + if (typeof expirationInDays === 'undefined') { + expirationInDays = 90; + } + if (typeof jsonEncodingEnabled === 'undefined') { + jsonEncodingEnabled = false; + } + + //Full name = unique prefix + name with the first letter capitalized. + this.fullCookieName = 'amePref' + name.charAt(0).toUpperCase() + name.slice(1); + this.jsonEncodingEnabled = jsonEncodingEnabled; + this.cookieOptions = { + 'path': '/', + 'samesite': 'lax' + }; + if (expirationInDays > 0) { + this.cookieOptions.expires = expirationInDays; + } +} + +WsAmePreferenceCookie.prototype.read = function (defaultValue) { + let cookieValue = jQuery.cookie(this.fullCookieName); + if (typeof cookieValue === 'undefined') { + return defaultValue; + } + + if (this.jsonEncodingEnabled) { + if ((typeof cookieValue === 'string') && (cookieValue !== '')) { + try { + cookieValue = JSON.parse(cookieValue); + } catch (error) { + return defaultValue; //Use the default value if the stored JSON is invalid. + } + } else { + return defaultValue; + } + } + + return cookieValue; +} + +WsAmePreferenceCookie.prototype.write = function (value) { + if (this.jsonEncodingEnabled) { + value = JSON.stringify(value); + } + jQuery.cookie(this.fullCookieName, value, this.cookieOptions); +} + +WsAmePreferenceCookie.prototype.removeCookie = function () { + return jQuery.removeCookie(this.fullCookieName, this.cookieOptions); +} + +/** + * Read the cookie value, and if it's set, write it back to the cookie. + * This extends the cookie's expiration date by the configured number of days. + * + * @param {*} defaultValue + * @returns {*} + */ +WsAmePreferenceCookie.prototype.readAndRefresh = function (defaultValue) { + const notFound = {}; + const value = this.read(notFound); + if (value === notFound) { + return defaultValue; + } + + //phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.write -- This is not document.write(), but a custom method. + this.write(value); + return value; +} \ No newline at end of file diff --git a/js/jquery.color.d.ts b/js/jquery.color.d.ts new file mode 100644 index 0000000..93484d2 --- /dev/null +++ b/js/jquery.color.d.ts @@ -0,0 +1,204 @@ +// Type definitions for jquery.color.js v2.1.2 +// Project: https://github.com/jquery/jquery-color +// Definitions by: Derek Cicerone +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.3 + +/// + +interface JQueryColor { + /** + * Returns the red component of the color (integer from 0 - 255). + */ + red(): number; + + /** + * Returns a copy of the color object with the red set to val. + */ + red(val: number): JQueryColor; + + /** + * Returns a copy of the color object with the red set to val. + */ + red(val: string): JQueryColor; + + /** + * Returns the green component of the color (integer from 0 - 255). + */ + green(): number; + + /** + * Returns a copy of the color object with the green set to val. + */ + green(val: number): JQueryColor; + + /** + * Returns a copy of the color object with the green set to val. + */ + green(val: string): JQueryColor; + + /** + * Returns the blue component of the color (integer from 0 - 255). + */ + blue(): number; + + /** + * Returns a copy of the color object with the blue set to val. + */ + blue(val: number): JQueryColor; + + /** + * Returns a copy of the color object with the blue set to val. + */ + blue(val: string): JQueryColor; + + /** + * Returns the alpha value of this color (float from 0.0 - 1.0). + */ + alpha(): number; + + /** + * Returns a copy of the color object with the alpha set to val. + */ + alpha(val: number): JQueryColor; + + /** + * Returns a copy of the color object with the alpha set to val. + */ + alpha(val: string): JQueryColor; + + /** + * Returns the hue component of the color (integer from 0 - 359). + */ + hue(): number; + + /** + * Returns a copy of the color object with the hue set to val. + */ + hue(val: number): JQueryColor; + + /** + * Returns a copy of the color object with the hue set to val. + */ + hue(val: string): JQueryColor; + + /** + * Returns the saturation component of the color (float from 0.0 - 1.0). + */ + saturation(): number; + + /** + * Returns a copy of the color object with the saturation set to val. + */ + saturation(val: number): JQueryColor; + + /** + * Returns a copy of the color object with the saturation set to val. + */ + saturation(val: string): JQueryColor; + + /** + * Returns the lightness component of the color (float from 0.0 - 1.0). + */ + lightness(): number; + + /** + * Returns a copy of the color object with the lightness set to val. + */ + lightness(val: number): JQueryColor; + + /** + * Returns a copy of the color object with the lightness set to val. + */ + lightness(val: string): JQueryColor; + + /** + * Returns a rgba "tuple" [ red, green, blue, alpha ]. + */ + rgba(): number[]; + + /** + * Returns a copy of the color with any defined values set to the new value. + */ + rgba(red: number, green: number, blue: number, alpha?: number): JQueryColor; + + /** + * Returns a copy of the color with any defined values set to the new value. + */ + rgba(val: RgbaColor): JQueryColor; + + /** + * Returns a copy of the color with any defined values set to the new value. + */ + rgba(vals: number[]): JQueryColor; + + /** + * Returns a HSL tuple [ hue, saturation, lightness, alpha ]. + */ + hsla(): number[]; + + /** + * Returns a copy of the color with any defined values set to the new value. + */ + hsla(hue: number, saturation: number, lightness: number, alpha?: number): JQueryColor; + + /** + * Returns a copy of the color with any defined values set to the new value. + */ + hsla(val: HslaColor): JQueryColor; + + /** + * Returns a copy of the color with any defined values set to the new value. + */ + hsla(vals: number[]): JQueryColor; + + /** + * Returns a CSS string "rgba(255, 255, 255, 0.4)". + */ + toRgbaString(): string; + + /** + * Returns a css string "hsla(330, 75%, 25%, 0.4)". + */ + toHslaString(): string; + + /** + * Returns a css string "#abcdef", with "includeAlpha" uses "#rrggbbaa" (alpha *= 255). + */ + toHexString(includeAlpha?: boolean): string; + + /** + * The color distance (0.0 - 1.0) of the way between this color and othercolor. + */ + transition(othercolor: JQueryColor, distance: number): JQueryColor; + + /** + * Will apply this color on top of the other color using alpha blending. + */ + blend(othercolor: JQueryColor): void; + + /** + * Checks if two colors are equal. + */ + is(otherColor: JQueryColor): boolean; +} + +interface HslaColor { + hue?: number | undefined; + saturation?: number | undefined; + lightness?: number | undefined; + alpha?: number | undefined; +} + +interface RgbaColor { + red?: number | undefined; + green?: number | undefined; + blue?: number | undefined; + alpha?: number | undefined; +} + +interface JQueryStatic { + Color(color: HslaColor): JQueryColor; + Color(color: RgbaColor): JQueryColor; + Color(color: string): JQueryColor; +} diff --git a/js/jquery.form.d.ts b/js/jquery.form.d.ts index 2f8c47b..791b755 100644 --- a/js/jquery.form.d.ts +++ b/js/jquery.form.d.ts @@ -1,7 +1,25 @@ /// +interface AjaxFormOptions { + url?: string; + type?: string; + dataType?: string; + beforeSerialize?: (jqForm: JQuery, options: AjaxFormOptions) => boolean; + beforeSubmit?: (formData: any[], jqForm: JQuery, options: AjaxFormOptions) => boolean; + clearForm?: boolean; + forceSync?: boolean; + iframe?: boolean; + resetForm?: boolean; + semantic?: boolean; + target?: string | JQuery; + timeout?: number; + success?: (response: any, statusText: string, xhr: JQueryXHR, jqForm: JQuery) => void; + error?: (xhr: JQueryXHR, statusText: string, errorThrown: string) => void; + complete?: (xhr: JQueryXHR, statusText: string) => void; +} + interface JQuery { //These method are added by the jquery-form plugin. - ajaxForm: (options: any) => JQuery; + ajaxForm: (options: AjaxFormOptions) => JQuery; resetForm: () => JQuery; } \ No newline at end of file diff --git a/js/knockout-sortable.js b/js/knockout-sortable.js new file mode 100644 index 0000000..85572cb --- /dev/null +++ b/js/knockout-sortable.js @@ -0,0 +1,494 @@ +// knockout-sortable 1.2.1 | (c) 2021 Ryan Niemeyer | http://www.opensource.org/licenses/mit-license +;(function(factory) { + if (typeof define === "function" && define.amd) { + // AMD anonymous module + define(["knockout", "jquery", "jquery-ui/ui/widgets/sortable", "jquery-ui/ui/widgets/draggable", "jquery-ui/ui/widgets/droppable"], factory); + } else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { + // CommonJS module + var ko = require("knockout"), + jQuery = require("jquery"); + require("jquery-ui/ui/widgets/sortable"); + require("jquery-ui/ui/widgets/draggable"); + require("jquery-ui/ui/widgets/droppable"); + factory(ko, jQuery); + } else { + // No module loader (plain \n", + esc_attr($id), + $code + ); + } + + /** + * Set the "type" attribute of the script tag. + * + * @param string $scriptType Typically either "text/javascript" or "module". If the script + * is not a module, the "type" attribute can be omitted. + * @return $this + */ + public function setType($scriptType) { + if ( ($scriptType === '') || !is_string($scriptType) ) { + return $this->removeAttribute('type'); + } + return $this->addAttribute('type', $scriptType); + } + + /** + * Set the "type" attribute of the script tag to "module". + * This is a shortcut for `setType('module')`. + * + * @return $this + */ + public function setTypeToModule() { + return $this->setType('module'); + } + + public function setAsync() { + return $this->addAttribute('async', true); + } + + /** + * Get the specified or auto-detected version number. + * + * @return string|null|false + */ + public function getEffectiveVersion() { + if ( $this->autoDetectVersion ) { + $fileName = $this->getAbsoluteFilePath(); + if ( !empty($fileName) && is_file($fileName) ) { + $mtime = filemtime($fileName); + if ( $mtime !== false ) { + $this->version = (string)$mtime; + } + } + $this->autoDetectVersion = false; //Already detected. + } + return $this->version; + } + + /** + * @return string|null + */ + protected function getAbsoluteFilePath() { + if ( ($this->absoluteFilePath !== null) || $this->pathDetectionDone ) { + return $this->absoluteFilePath; + } + + $this->absoluteFilePath = self::guessFilePathFromUrl($this->url); + $this->pathDetectionDone = true; + + return $this->absoluteFilePath; + } + + /** + * Attempt to determine the full path to a file that's part of a plugin, theme, + * or WordPress core based on its URL. + * + * This function does not check if the file actually exists. It also might not + * work when complex rewrite rules are used. + * + * @param string $dependencyUrl + * @return string|null + */ + protected static function guessFilePathFromUrl($dependencyUrl) { + static $baseUrlMap = null; + if ( $baseUrlMap === null ) { + $baseUrlMap = [ + plugins_url() => WP_PLUGIN_DIR, + plugins_url('', WPMU_PLUGIN_DIR . '/dummy') => WPMU_PLUGIN_DIR, + get_stylesheet_directory_uri() => get_stylesheet_directory(), + get_template_directory_uri() => get_template_directory(), + content_url() => WP_CONTENT_DIR, + site_url('/' . WPINC) => ABSPATH . WPINC, + ]; + } + + $fileName = null; + foreach ($baseUrlMap as $baseUrl => $directory) { + $baseUrlLength = strlen($baseUrl); + if ( substr($dependencyUrl, 0, $baseUrlLength) === $baseUrl ) { + $fileName = $directory . '/' . substr($dependencyUrl, $baseUrlLength); + //Remove the query string. + list($fileName,) = explode('?', $fileName, 2); + break; + } + } + + return $fileName; + } + + public function getUrl() { + return $this->url; + } + + /** + * Register the dependency with WordPress immediately. + * + * You might not need to call this method directly. The dependency will be + * registered automatically when one of the following happens: + * - enqueue() is called. + * - This object has been added to another dependency using addDependency(), + * and that dependency gets registered or enqueued. + * + * It's safe to call this method multiple times. It will only register + * the dependency once (unless it gets deregistered in the meantime). + * + * @return $this + */ + public function register() { + if ( $this->isRegistered() ) { + return $this; + } + + //Prepare and register dependencies where needed. + $preparedDependencies = []; + foreach ($this->dependencies as $dependency) { + if ( $dependency instanceof ScriptDependency ) { + $dependency->register(); + $preparedDependencies[] = $dependency->handle; + } else if ( !empty($dependency) ) { + $preparedDependencies[] = $dependency; + } + } + + wp_register_script( + $this->handle, + $this->getUrl(), + $preparedDependencies, + $this->getEffectiveVersion(), + $this->inFooter + ); + $this->isRegistered = true; + + //Add pending JS variables and mark them as no longer pending. + foreach ($this->pendingJsVariables as $variableName => $unused) { + wp_add_inline_script( + $this->handle, + $this->generateJsVariableCode($variableName), + $this->jsVariables[$variableName]['position'] + ); + } + $this->pendingJsVariables = []; + + return $this; + } + + protected function canRegisterDepsNow() { + //If you register scripts or styles too early, or even *check* if they are registered + //too early, WordPress will complain that you're "doing it wrong". So we have to wait + //until the appropriate action has been fired. + //Optimization: Once the required action has been fired, we don't need to check again. + static $thresholdReached = false; + if ( $thresholdReached ) { + return true; + } + + $thresholdReached = ( + did_action('init') || did_action('wp_enqueue_scripts') + || did_action('admin_enqueue_scripts') || did_action('login_enqueue_scripts') + ); + return $thresholdReached; + } + + /** + * Check if the dependency has been registered with WordPress. + * + * @return bool + */ + public function isRegistered() { + if ( !$this->canRegisterDepsNow() ) { + return $this->isRegistered; + } + + $this->isRegistered = wp_script_is($this->handle, 'registered'); + return $this->isRegistered; + } + + /** + * Check if the dependency has been enqueued. + * + * @return bool + */ + public function isEnqueued() { + if ( !$this->canRegisterDepsNow() ) { + return $this->isEnqueued; + } + + /** @noinspection PhpRedundantOptionalArgumentInspection -- Let's make it explicit. */ + $this->isEnqueued = wp_script_is($this->handle, 'enqueued'); + return $this->isEnqueued; + } + + + /** + * Register the dependency during the appropriate action, or immediately if the action + * has already been fired. + * + * Note that this may never actually happen if the current request is not one + * that usually loads scripts and styles (e.g. a REST API request). If you still + * want to register a dependency in situations like that, add your own hook + * callback and use register() instead. + * + * @return $this + */ + public function autoRegister() { + $properAction = self::getRegistrationAction(); + if ( did_action($properAction) ) { + $this->register(); + } else { + self::$pendingRegistrations[$this->handle] = $this; + self::addAutoRegistrationHooks(); + } + return $this; + } + + protected static function getRegistrationAction() { + if ( is_admin() ) { + return 'admin_enqueue_scripts'; + } else if ( + (function_exists('is_login') && is_login()) + || (isset($GLOBALS['pagenow']) && ($GLOBALS['pagenow'] === 'wp-login.php')) + ) { + return 'login_enqueue_scripts'; + } else { + return 'wp_enqueue_scripts'; + } + } + + protected static function addAutoRegistrationHooks() { + if ( self::$autoRegistrationHooksAdded ) { + return; + } + + $targetAction = self::getRegistrationAction(); + add_action($targetAction, [__CLASS__, 'registerPendingDependencies'], self::AUTO_REGISTER_PRIORITY); + add_action($targetAction, [__CLASS__, 'enqueuePendingDependencies'], self::AUTO_ENQUEUE_PRIORITY); + self::$autoRegistrationHooksAdded = true; + } + + /** + * @internal + * @return void + */ + public static function registerPendingDependencies() { + foreach (self::$pendingRegistrations as $dependency) { + $dependency->register(); + } + self::$pendingRegistrations = []; + } + + /** + * @internal + * @return void + */ + public static function enqueuePendingDependencies() { + foreach (self::$pendingEnqueues as $dependency) { + $dependency->enqueue(); + } + self::$pendingEnqueues = []; + } + + /** + * Enqueue the dependency immediately. + * + * @return $this + */ + public function enqueue() { + $this->register(); + wp_enqueue_script($this->handle); + $this->isEnqueued = true; + return $this; + } + + /** + * Enqueue the dependency during the appropriate action, or immediately if the action + * has already been fired. + * + * @return $this + */ + public function autoEnqueue() { + $properAction = self::getRegistrationAction(); + if ( did_action($properAction) ) { + $this->enqueue(); + } else { + self::$pendingEnqueues[$this->handle] = $this; + self::addAutoRegistrationHooks(); + } + return $this; + } + + /** + * @param string $url + * @param string|null $handle Optional. If omitted, a handle will be generated automatically. + * @param string|null $fullPath + * @return static + */ + public static function create($url, $handle = null, $fullPath = null) { + if ( $handle === null ) { + $handle = self::generateHandle($url); + } + return new static($url, $handle, $fullPath); + } + + protected static function generateHandle($url) { + self::$generatedHandleCounter++; + $handle = 'wsdep-ah' . self::$generatedHandleCounter; + + $parsedUrl = wp_parse_url($url); + if ( empty($parsedUrl) || empty($parsedUrl['path']) ) { + return $handle; + } + + $path = $parsedUrl['path']; + $suffix = sanitize_key(pathinfo($path, PATHINFO_FILENAME)); + if ( !empty($suffix) ) { + $handle .= '-' . $suffix; + } + return $handle; + } +} \ No newline at end of file
    ')\n\t\t\t\t\t\t.attr('id', publishFailNoticeId)\n\t\t\t\t\t\t.addClass('notice notice-error is-dismissible')\n\t\t\t\t\t\t.text(message);\n\n\t\t\t\t\t//WordPress won't automatically add the dismiss button to a dynamically\n\t\t\t\t\t//generated notice like this, so we have to do it.\n\t\t\t\t\t$notice.append(\n\t\t\t\t\t\t$('')\n\t\t\t\t\t\t\t.append('Dismiss this notice')\n\t\t\t\t\t\t\t.on('click', (event) => {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t$notice.remove(); //Not as fancy as WP does it.\n\t\t\t\t\t\t\t})\n\t\t\t\t\t);\n\n\t\t\t\t\tconst $container = $('#ame-ac-global-notification-area');\n\t\t\t\t\t$container.append($notice);\n\t\t\t\t})\n\n\t\t\t\tpromise.done(() => {\n\t\t\t\t\tthis.$saveButton.val(this.$saveButton.data('published-text') ?? 'Saved');\n\n\t\t\t\t\t//The preview could be stale. For example, the color scheme module\n\t\t\t\t\t//switches between \"actual\" and \"preview\" color schemes dynamically,\n\t\t\t\t\t//but the \"actual\" scheme could change after applying new settings.\n\t\t\t\t\t//Let's reload the preview frame to make sure it's up-to-date.\n\t\t\t\t\tthis.queuePreviewFrameReload();\n\t\t\t\t});\n\n\t\t\t\tpromise.always(() => {\n\t\t\t\t\t$spinner.css('visibility', 'hidden');\n\t\t\t\t});\n\t\t\t});\n\n\t\t\t//Prevent the user from interacting with settings while the changeset is being modified.\n\t\t\tthis.settings.isExclusiveOperationInProgress.subscribe((isInProgress) => {\n\t\t\t\t$('#ame-ac-sidebar-blocker-overlay').toggle(isInProgress);\n\t\t\t});\n\n\t\t\t//Show a general overlay with a progress spinner while something is happening.\n\t\t\tthis.isGeneralOverlayVisible = ko.pureComputed(() => {\n\t\t\t\treturn this.isImporting() || this.isDiscardingChanges();\n\t\t\t});\n\n\t\t\t//Initialize the \"download admin theme\" dialog.\n\t\t\tthis.downloadThemeDialog = new DownloadThemeDialog(\n\t\t\t\t() => this.settings.getCurrentChangeset().name(),\n\t\t\t\t() => this.settings.savePendingSettings()\n\t\t\t);\n\n\t\t\t//Toggle available extra actions based on changeset status.\n\t\t\tthis.importActionEnabled = ko.pureComputed(() => {\n\t\t\t\tconst changeset = this.settings.getCurrentChangeset();\n\t\t\t\treturn changeset && changeset.canBeModified()\n\t\t\t\t\t&& !this.settings.isExclusiveOperationInProgress();\n\t\t\t});\n\t\t\tthis.importActionEnabled.subscribe((isEnabled) => {\n\t\t\t\tif (this.$extraActionMenu) {\n\t\t\t\t\tthis.$extraActionMenu.find('.ame-ac-import-theme-action')\n\t\t\t\t\t\t.toggleClass('ui-state-disabled', !isEnabled);\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.discardChangesActionEnabled = ko.pureComputed(() => {\n\t\t\t\tconst changeset = this.settings.getCurrentChangeset();\n\t\t\t\treturn changeset && changeset.isNonEmpty() && changeset.canBeModified()\n\t\t\t\t\t&& !this.settings.isExclusiveOperationInProgress()\n\t\t\t});\n\t\t\tthis.discardChangesActionEnabled.subscribe((isEnabled) => {\n\t\t\t\tif (this.$extraActionMenu) {\n\t\t\t\t\tthis.$extraActionMenu.find('.ame-ac-discard-changes-action')\n\t\t\t\t\t\t.toggleClass('ui-state-disabled', !isEnabled);\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.downloadThemeActionEnabled = ko.pureComputed(() => {\n\t\t\t\treturn !this.settings.isExclusiveOperationInProgress();\n\t\t\t});\n\t\t\tthis.downloadThemeActionEnabled.subscribe((isEnabled) => {\n\t\t\t\tif (this.$extraActionMenu) {\n\t\t\t\t\tthis.$extraActionMenu.find('.ame-ac-download-theme-action')\n\t\t\t\t\t\t.toggleClass('ui-state-disabled', !isEnabled);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tthis.sectionNavigation = new SectionNavigation();\n\n\t\t\t//Set up the preview frame.\n\t\t\tthis.$previewFrame = $('iframe#ame-ac-preview');\n\n\t\t\tthis.initialPreviewUrl = scriptData.initialPreviewUrl;\n\t\t\tthis.refreshPreviewNonce = scriptData.refreshPreviewNonce;\n\n\t\t\tthis.$previewFrame.on('load', () => {\n\t\t\t\tthis.isFrameLoading = false;\n\n\t\t\t\t//The URL that was actually loaded might not match the one that\n\t\t\t\t//was requested (e.g. because there was a redirect).\n\t\t\t\tthis.currentPreviewUrl = null;\n\n\t\t\t\t//Close the previous postMessage connection.\n\t\t\t\tif (this.previewConnection) {\n\t\t\t\t\tthis.previewConnection.disconnect();\n\t\t\t\t\tthis.previewConnection = null;\n\t\t\t\t}\n\n\t\t\t\tconst frame = this.$previewFrame.get(0) as HTMLIFrameElement;\n\t\t\t\tif (!frame || !(frame instanceof HTMLIFrameElement)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t//Try to get the preview URL from the iframe.\n\t\t\t\ttry {\n\t\t\t\t\tconst url = frame.contentWindow?.location.href;\n\t\t\t\t\tif (url) {\n\t\t\t\t\t\tthis.currentPreviewUrl = url;\n\t\t\t\t\t}\n\t\t\t\t} catch (e) {\n\t\t\t\t\t//We can't get the URL directly, probably because it's a cross-origin iframe.\n\t\t\t\t}\n\n\t\t\t\tthis.previewConnection = AmeAcCommunicator.connectToChild(\n\t\t\t\t\tframe,\n\t\t\t\t\t{\n\t\t\t\t\t\t'setPreviewUrl': (url: string) => {\n\t\t\t\t\t\t\tif (this.isPreviewableUrl(url)) {\n\t\t\t\t\t\t\t\tthis.previewUrl = url;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t'notifyPreviewUrlChanged': (url: string) => {\n\t\t\t\t\t\t\tthis.currentPreviewUrl = url;\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tthis.allowedCommOrigins,\n\t\t\t\t\tscriptData.isWpDebugEnabled\n\t\t\t\t);\n\n\t\t\t\tthis.previewConnection.promise.then((connection) => {\n\t\t\t\t\tif (typeof connection === 'undefined') {\n\t\t\t\t\t\t//This should never happen, but the type checker doesn't know that.\n\t\t\t\t\t\tthrow new Error('Unexpected error: Connection apparently succeeded, but the connection object is undefined');\n\t\t\t\t\t}\n\n\t\t\t\t\tconnection.execute('getCurrentUrl').then((url) => {\n\t\t\t\t\t\tif (url && (typeof url === 'string')) {\n\t\t\t\t\t\t\tthis.currentPreviewUrl = url;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\t//Notify other scripts that the preview frame is loaded and\n\t\t\t\t\t//the postMessage connection is ready for use.\n\t\t\t\t\t$('body').trigger('adminMenuEditor:acPreviewConnectionReady');\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tthis.previewUrl = this.initialPreviewUrl;\n\n\t\t\t//Notify other scripts. This lets them register custom controls and so on.\n\t\t\t$('#ame-ac-admin-customizer').trigger('adminMenuEditor:acRegister', [this]);\n\n\t\t\tconst throttledReloadPreview = _.throttle(\n\t\t\t\t() => {\n\t\t\t\t\tthis.queuePreviewFrameReload();\n\t\t\t\t},\n\t\t\t\t1000, //The reload method does its own throttling, so we use a low wait time here.\n\t\t\t\t{leading: true, trailing: true}\n\t\t\t);\n\n\t\t\t//Refresh the preview when any setting changes.\n\t\t\tthis.settings.addChangeListener((setting, newValue) => {\n\t\t\t\tif (\n\t\t\t\t\tsetting.supportsPostMessage\n\t\t\t\t\t&& this.previewConnection\n\t\t\t\t\t&& this.previewConnection.isConnected\n\t\t\t\t) {\n\t\t\t\t\tthis.previewConnection.execute('previewSetting', setting.id, newValue);\n\t\t\t\t} else {\n\t\t\t\t\tthrottledReloadPreview();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst registerUnloadPrompt = () => {\n\t\t\t\t//Ask for confirmation when the user tries to leave the page and the changeset\n\t\t\t\t//has unpublished changes.\n\t\t\t\t$(window).on('beforeunload.ame-ac-exit-confirm', (event) => {\n\t\t\t\t\tif (this.hasUnpublishedChanges()) {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t//Note: The confirmation prompt will only be displayed if the user\n\t\t\t\t\t\t//has interacted with the page (e.g. clicked something).\n\n\t\t\t\t\t\t//As of this writing, MDN says that some browsers still don't support triggering\n\t\t\t\t\t\t//an \"unsaved changes\" prompt with event.preventDefault(). You need to set\n\t\t\t\t\t\t//event.returnValue to a string or return a string from the event handler.\n\t\t\t\t\t\t//Modern browsers will ignore the content and display their own generic message.\n\t\t\t\t\t\treturn this.exitPromptMessage;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t/*\n\t\t\t Allegedly, registering a beforeunload handler can cause the browser to\n\t\t\t disable some optimizations, so let's only do it when the user changes\n\t\t\t something or the changeset already contains some changes.\n\t\t\t */\n\t\t\tif (this.settings.getCurrentChangeset().isNonEmpty()) {\n\t\t\t\tregisterUnloadPrompt();\n\t\t\t} else {\n\t\t\t\tconst listenerId = this.settings.addChangeListener(() => {\n\t\t\t\t\t//Remove the listener after it has been triggered once.\n\t\t\t\t\tthis.settings.removeChangeListener(listenerId);\n\t\t\t\t\tregisterUnloadPrompt();\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tgetSettingObservable(settingId: string, defaultValue: any): KnockoutObservable {\n\t\t\t//Let's just implement this temporarily while working on refactoring this\n\t\t\t//stuff to use KO components.\n\t\t\treturn this.settings\n\t\t\t\t.get(settingId)\n\t\t\t\t.map(setting => setting.value)\n\t\t\t\t.getOrElse(ko.observable(defaultValue));\n\t\t}\n\n\t\tgetAllSettingValues(): Record {\n\t\t\tthrow new Error('Method not implemented.');\n\t\t}\n\n\t\tget previewUrl(): string | null {\n\t\t\treturn this.currentPreviewUrl;\n\t\t}\n\n\t\tset previewUrl(url: string | null) {\n\t\t\tif (url === this.currentPreviewUrl) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t//The URL starts out as null, but it cannot be set to NULL again after\n\t\t\t//the preview frame has been loaded.\n\t\t\tif (url === null) {\n\t\t\t\tthrow new Error('Cannot directly set preview URL to null');\n\t\t\t}\n\n\t\t\tif (this.isPreviewableUrl(url)) {\n\t\t\t\tthis.navigatePreviewFrame(url);\n\t\t\t}\n\t\t}\n\n\t\tprivate navigatePreviewFrame(url: string | null = null, forceReload: boolean = false) {\n\t\t\tconst oldUrl = this.previewUrl;\n\t\t\tif (url === null) {\n\t\t\t\turl = oldUrl ?? this.initialPreviewUrl;\n\t\t\t}\n\n\t\t\tconst isSameUrl = (oldUrl === url);\n\t\t\tif (isSameUrl && !forceReload) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t//If there are any unsaved changes, let's include them in the preview by simulating\n\t\t\t//a form submission and sending the changes as form data. The server-side component\n\t\t\t//will merge these changes with existing changeset data.\n\t\t\tconst unsavedChanges = this.settings.unsavedChanges;\n\t\t\tconst simulateFormSubmission = !_.isEmpty(unsavedChanges);\n\n\t\t\tconst parsedUrl = new URL(url);\n\n\t\t\t//If we're not using form submission, add a special parameter\n\t\t\t//to the URL to force a refresh.\n\t\t\tconst refreshParam = '_ame-ac-refresh-trigger';\n\t\t\tif (isSameUrl && !simulateFormSubmission) {\n\t\t\t\tparsedUrl.searchParams.set(refreshParam, Date.now() + '_' + Math.random());\n\t\t\t} else {\n\t\t\t\t//Otherwise, remove the parameter just to be safe.\n\t\t\t\tparsedUrl.searchParams.delete(refreshParam);\n\t\t\t}\n\n\t\t\t//Ensure that the changeset used in the preview matches the current\n\t\t\t//changeset and preview is enabled. This is just a precaution. Normally,\n\t\t\t//the preview script automatically changes link URLs.\n\t\t\tparsedUrl.searchParams.set('ame-ac-changeset', this.settings.changesetName());\n\t\t\tparsedUrl.searchParams.set('ame-ac-preview', '1');\n\n\t\t\tthis.hasPendingPreviewReload = false; //Reloading now, so no longer pending.\n\t\t\tthis.isFrameLoading = true;\n\n\t\t\t//console.info('navigatePreviewFrame: Navigating to ' + parsedUrl.href);\n\t\t\tif (simulateFormSubmission) {\n\t\t\t\tconst formData = {\n\t\t\t\t\taction: 'ws_ame_ac_refresh_preview_frame',\n\t\t\t\t\t\"ame-ac-changeset\": this.settings.changesetName(),\n\t\t\t\t\tmodified: JSON.stringify(unsavedChanges),\n\t\t\t\t\tnonce: this.refreshPreviewNonce\n\t\t\t\t}\n\n\t\t\t\tconst $form = $('')\n\t\t\t\t\t.attr('method', 'post')\n\t\t\t\t\t.attr('action', parsedUrl.href)\n\t\t\t\t\t.attr('target', 'ame-ac-preview-frame')\n\t\t\t\t\t.appendTo('body');\n\n\t\t\t\tlet key: keyof typeof formData;\n\t\t\t\tfor (key in formData) {\n\t\t\t\t\tconst value = formData[key];\n\t\t\t\t\t$('')\n\t\t\t\t\t\t.attr('type', 'hidden')\n\t\t\t\t\t\t.attr('name', key)\n\t\t\t\t\t\t.val(value)\n\t\t\t\t\t\t.appendTo($form);\n\t\t\t\t}\n\n\t\t\t\tthis.currentPreviewUrl = parsedUrl.href;\n\t\t\t\t$form.trigger('submit');\n\t\t\t\t$form.remove();\n\t\t\t} else {\n\t\t\t\tthis.currentPreviewUrl = parsedUrl.href;\n\t\t\t\tthis.$previewFrame.attr('src', this.currentPreviewUrl);\n\t\t\t}\n\t\t}\n\n\t\tprivate _isFrameLoading: boolean = false;\n\t\tprivate frameLoadingTimeoutId: number | null = null;\n\t\tprivate lastPreviewLoadTimestamp: Date = new Date(0);\n\n\t\tprivate reloadWaitTimeoutId: number | null = null;\n\t\tprivate hasPendingPreviewReload: boolean = false;\n\n\t\tprivate set isFrameLoading(isLoading: boolean) {\n\t\t\tconst wasLoadingBefore = this._isFrameLoading;\n\t\t\tif (!isLoading && (isLoading === wasLoadingBefore)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t//In some circumstances, we may start to load a new URL before\n\t\t\t//the previous one has finished loading. This is valid and should\n\t\t\t//reset the load timeout.\n\n\t\t\t$('#ame-ac-preview-refresh-indicator').toggleClass('ame-ac-show-indicator', isLoading);\n\t\t\tif (this.frameLoadingTimeoutId) {\n\t\t\t\tclearTimeout(this.frameLoadingTimeoutId);\n\t\t\t\tthis.frameLoadingTimeoutId = null;\n\t\t\t}\n\n\t\t\tif (isLoading) {\n\t\t\t\t//As a precaution, we'll assume that if the frame doesn't load in a reasonable\n\t\t\t\t//time, it will never finish loading.\n\t\t\t\tthis.frameLoadingTimeoutId = window.setTimeout(() => {\n\t\t\t\t\tif (this.isFrameLoading) {\n\t\t\t\t\t\tthis.isFrameLoading = false;\n\t\t\t\t\t}\n\t\t\t\t}, 20000);\n\t\t\t}\n\t\t\tthis._isFrameLoading = isLoading;\n\n\t\t\tif (wasLoadingBefore && !isLoading) {\n\t\t\t\tthis.lastPreviewLoadTimestamp = new Date();\n\t\t\t}\n\n\t\t\t//Once the frame is loaded, trigger any pending reload.\n\t\t\tif (!isLoading && this.hasPendingPreviewReload) {\n\t\t\t\tthis.hasPendingPreviewReload = false;\n\t\t\t\tthis.queuePreviewFrameReload();\n\t\t\t}\n\t\t}\n\n\t\tpublic get isFrameLoading(): boolean {\n\t\t\treturn this._isFrameLoading;\n\t\t}\n\n\t\tprivate queuePreviewFrameReload() {\n\t\t\tif (this.reloadWaitTimeoutId) {\n\t\t\t\treturn; //The frame will reload soon.\n\t\t\t}\n\n\t\t\tif (this.isFrameLoading) {\n\t\t\t\tthis.hasPendingPreviewReload = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t//To avoid stressing the server, wait at least X ms after the last\n\t\t\t//load completes before reloading the frame.\n\t\t\tconst reloadWaitTime = 2000;\n\t\t\tconst now = new Date();\n\t\t\tconst timeSinceLastLoad = now.getTime() - this.lastPreviewLoadTimestamp.getTime();\n\t\t\tif (timeSinceLastLoad < reloadWaitTime) {\n\t\t\t\tthis.reloadWaitTimeoutId = window.setTimeout(() => {\n\t\t\t\t\tthis.reloadWaitTimeoutId = null;\n\t\t\t\t\tthis.queuePreviewFrameReload();\n\t\t\t\t}, reloadWaitTime - timeSinceLastLoad);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t//Actually reload the frame.\n\t\t\tthis.navigatePreviewFrame(null, true);\n\t\t}\n\n\t\tonBindingsApplied(rootElement: HTMLElement) {\n\t\t\t//Navigate to the root section. In the current implementation this can't happen\n\t\t\t//until bindings have been applied, so it's not part of the constructor.\n\t\t\tthis.navigateToRootSection();\n\n\t\t\t//Initialize the action menu.\n\t\t\tthis.$extraActionButton = jQuery('#ame-ac-extra-actions-trigger', rootElement);\n\t\t\tthis.$extraActionMenu = jQuery('#ame-ac-extra-actions-menu', rootElement).menu();\n\n\t\t\t//Update menu states.\n\t\t\tthis.importActionEnabled.notifySubscribers(this.importActionEnabled());\n\t\t\tthis.discardChangesActionEnabled.notifySubscribers(this.discardChangesActionEnabled());\n\n\t\t\t//Get the file picker.\n\t\t\tthis.$importFileInput = jQuery('#ame-ac-import-admin-theme-file', rootElement);\n\t\t}\n\n\t\tnavigateToRootSection() {\n\t\t\tthis.sectionNavigation.navigateToSection('ame-ac-section-structure-root');\n\t\t}\n\n\t\t// noinspection JSUnusedGlobalSymbols -- Used in at least one add-on.\n\t\t/**\n\t\t * Execute an RPC method in the preview frame.\n\t\t *\n\t\t * @param {string} methodName\n\t\t * @param {*} args\n\t\t */\n\t\texecuteRpcMethod(methodName: string, ...args: any): JQueryPromise {\n\t\t\tif (!this.previewConnection || !this.previewConnection.isConnected) {\n\t\t\t\treturn $.Deferred().reject('The preview frame is not connected.').promise();\n\t\t\t}\n\t\t\treturn this.previewConnection.execute(methodName, ...args);\n\t\t}\n\n\t\tconfirmExit() {\n\t\t\tif (this.hasUnpublishedChanges()) {\n\t\t\t\tif (window.confirm(this.exitPromptMessage)) {\n\t\t\t\t\t//Remove the confirmation prompt that appears when leaving the page.\n\t\t\t\t\t//We don't want to show two prompts.\n\t\t\t\t\t$(window).off('beforeunload.ame-ac-exit-confirm');\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate hasUnpublishedChanges(): boolean {\n\t\t\tconst changeset = this.settings.getCurrentChangeset();\n\t\t\treturn (\n\t\t\t\tchangeset.isNonEmpty()\n\t\t\t\t&& !changeset.wasPublished()\n\t\t\t\t&& (changeset.status() !== 'trash') //Can't publish a trashed changeset.\n\t\t\t);\n\t\t}\n\n\t\t// noinspection JSUnusedGlobalSymbols -- Used in the Knockout template.\n\t\ttoggleExtraActionMenu() {\n\t\t\tif (!this.$extraActionMenu) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.$extraActionMenu.toggle();\n\n\t\t\tif (this.$extraActionMenu.is(':visible')) {\n\t\t\t\t//Position the menu below the button.\n\t\t\t\tconst $button = $('#ame-ac-extra-actions-trigger');\n\t\t\t\tthis.$extraActionMenu.position({\n\t\t\t\t\tmy: 'right top',\n\t\t\t\t\tat: 'right bottom',\n\t\t\t\t\tof: $button,\n\t\t\t\t\tcollision: 'flipfit'\n\t\t\t\t});\n\n\t\t\t\t//Hide the menu when the user clicks outside the menu or the button.\n\t\t\t\t$(document).on('mousedown.ameAcExtraMenuHide', this.handleClickOutsideActionMenu.bind(this));\n\t\t\t} else {\n\t\t\t\t//Remove the click listener if it's still active.\n\t\t\t\t$(document).off('mousedown.ameAcExtraMenuHide');\n\t\t\t}\n\t\t}\n\n\t\thandleClickOutsideActionMenu(event: JQueryEventObject) {\n\t\t\tif (\n\t\t\t\t!this.$extraActionMenu\n\t\t\t\t|| !this.$extraActionMenu.is(':visible')\n\t\t\t\t|| !this.$extraActionButton\n\t\t\t) {\n\t\t\t\t//The event listener should not be active if the menu is not visible.\n\t\t\t\t$(document).off('mousedown.ameAcExtraMenuHide');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst menuElement = this.$extraActionMenu.get(0);\n\t\t\tconst buttonElement = this.$extraActionButton.get(0);\n\t\t\tconst isClickOutsideMenu = !menuElement.contains(event.target);\n\t\t\tconst isClickOutsideButton = !buttonElement.contains(event.target);\n\n\t\t\tif (isClickOutsideMenu && isClickOutsideButton) {\n\t\t\t\tthis.hideExtraActionMenu();\n\t\t\t}\n\t\t}\n\n\t\tprivate hideExtraActionMenu() {\n\t\t\tif (!this.$extraActionMenu) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.$extraActionMenu.hide();\n\t\t\t//Stop listening for clicks outside the menu.\n\t\t\t$(document).off('mousedown.ameAcExtraMenuHide');\n\t\t}\n\n\t\tactionOpenDownloadDialog() {\n\t\t\tif (!this.downloadThemeActionEnabled()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.downloadThemeDialog.isOpen(true);\n\t\t\tthis.isImportReportVisible(false);\n\t\t\tthis.hideExtraActionMenu();\n\t\t}\n\n\t\tactionOpenImportDialog() {\n\t\t\tif (!this.importActionEnabled()) {\n\t\t\t\t//Can't import if there is no changeset or the changeset can't be edited.\n\t\t\t\t//The menu item should be disabled in this case, but we'll check anyway.\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tthis.hideExtraActionMenu();\n\n\t\t\t//Allow the default action to proceed, which will open the file picker.\n\t\t\treturn true;\n\t\t}\n\n\t\tactionDiscardChanges() {\n\t\t\tif (!this.discardChangesActionEnabled()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.hideExtraActionMenu();\n\n\t\t\tif (this.settings.isExclusiveOperationInProgress()) {\n\t\t\t\talert('Another operation is in progress. Please wait for it to complete before discarding changes.');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!confirm('Are you sure you want to discard your unsaved changes?')) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.isImportReportVisible(false);\n\t\t\tthis.isDiscardingChanges(true);\n\n\t\t\tthis.settings.trashChangeset()\n\t\t\t\t.then(() => {\n\t\t\t\t\t//Reload the customizer with a new changeset.\n\n\t\t\t\t\t//First, to get the customizer's base URL, get the current URL\n\t\t\t\t\t//and remove all query parameters except \"page\".\n\t\t\t\t\tconst url = new URL(window.location.href);\n\t\t\t\t\tconst page = url.searchParams.get('page');\n\t\t\t\t\turl.search = '';\n\t\t\t\t\turl.searchParams.set('page', page || 'ame-admin-customizer');\n\t\t\t\t\t//Don't need the hash either.\n\t\t\t\t\turl.hash = '';\n\n\t\t\t\t\t//Add a random parameter to force a reload.\n\t\t\t\t\turl.searchParams.set('_ame-ac-reload', Math.random().toString(36).substring(7));\n\n\t\t\t\t\t//Navigate to the new URL.\n\t\t\t\t\twindow.location.href = url.toString();\n\n\t\t\t\t\t//Note that the isDiscardingChanges flag is not reset here,\n\t\t\t\t\t//so the progress overlay will stay visible until the page reloads.\n\t\t\t\t})\n\t\t\t\t.fail((requestObject) => {\n\t\t\t\t\tlet message: string = requestObject.statusText || 'Unknown error.';\n\n\t\t\t\t\tif (typeof requestObject.responseJSON === 'object') {\n\t\t\t\t\t\tconst customMessage = _.get(requestObject.responseJSON, ['data', 'message']);\n\t\t\t\t\t\tif (typeof customMessage === 'string') {\n\t\t\t\t\t\t\tmessage = customMessage;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\talert('Error: ' + message);\n\t\t\t\t\tthis.isDiscardingChanges(false);\n\t\t\t\t});\n\t\t}\n\n\t\thandleImportFileSelection() {\n\t\t\tif (!this.$importFileInput) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst fileInput = this.$importFileInput.get(0) as HTMLInputElement;\n\t\t\tif (!fileInput || !fileInput.files || (fileInput.files.length < 1)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t//Get the first file. Normally, there should only be one.\n\t\t\tconst selectedFile = fileInput.files.item(0);\n\t\t\tif (!selectedFile) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t//Limit the file size.\n\t\t\tif (selectedFile.size > this.maxImportFileSize) {\n\t\t\t\talert(\n\t\t\t\t\t'Error: The selected file is too large. The maximum file size is '\n\t\t\t\t\t+ Math.round(this.maxImportFileSize / 1024) + ' KiB'\n\t\t\t\t);\n\t\t\t\t//Clear the file input.\n\t\t\t\tthis.$importFileInput.val('');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.isImporting(true);\n\t\t\tthis.lastImportReport(null);\n\n\t\t\tJSZip.loadAsync(selectedFile).then(\n\t\t\t\t(zip) => {\n\t\t\t\t\tconst metadataFileRegex = /^([\\\\/]?[a-zA-Z0-9_-]+[\\\\/])metadata\\.json$/;\n\t\t\t\t\tconst foundMetadataFiles = zip.file(metadataFileRegex);\n\t\t\t\t\tif (!foundMetadataFiles || (foundMetadataFiles.length < 1)) {\n\t\t\t\t\t\tthrow new Error('The selected file is not an admin theme generated by this tool.');\n\t\t\t\t\t}\n\t\t\t\t\tconst metadataFile = foundMetadataFiles[0];\n\n\t\t\t\t\t//Get the directory name and separator from the metadata file path.\n\t\t\t\t\t//The prefix will usually be something like \"admin-theme-slug/\".\n\t\t\t\t\tconst matches = metadataFileRegex.exec(metadataFile.name);\n\t\t\t\t\tlet directoryPrefix: string;\n\t\t\t\t\tif (!matches || (matches.length < 2)) {\n\t\t\t\t\t\tthrow new Error('The directory structure of this ZIP file is not recognized.');\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdirectoryPrefix = matches[1];\n\t\t\t\t\t}\n\n\t\t\t\t\tconst settingsFile = zip.file(directoryPrefix + 'settings.json');\n\t\t\t\t\tif (!settingsFile) {\n\t\t\t\t\t\tthrow new Error('The selected ZIP file is missing a settings.json file.');\n\t\t\t\t\t}\n\n\t\t\t\t\t//Read both files.\n\t\t\t\t\treturn Promise.all([\n\t\t\t\t\t\tmetadataFile.async('string'),\n\t\t\t\t\t\tsettingsFile.async('string')\n\t\t\t\t\t]);\n\t\t\t\t},\n\t\t\t\t(error) => {\n\t\t\t\t\tconst errorMessage = error.message || error;\n\t\t\t\t\tthrow new Error('Error reading \"' + selectedFile.name + '\": ' + errorMessage);\n\t\t\t\t}\n\t\t\t).then((fileContents) => {\n\t\t\t\tif (!fileContents) {\n\t\t\t\t\tthrow new Error('Failed to read settings and metadata from the ZIP file.');\n\t\t\t\t}\n\n\t\t\t\tconst metadata = this.parseImportedAdminThemeFile(\n\t\t\t\t\tfileContents[0],\n\t\t\t\t\t'metadata.json',\n\t\t\t\t\tAdminThemeMetadata\n\t\t\t\t);\n\t\t\t\tconst settings = this.parseImportedAdminThemeFile(\n\t\t\t\t\tfileContents[1],\n\t\t\t\t\t'settings.json',\n\t\t\t\t\tAdminThemeSettings\n\t\t\t\t);\n\t\t\t\tconst report = new AdminThemeImportReport(selectedFile.name, metadata);\n\n\t\t\t\t//Import metadata.\n\t\t\t\tthis.downloadThemeDialog.meta(new ObservableThemeMetadata(metadata));\n\n\t\t\t\t//Import settings.\n\t\t\t\tfor (const [settingId, value] of Object.entries(settings)) {\n\t\t\t\t\treport.totalSettings++;\n\n\t\t\t\t\tconst foundSetting = this.settings.get(settingId);\n\t\t\t\t\tfoundSetting.forEach((setting) => {\n\t\t\t\t\t\tconst oldValue = setting.value();\n\t\t\t\t\t\tconst errors = setting.tryUpdate(value);\n\t\t\t\t\t\tif (errors && errors.length) {\n\t\t\t\t\t\t\treport.invalidSettings++;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treport.importedSettings++;\n\t\t\t\t\t\t\tif (oldValue != value) {\n\t\t\t\t\t\t\t\treport.differentImportedSettings++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\tif (foundSetting.isEmpty()) {\n\t\t\t\t\t\treport.skippedSettings++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.lastImportReport(report);\n\t\t\t\tthis.isImportReportVisible(true);\n\n\t\t\t}).catch((error) => {\n\t\t\t\t//Error handling: Show the error message to the user.\n\t\t\t\tlet errorMessage: string;\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\terrorMessage = error.message;\n\t\t\t\t} else {\n\t\t\t\t\terrorMessage = String(error);\n\t\t\t\t}\n\t\t\t\talert('Error: ' + errorMessage);\n\t\t\t}).finally(() => {\n\t\t\t\tthis.isImporting(false);\n\t\t\t\tthis.$importFileInput?.val('');\n\t\t\t});\n\t\t}\n\n\t\tprivate parseImportedAdminThemeFile(\n\t\t\tcontent: string,\n\t\t\tname: string,\n\t\t\tschema: T\n\t\t): ReturnType {\n\t\t\ttry {\n\t\t\t\tconst parsedJson = JSON.parse(content);\n\t\t\t\treturn schema.parse(parsedJson);\n\t\t\t} catch (error) {\n\t\t\t\tlet errorMessage: string;\n\t\t\t\tif (error instanceof ZodError) {\n\t\t\t\t\t//Convert issues to a newline-separated string.\n\t\t\t\t\terrorMessage = error.issues.map((issue) => {\n\t\t\t\t\t\treturn issue.path.join('.') + ': ' + issue.message;\n\t\t\t\t\t}).join('\\n');\n\t\t\t\t} else if (error instanceof Error) {\n\t\t\t\t\terrorMessage = error.message;\n\t\t\t\t} else {\n\t\t\t\t\terrorMessage = String(error);\n\t\t\t\t}\n\t\t\t\t//Add the file name to the error message.\n\t\t\t\tthrow new Error('Error parsing ' + name + ':\\n' + errorMessage);\n\t\t\t}\n\t\t}\n\n\t\tdismissImportReport(): void {\n\t\t\tthis.isImportReportVisible(false);\n\t\t}\n\t}\n}\n\ndeclare global {\n\tinterface Window {\n\t\twsAdminCustomizer: AmeAdminCustomizer.AdminCustomizer;\n\t}\n}\n\njQuery(function () {\n\t//Give other scripts a chance to load before we start.\n\t//Some of them also use jQuery to run when the DOM is ready.\n\tsetTimeout(() => {\n\t\twindow.wsAdminCustomizer = new AmeAdminCustomizer.AdminCustomizer(wsAmeAdminCustomizerData);\n\t\tconst rootElement = document.getElementById('ame-ac-admin-customizer');\n\t\tif (rootElement === null) {\n\t\t\tthrow new Error('The root element for the admin customizer was not found.');\n\t\t}\n\n\t\tko.applyBindings(window.wsAdminCustomizer, rootElement);\n\n\t\t//Notify the customizer that bindings have been applied. It needs to do some\n\t\t//additional setup that can't be done until the DOM structure is ready.\n\t\tsetTimeout(() => {\n\t\t\twindow.wsAdminCustomizer.onBindingsApplied(rootElement);\n\t\t}, 5); //Components are rendered asynchronously.\n\t}, 20);\n});","'use strict';\r\nexport var AmeAdminCustomizerBase;\r\n(function (AmeAdminCustomizerBase) {\r\n class AdminCustomizerBase {\r\n constructor(scriptData) {\r\n this.allowedCommOrigins = scriptData.allowedCommOrigins;\r\n if (this.allowedCommOrigins.length === 0) {\r\n this.allowedCommOrigins = [window.location.origin];\r\n }\r\n this.allowedPreviewUrls = scriptData.allowedPreviewUrls;\r\n this.parsedAllowedUrls = this.allowedPreviewUrls.map(url => new URL(url));\r\n }\r\n isPreviewableUrl(url) {\r\n if (typeof url === 'string') {\r\n url = new URL(url);\r\n }\r\n if (typeof url.protocol === 'undefined') {\r\n return false;\r\n }\r\n //Only HTTP(S) links are previewable.\r\n if ((url.protocol !== 'http:') && (url.protocol !== 'https:')) {\r\n return false;\r\n }\r\n //Check against the list of allowed URLs.\r\n for (const allowedUrl of this.parsedAllowedUrls) {\r\n //Protocol and host must match. The path must start with the path\r\n //of the allowed URL (possibly without a trailing slash).\r\n if ((url.protocol === allowedUrl.protocol) && (url.host === allowedUrl.host)) {\r\n const allowedPath = allowedUrl.pathname.replace(/\\/$/, '');\r\n if (url.pathname.indexOf(allowedPath) === 0) {\r\n return true;\r\n }\r\n }\r\n }\r\n return false;\r\n }\r\n }\r\n AmeAdminCustomizerBase.AdminCustomizerBase = AdminCustomizerBase;\r\n})(AmeAdminCustomizerBase || (AmeAdminCustomizerBase = {}));\r\n//# sourceMappingURL=admin-customizer-base.js.map","import { AmeAcSection } from './ame-ac-section.js';\r\nimport { createComponentConfig } from '../../../pro-customizables/ko-components/control-base.js';\r\nclass AmeAcContentSection extends AmeAcSection {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n if ((typeof params.parentSectionLevel === 'function') && ko.isObservable(params.parentSectionLevel)) {\r\n this.parentSectionLevel = params.parentSectionLevel;\r\n }\r\n else {\r\n this.parentSectionLevel = null;\r\n }\r\n this.contentSectionLevel = ko.pureComputed(() => {\r\n let parentLevel = 0;\r\n if (this.parentSectionLevel !== null) {\r\n parentLevel = this.parentSectionLevel();\r\n }\r\n return parentLevel + 1;\r\n });\r\n //Tell child sections about our section level.\r\n this.childComponents().forEach((child) => {\r\n if (child.name === 'ame-ac-content-section') {\r\n child.params.parentSectionLevel = this.contentSectionLevel;\r\n }\r\n });\r\n this.sectionLevelClass = ko.pureComputed(() => {\r\n const level = this.contentSectionLevel();\r\n return 'ame-ac-content-section-' + level;\r\n });\r\n }\r\n}\r\nexport default createComponentConfig(AmeAcContentSection, `\n\t
  • \n\t\t

    \t\n\t
  • \t\n\t\n\t\t\n\t\t\n`);\r\n//# sourceMappingURL=ame-ac-content-section.js.map","import { ComponentBindingOptions, createComponentConfig, KoContainerViewModel } from '../../../pro-customizables/ko-components/control-base.js';\r\nimport { AmeCustomizable } from '../../../pro-customizables/assets/customizable.js';\r\nvar ControlGroup = AmeCustomizable.ControlGroup;\r\nclass AmeAcControlGroup extends KoContainerViewModel {\r\n constructor(params, $element) {\r\n var _a, _b;\r\n super(params, $element);\r\n this.labelFor = (_b = ((_a = this.uiElement) === null || _a === void 0 ? void 0 : _a.labelFor)) !== null && _b !== void 0 ? _b : null;\r\n this.titleDisabled = (typeof params.titleDisabled !== 'undefined') ? (!!params.titleDisabled) : false;\r\n }\r\n getExpectedUiElementType() {\r\n return ControlGroup;\r\n }\r\n mapChildToComponentBinding(child) {\r\n if (child.component) {\r\n return ComponentBindingOptions.fromElement(child);\r\n }\r\n return super.mapChildToComponentBinding(child);\r\n }\r\n}\r\nexport default createComponentConfig(AmeAcControlGroup, `\n\t
  • \n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t
      \n\t\t\t
    • \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t
    • \t\t\n\t\t
    \n\t
  • \n`);\r\n//# sourceMappingURL=ame-ac-control-group.js.map","import { createComponentConfig, KoControlViewModel } from '../../../pro-customizables/ko-components/control-base.js';\r\nimport { AmeCustomizable } from '../../../pro-customizables/assets/customizable.js';\r\nvar Control = AmeCustomizable.Control;\r\nclass MissingComponentError extends Error {\r\n constructor(uiElement) {\r\n super(`The UI element \"${uiElement.label}\" [${uiElement.id}] is missing a component name.`);\r\n this.uiElement = uiElement;\r\n }\r\n}\r\nclass AmeAcControl extends KoControlViewModel {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n //uiElement is required for this component.\r\n if (!this.uiElement) {\r\n throw new Error('The uiElement parameter is required for AmeAcControl');\r\n }\r\n this.wrapperLabelEnabled = (this.uiElement.label !== '') && (!this.uiElement.includesOwnLabel);\r\n this.labelForId = this.uiElement.labelTargetId;\r\n if (!this.uiElement.component) {\r\n throw new MissingComponentError(this.uiElement);\r\n }\r\n }\r\n getExpectedUiElementType() {\r\n return Control;\r\n }\r\n}\r\nexport default createComponentConfig(AmeAcControl, `\n\t
  • \n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t
  • \n`);\r\n//# sourceMappingURL=ame-ac-control.js.map","import { createComponentConfig, KoContainerViewModel } from '../../../pro-customizables/ko-components/control-base.js';\r\nimport { AmeCustomizable } from '../../../pro-customizables/assets/customizable.js';\r\nvar Section = AmeCustomizable.Section;\r\nimport { AmeAcSection } from './ame-ac-section.js';\r\nclass AmeAcSectionLink extends KoContainerViewModel {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n //uiElement is required for this component.\r\n if (!this.uiElement) {\r\n throw new Error('The uiElement parameter is required for AmeAcSectionLink');\r\n }\r\n this.targetElementId = AmeAcSection.getSectionElementId(this.uiElement);\r\n }\r\n getExpectedUiElementType() {\r\n return Section;\r\n }\r\n}\r\nexport default createComponentConfig(AmeAcSectionLink, `\n\t
  • \n\t\t

    \n\t
  • \n`);\r\n//# sourceMappingURL=ame-ac-section-link.js.map","import { ComponentBindingOptions, createComponentConfig, KoContainerViewModel } from '../../../pro-customizables/ko-components/control-base.js';\r\nimport { AmeCustomizable } from '../../../pro-customizables/assets/customizable.js';\r\nvar Section = AmeCustomizable.Section;\r\nvar Control = AmeCustomizable.Control;\r\nvar ControlGroup = AmeCustomizable.ControlGroup;\r\nexport class AmeAcSection extends KoContainerViewModel {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n //Must have an uiElement.\r\n if (this.uiElement === null) {\r\n throw new Error('AmeAcSection must have an uiElement.');\r\n }\r\n this.elementId = AmeAcSection.getSectionElementId(this.uiElement);\r\n if ((typeof params.breadcrumbs !== 'undefined') && ko.isObservable(params.breadcrumbs)) {\r\n this.breadcrumbs = params.breadcrumbs;\r\n }\r\n else {\r\n this.breadcrumbs = null;\r\n }\r\n //To keep the header text alignment consistent when navigating between sections,\r\n //let's show something even if there are no breadcrumbs.\r\n const emptyBreadcrumbText = 'Admin Menu Editor Pro';\r\n this.breadcrumbText = ko.pureComputed(() => {\r\n if (this.breadcrumbs === null) {\r\n return emptyBreadcrumbText;\r\n }\r\n const breadcrumbs = this.breadcrumbs();\r\n if (breadcrumbs.length < 1) {\r\n return emptyBreadcrumbText;\r\n }\r\n let titles = breadcrumbs.map(crumb => crumb.title);\r\n //Show the root section differently, \"Admin Customizer\" is too long.\r\n //Not sure about what text to use here, could matching the Theme Customizer be confusing?\r\n //Alternatives: 🛠️🎨, use \\uFE0E to render the emoji without colors (only works for some).\r\n //Alternatives: ⋯ and …\r\n titles[0] = 'Customizing';\r\n //Due to space constraints, show only the last 2 breadcrumbs.\r\n if (titles.length > 2) {\r\n titles = titles.slice(titles.length - 2);\r\n }\r\n return titles.join(' \\u25B8 ');\r\n });\r\n }\r\n getExpectedUiElementType() {\r\n return Section;\r\n }\r\n mapChildToComponentBinding(child) {\r\n if (child instanceof Section) {\r\n if (child.preferredRole === 'content') {\r\n return ComponentBindingOptions.fromElement(child, 'ame-ac-content-section');\r\n }\r\n else {\r\n return ComponentBindingOptions.fromElement(child, 'ame-ac-section-link');\r\n }\r\n }\r\n else if (child instanceof ControlGroup) {\r\n return ComponentBindingOptions.fromElement(child, 'ame-ac-control-group');\r\n }\r\n else if ((child instanceof Control)\r\n && (['ame-ac-separator', 'ame-horizontal-separator'].indexOf(child.component) < 0)) {\r\n //Wrap each control in a control group if it's not already in one.\r\n //Separators are an exception because they're cosmetic and need different styling.\r\n const controlGroup = child.createControlGroup();\r\n return this.mapChildToComponentBinding(controlGroup);\r\n }\r\n else {\r\n return ComponentBindingOptions.fromElement(child);\r\n }\r\n }\r\n static getSectionElementId(section) {\r\n const prefix = 'ame-ac-section-';\r\n if (section.id) {\r\n return prefix + section.id;\r\n }\r\n const slug = section.title.toLowerCase().replace(/[^a-z0-9]/g, '-');\r\n if (slug !== '') {\r\n return prefix + slug;\r\n }\r\n throw new Error('Cannot generate a section element ID because the section does not have an ID or a title.');\r\n }\r\n dispose() {\r\n super.dispose();\r\n this.childComponents.dispose();\r\n }\r\n}\r\nexport default createComponentConfig(AmeAcSection, `\n\t
      \n\t\t\n\t\t\n\t\t\t\n\t\t\n\t
    \n`);\r\n//# sourceMappingURL=ame-ac-section.js.map","import { createComponentConfig, KoStandaloneControl } from '../../../pro-customizables/ko-components/control-base.js';\r\nclass AmeAcSeparator extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n }\r\n}\r\nexport default createComponentConfig(AmeAcSeparator, `\n\t
  • \n`);\r\n//# sourceMappingURL=ame-ac-separator.js.map","import { createRendererComponentConfig, KoRendererViewModel } from '../../../pro-customizables/ko-components/control-base.js';\r\nimport { AmeCustomizable } from '../../../pro-customizables/assets/customizable.js';\r\nvar Section = AmeCustomizable.Section;\r\nclass AmeAcStructure extends KoRendererViewModel {\r\n constructor(params, $element) {\r\n var _a;\r\n super(params, $element);\r\n this.allSections = [];\r\n const rootSection = new Section({\r\n t: 'section',\r\n id: 'structure-root',\r\n title: (_a = this.structure.title) !== null && _a !== void 0 ? _a : 'Root',\r\n }, this.structure.children);\r\n //Recursively collect all sections.\r\n function collectChildSections(section, accumulator = []) {\r\n accumulator.push(section);\r\n for (const child of section.children) {\r\n if (child instanceof Section) {\r\n collectChildSections(child, accumulator);\r\n }\r\n }\r\n return accumulator;\r\n }\r\n this.allSections = collectChildSections(rootSection);\r\n //Give the breadcrumb list to each section, if available.\r\n if (typeof params.breadcrumbs !== 'undefined') {\r\n for (const section of this.allSections) {\r\n section.componentParams.breadcrumbs = params.breadcrumbs;\r\n }\r\n }\r\n }\r\n}\r\nexport default createRendererComponentConfig(AmeAcStructure, `\n\t\n\t\t\n\t\n`);\r\n//# sourceMappingURL=ame-ac-structure.js.map","import { createComponentConfig, KoStandaloneControl } from '../../../pro-customizables/ko-components/control-base.js';\r\nclass AmeAcValidationErrors extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n if (typeof params.errors !== 'undefined') {\r\n if (Array.isArray(params.errors)) {\r\n this.errors = params.errors;\r\n }\r\n else if (ko.isObservable(params.errors)) {\r\n this.errors = params.errors;\r\n }\r\n else {\r\n throw new Error('The \"errors\" parameter must be an array or an observable array.');\r\n }\r\n }\r\n else {\r\n console.log('Params:', params);\r\n throw new Error('The \"errors\" parameter is required for the AmeAcValidationErrors component.');\r\n }\r\n }\r\n}\r\nexport default createComponentConfig(AmeAcValidationErrors, `\n\t
      \n\t\t
    • \n\t\t\t\n\t\t
    • \n\t
    \n`);\r\n//# sourceMappingURL=ame-ac-validation-errors.js.map","'use strict';\r\nimport { createControlComponentConfig, KoStandaloneControl } from '../control-base.js';\r\nimport { LazyPopupSliderAdapter } from '../lazy-popup-slider-adapter.js';\r\nconst allDimensionKeys = [\r\n 'top', 'bottom', 'left', 'right',\r\n 'topLeft', 'topRight', 'bottomLeft', 'bottomRight'\r\n];\r\nfunction isDimensionKey(key) {\r\n return allDimensionKeys.includes(key);\r\n}\r\nconst DefaultDimensionsInOrder = [\r\n ['top', 'Top'],\r\n ['bottom', 'Bottom'],\r\n ['left', 'Left'],\r\n ['right', 'Right'],\r\n];\r\nconst SideDimensions = ['top', 'bottom', 'left', 'right'];\r\nconst SymmetricDimensionMap = {\r\n 'vertical': ['top', 'bottom'],\r\n 'horizontal': ['left', 'right'],\r\n};\r\nfunction isSymmetricDimensionKey(key) {\r\n return SymmetricDimensionMap.hasOwnProperty(key);\r\n}\r\nlet nextId = 0;\r\nclass AmeBoxDimensions extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.inputIdPrefix = '_ame-box-dimensions-c-input-' + (nextId++);\r\n this.unitElementId = '_ame-box-dimensions-c-unit-' + (nextId++);\r\n if ((typeof params['dimensionNames'] !== 'undefined') && Array.isArray(params['dimensionNames'])) {\r\n this.dimensionsInOrder = params['dimensionNames'];\r\n }\r\n else {\r\n this.dimensionsInOrder = DefaultDimensionsInOrder;\r\n }\r\n //Make observable proxies for the individual dimension settings.\r\n const temp = {};\r\n for (const [dimensionKey, dimensionName] of this.dimensionsInOrder) {\r\n const setting = this.settings['value.' + dimensionKey];\r\n if (!setting || (typeof setting !== 'object')) {\r\n throw new Error(`Missing setting for the \"${dimensionName}\" side.`);\r\n }\r\n temp[dimensionKey] = ko.computed({\r\n read: () => {\r\n return setting.value();\r\n },\r\n write: (newValue) => {\r\n if (newValue === '') {\r\n newValue = null;\r\n }\r\n setting.value(newValue);\r\n },\r\n deferEvaluation: true,\r\n }).extend({ 'ameNumericInput': true });\r\n }\r\n this.dimensions = temp;\r\n //Similarly, make an observable for the unit setting.\r\n const unitSetting = this.settings['value.unit'];\r\n if (!unitSetting || (typeof unitSetting !== 'object')) {\r\n throw new Error('Missing setting for the unit.');\r\n }\r\n this.unitSetting = unitSetting;\r\n const defaultDropdownOptions = {\r\n options: [],\r\n optionsText: 'text',\r\n optionsValue: 'value'\r\n };\r\n if (params.unitDropdownOptions && (typeof params.unitDropdownOptions === 'object')) {\r\n const unitDropdownOptions = params.unitDropdownOptions;\r\n this.unitDropdownOptions = {\r\n options: unitDropdownOptions['options'] || defaultDropdownOptions.options,\r\n optionsText: unitDropdownOptions['optionsText'] || defaultDropdownOptions.optionsText,\r\n optionsValue: unitDropdownOptions['optionsValue'] || defaultDropdownOptions.optionsValue,\r\n };\r\n }\r\n else {\r\n this.unitDropdownOptions = defaultDropdownOptions;\r\n }\r\n this.isLinkActive = ko.observable(false);\r\n //Enable the link button by default if all dimensions are equal. Exception: null values.\r\n //Dimensions can have different defaults, so null doesn't necessarily mean that they\r\n //are actually equal.\r\n const firstKey = Object.keys(this.dimensions)[0];\r\n const firstValue = this.dimensions[firstKey]();\r\n if ((firstValue !== null) && (firstValue !== '')) {\r\n let areAllDimensionsEqual = true;\r\n for (const [dimensionKey] of this.dimensionsInOrder) {\r\n if (this.dimensions[dimensionKey]() !== firstValue) {\r\n areAllDimensionsEqual = false;\r\n break;\r\n }\r\n }\r\n this.isLinkActive(areAllDimensionsEqual);\r\n }\r\n //When \"link\" mode is enabled, keep all dimensions in sync.\r\n let isUpdatingAllDimensions = false; //Prevent infinite loops.\r\n const updateAllDimensions = (newValue) => {\r\n if (!isUpdatingAllDimensions && this.isLinkActive()) {\r\n isUpdatingAllDimensions = true;\r\n newValue = this.normalizeValue(newValue);\r\n for (const observable of Object.values(this.dimensions)) {\r\n observable(newValue);\r\n }\r\n isUpdatingAllDimensions = false;\r\n }\r\n };\r\n for (const dimensionKey of Object.keys(this.dimensions)) {\r\n this.dimensions[dimensionKey].subscribe(updateAllDimensions);\r\n }\r\n //In \"symmetric\" mode, the top/bottom and left/right dimensions are always equal.\r\n //The control will only show \"vertical\" and \"horizontal\" inputs.\r\n this.symmetricModeEnabled = ko.observable(this.decideSymmetricMode(params));\r\n //Create computed observables for the \"vertical\" and \"horizontal\" dimensions.\r\n this.symmetricValues = {};\r\n for (const name in SymmetricDimensionMap) {\r\n if (!isSymmetricDimensionKey(name) || !SymmetricDimensionMap.hasOwnProperty(name)) {\r\n continue;\r\n }\r\n const sides = SymmetricDimensionMap[name];\r\n this.symmetricValues[name] = ko.computed({\r\n read: () => {\r\n if (this.symmetricModeEnabled()) {\r\n return this.dimensions[sides[0]]();\r\n }\r\n else {\r\n return null;\r\n }\r\n },\r\n write: (newValue) => {\r\n if (this.symmetricModeEnabled()) {\r\n newValue = this.normalizeValue(newValue);\r\n for (const side of sides) {\r\n this.dimensions[side](newValue);\r\n }\r\n }\r\n },\r\n deferEvaluation: true\r\n }).extend({ 'ameNumericInput': true });\r\n }\r\n //The control displays a different set of inputs depending on the current mode.\r\n this.inputsInOrder = ko.pureComputed(() => {\r\n let result;\r\n if (this.symmetricModeEnabled()) {\r\n result = [\r\n ['vertical', 'Vertical'],\r\n ['horizontal', 'Horizontal'],\r\n ];\r\n }\r\n else {\r\n result = this.dimensionsInOrder;\r\n }\r\n return result;\r\n });\r\n let sliderOptions = {\r\n 'positionParentSelector': '.ame-single-box-dimension',\r\n 'verticalOffset': -2,\r\n };\r\n if (typeof params.popupSliderWithin === 'string') {\r\n sliderOptions.positionWithinClosest = params.popupSliderWithin;\r\n }\r\n this.sliderAdapter = new LazyPopupSliderAdapter(params.sliderRanges ? params.sliderRanges : null, '.ame-box-dimensions-control', 'input.ame-box-dimensions-input', sliderOptions);\r\n }\r\n get classes() {\r\n return ['ame-box-dimensions-control', ...super.classes];\r\n }\r\n //noinspection JSUnusedGlobalSymbols -- Used in the template.\r\n /**\r\n * Get an observable for a specific dimension or a pair of dimensions.\r\n *\r\n * Unfortunately, Knockout doesn't seem to support nested indexed accessors\r\n * like \"dimensions[$data[0]]\", so we have to use a method instead.\r\n */\r\n getInputObservable(key) {\r\n if (this.symmetricModeEnabled() && isSymmetricDimensionKey(key)) {\r\n return this.symmetricValues[key];\r\n }\r\n if (isDimensionKey(key)) {\r\n return this.dimensions[key];\r\n }\r\n throw new Error('Invalid input key for the current mode: ' + key);\r\n }\r\n getInputIdFor(key) {\r\n return this.inputIdPrefix + '-' + key;\r\n }\r\n // noinspection JSUnusedGlobalSymbols -- Used in the template.\r\n getInputAttributes(key) {\r\n return {\r\n id: this.getInputIdFor(key),\r\n 'data-unit-element-id': this.unitElementId,\r\n 'data-ame-box-dimension': key,\r\n };\r\n }\r\n // noinspection JSUnusedGlobalSymbols -- Used in the template.\r\n getSettingFor(key) {\r\n const settingName = 'value.' + key;\r\n if (settingName in this.settings) {\r\n return this.settings[settingName];\r\n }\r\n if (this.symmetricModeEnabled() && isSymmetricDimensionKey(key)) {\r\n for (const dimension of SymmetricDimensionMap[key]) {\r\n //Since both symmetric dimensions are always equal, we can use\r\n //either of the two settings.\r\n const settingName = 'value.' + dimension;\r\n if (settingName in this.settings) {\r\n return this.settings[dimension];\r\n }\r\n }\r\n }\r\n return null;\r\n }\r\n // noinspection JSUnusedGlobalSymbols -- Actually used in the template.\r\n toggleLink() {\r\n this.isLinkActive(!this.isLinkActive());\r\n //When enabling \"link\" mode, fill all inputs with the same value.\r\n //Use the first non-empty value.\r\n if (this.isLinkActive()) {\r\n let firstValue = null;\r\n for (const dimensionObservable of Object.values(this.dimensions)) {\r\n const value = dimensionObservable();\r\n if ((value !== null) && (value !== '')) {\r\n firstValue = value;\r\n break;\r\n }\r\n }\r\n if (firstValue !== null) {\r\n firstValue = this.normalizeValue(firstValue);\r\n for (const dimensionObservable of Object.values(this.dimensions)) {\r\n dimensionObservable(firstValue);\r\n }\r\n }\r\n }\r\n }\r\n normalizeValue(value) {\r\n if (value === null) {\r\n return null;\r\n }\r\n //Convert strings to numbers, and invalid strings to null.\r\n if (typeof value === 'string') {\r\n value = parseFloat(value);\r\n if (isNaN(value)) {\r\n return null;\r\n }\r\n }\r\n return value;\r\n }\r\n /**\r\n * Determine whether the control should be in \"symmetric\" mode.\r\n */\r\n decideSymmetricMode(componentParams) {\r\n //This mode is off by default and can be enabled by setting the \"symmetricMode\" parameter.\r\n let enableMode = (typeof componentParams['symmetricMode'] !== 'undefined')\r\n ? (!!componentParams['symmetricMode'])\r\n : false;\r\n if (!enableMode) {\r\n return false;\r\n }\r\n //Symmetric mode can't be enabled if the control doesn't have all side dimensions.\r\n const hasAllSideDimensions = SideDimensions.every((key) => {\r\n return (key in this.dimensions);\r\n });\r\n if (!hasAllSideDimensions) {\r\n return false;\r\n }\r\n //It also can only be enabled if top/bottom and left/right dimensions are equal.\r\n return ((this.dimensions['top']() === this.dimensions['bottom']())\r\n && (this.dimensions['left']() === this.dimensions['right']()));\r\n }\r\n}\r\nexport default createControlComponentConfig(AmeBoxDimensions, `\n\t
    \n\t\t\n\t\t\t
    \n\t\t\t\t\t\t\t\t\n\t\t\t\t\n\t\t\t
    \n\t\t\n\t\t\n\t\t\n\t
    \n`);\r\n//# sourceMappingURL=ame-box-dimensions.js.map","import { KoStandaloneControl } from '../control-base.js';\r\nexport class ChoiceControlOption {\r\n constructor(data) {\r\n this.value = data.value;\r\n this.label = data.label;\r\n this.description = data.description || '';\r\n this.enabled = (typeof data.enabled === 'undefined') || data.enabled;\r\n this.icon = data.icon || '';\r\n }\r\n}\r\nexport class AmeChoiceControl extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.options = ko.observableArray([]);\r\n if ((typeof params['options'] !== 'undefined') && Array.isArray(params.options)) {\r\n this.options(params.options.map((optionData) => new ChoiceControlOption(optionData)));\r\n }\r\n }\r\n}\r\n//# sourceMappingURL=ame-choice-control.js.map","'use strict';\r\nimport { createControlComponentConfig, KoStandaloneControl } from '../control-base.js';\r\n/**\r\n * Code editor control with syntax highlighting.\r\n *\r\n * This control uses the custom Knockout binding \"ameCodeMirror\" to do the heavy\r\n * lifting. The binding is defined in ko-extensions.ts.\r\n *\r\n * Note: The user can disable syntax highlighting. In that case, this control\r\n * should behave like a normal textarea.\r\n */\r\nclass AmeCodeEditor extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n if ((typeof params.editorSettings === 'object') && (params.editorSettings !== null)) {\r\n this.editorSettings = params.editorSettings;\r\n }\r\n else {\r\n this.editorSettings = false;\r\n }\r\n }\r\n}\r\nexport default createControlComponentConfig(AmeCodeEditor, `\n\t
    \n\t\t\n\t
    \n\t\n\t\t\n\t\n`);\r\n//# sourceMappingURL=ame-code-editor.js.map","import { createControlComponentConfig, KoStandaloneControl } from '../control-base.js';\r\n/**\r\n * A wrapper for the WordPress color picker.\r\n *\r\n * Note that the custom 'ameColorPicker' binding must be available when this component\r\n * is used. You must enqueue the 'ame-ko-extensions' script for this to work.\r\n */\r\nclass AmeColorPicker extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n }\r\n koDescendantsComplete(node) {\r\n //Make the color picker input visible. Its visibility is set to hidden by default.\r\n if (node.nodeType === Node.COMMENT_NODE) {\r\n //The component was bound to a comment node. The real element\r\n //should be the next non-comment sibling.\r\n let nextElement;\r\n do {\r\n nextElement = node.nextElementSibling;\r\n } while (nextElement && (nextElement.nodeType === Node.COMMENT_NODE));\r\n if (!nextElement) {\r\n return; //This should never happen.\r\n }\r\n node = nextElement;\r\n }\r\n if (!node || (node.nodeType !== Node.ELEMENT_NODE)) {\r\n return; //This should never happen.\r\n }\r\n const $picker = jQuery(node);\r\n //This should be a .wp-picker-container element that contains an input.\r\n const $input = $picker.find('input.ame-color-picker');\r\n if ($input.length > 0) {\r\n $input.css('visibility', 'visible');\r\n }\r\n }\r\n get classes() {\r\n return ['ame-color-picker', 'ame-color-picker-component', ...super.classes];\r\n }\r\n}\r\nexport default createControlComponentConfig(AmeColorPicker, `\n\t\n`);\r\n//# sourceMappingURL=ame-color-picker.js.map","/*\r\n * This utility module imports all the base Knockout components and exports\r\n * a function that can be used to register the components with Knockout.\r\n */\r\nimport ameBoxDimensions from './ame-box-dimensions/ame-box-dimensions.js';\r\nimport ameColorPicker from './ame-color-picker/ame-color-picker.js';\r\nimport ameFontStylePicker from './ame-font-style-picker/ame-font-style-picker.js';\r\nimport ameImageSelector from './ame-image-selector/ame-image-selector.js';\r\nimport ameNumberInput from './ame-number-input/ame-number-input.js';\r\nimport ameNestedDescription from './ame-nested-description/ame-nested-description.js';\r\nimport ameRadioButtonBar from './ame-radio-button-bar/ame-radio-button-bar.js';\r\nimport ameRadioGroup from './ame-radio-group/ame-radio-group.js';\r\nimport ameSelectBox from './ame-select-box/ame-select-box.js';\r\nimport ameSiblingDescription from './ame-sibling-description/ame-sibling-description.js';\r\nimport ameStaticHtml from './ame-static-html/ame-static-html.js';\r\nimport ameTextInput from './ame-text-input/ame-text-input.js';\r\nimport ameToggleCheckbox from './ame-toggle-checkbox/ame-toggle-checkbox.js';\r\nimport ameUnitDropdown from './ame-unit-dropdown/ame-unit-dropdown.js';\r\nimport ameWpEditor from './ame-wp-editor/ame-wp-editor.js';\r\nimport ameHorizontalSeparator from './ame-horizontal-separator/ame-horizontal-separator.js';\r\nimport ameCodeEditor from './ame-code-editor/ame-code-editor.js';\r\nlet componentsRegistered = false;\r\n/**\r\n * Register the base Knockout components that are part of AME.\r\n *\r\n * It's safe to call this function multiple times. It will only register the components once.\r\n */\r\nexport function registerBaseComponents() {\r\n if (componentsRegistered) {\r\n return;\r\n }\r\n ko.components.register('ame-box-dimensions', ameBoxDimensions);\r\n ko.components.register('ame-color-picker', ameColorPicker);\r\n ko.components.register('ame-font-style-picker', ameFontStylePicker);\r\n ko.components.register('ame-image-selector', ameImageSelector);\r\n ko.components.register('ame-number-input', ameNumberInput);\r\n ko.components.register('ame-nested-description', ameNestedDescription);\r\n ko.components.register('ame-radio-button-bar', ameRadioButtonBar);\r\n ko.components.register('ame-radio-group', ameRadioGroup);\r\n ko.components.register('ame-select-box', ameSelectBox);\r\n ko.components.register('ame-sibling-description', ameSiblingDescription);\r\n ko.components.register('ame-static-html', ameStaticHtml);\r\n ko.components.register('ame-text-input', ameTextInput);\r\n ko.components.register('ame-toggle-checkbox', ameToggleCheckbox);\r\n ko.components.register('ame-unit-dropdown', ameUnitDropdown);\r\n ko.components.register('ame-wp-editor', ameWpEditor);\r\n ko.components.register('ame-horizontal-separator', ameHorizontalSeparator);\r\n ko.components.register('ame-code-editor', ameCodeEditor);\r\n componentsRegistered = true;\r\n}\r\n//# sourceMappingURL=ame-components.js.map","import { KoComponentViewModel } from '../control-base.js';\r\n/**\r\n * Base class for description components.\r\n */\r\nexport class AmeDescriptionComponent extends KoComponentViewModel {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.description = params.description || '';\r\n }\r\n}\r\n//# sourceMappingURL=ame-description.js.map","import { createControlComponentConfig, KoStandaloneControl } from '../control-base.js';\r\n//Note: Font style picker CSS is already included in the main 'controls.scss' file\r\n//and won't be duplicated or included here. Instead, load that stylesheet when\r\n//using any control components.\r\n/**\r\n * Font style options that can be selected in the picker component.\r\n *\r\n * Regrettably, these are duplicated from the PHP version of the control.\r\n * Once browsers support importing JSON files, we can move this to a separate\r\n * file and use that file in both places.\r\n */\r\nconst fontStyleOptions = {\r\n \"font-style\": [\r\n {\r\n \"value\": null,\r\n \"text\": \"Default font style\",\r\n \"label\": \"—\"\r\n },\r\n {\r\n \"value\": \"italic\",\r\n \"text\": \"Italic\",\r\n \"label\": \"\"\r\n }\r\n ],\r\n \"text-transform\": [\r\n {\r\n \"value\": null,\r\n \"text\": \"Default letter case\",\r\n \"label\": \"—\"\r\n },\r\n {\r\n \"value\": \"uppercase\",\r\n \"text\": \"Uppercase\",\r\n \"label\": {\r\n 'text-transform': 'uppercase'\r\n }\r\n },\r\n {\r\n \"value\": \"lowercase\",\r\n \"text\": \"Lowercase\",\r\n \"label\": {\r\n 'text-transform': 'lowercase'\r\n }\r\n },\r\n {\r\n \"value\": \"capitalize\",\r\n \"text\": \"Capitalize each word\",\r\n \"label\": {\r\n 'text-transform': 'capitalize'\r\n }\r\n }\r\n ],\r\n \"font-variant\": [\r\n {\r\n \"value\": null,\r\n \"text\": \"Default font variant\",\r\n \"label\": \"—\"\r\n },\r\n {\r\n \"value\": \"small-caps\",\r\n \"text\": \"Small caps\",\r\n \"label\": {\r\n 'font-variant': 'small-caps'\r\n }\r\n }\r\n ],\r\n \"text-decoration\": [\r\n {\r\n \"value\": null,\r\n \"text\": \"Default text decoration\",\r\n \"label\": \"—\"\r\n },\r\n {\r\n \"value\": \"underline\",\r\n \"text\": \"Underline\",\r\n \"label\": \"\"\r\n },\r\n {\r\n \"value\": \"line-through\",\r\n \"text\": \"Strikethrough\",\r\n \"label\": \"\"\r\n }\r\n ]\r\n};\r\n//Generate label HTML for options that don't have it yet.\r\nfunction makeFontSample(styles) {\r\n let styleString = '';\r\n for (const [property, value] of Object.entries(styles)) {\r\n styleString += `${property}: ${value};`;\r\n }\r\n return `ab`;\r\n}\r\nlet flattenedOptions = [];\r\nfor (const [property, options] of Object.entries(fontStyleOptions)) {\r\n options.forEach((option) => {\r\n //Skip null values. They're used to indicate the default option,\r\n //and we don't need those in the Knockout version of this control.\r\n if (option.value === null) {\r\n return;\r\n }\r\n let labelString;\r\n if (typeof option.label === 'object') {\r\n labelString = makeFontSample(option.label);\r\n }\r\n else {\r\n labelString = option.label;\r\n }\r\n flattenedOptions.push({\r\n 'value': option.value,\r\n 'text': option.text || '',\r\n 'property': property,\r\n 'label': labelString\r\n });\r\n });\r\n}\r\nclass AmeFontStylePicker extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.options = flattenedOptions;\r\n }\r\n get classes() {\r\n return ['ame-font-style-control', ...super.classes];\r\n }\r\n // noinspection JSUnusedGlobalSymbols -- Used in the template, below.\r\n isOptionSelected(property, value) {\r\n if (this.settings.hasOwnProperty(property)) {\r\n return (this.settings[property].value() === value);\r\n }\r\n return false;\r\n }\r\n // noinspection JSUnusedGlobalSymbols -- Used in the template.\r\n toggleOption(property, value) {\r\n if (!this.settings.hasOwnProperty(property)) {\r\n return;\r\n }\r\n const targetSetting = this.settings[property];\r\n if (targetSetting.value() === value) {\r\n //When the user clicks on the currently selected option, reset it to the default.\r\n targetSetting.tryUpdate(null);\r\n }\r\n else {\r\n //Otherwise, set the new value.\r\n targetSetting.tryUpdate(value);\r\n }\r\n }\r\n}\r\n//Note: This weird spacing in the template string is intentional. It's used to\r\n//remove whitespace nodes from the DOM, which would otherwise slightly change\r\n//the layout of the control.\r\nexport default createControlComponentConfig(AmeFontStylePicker, `\n\t
    \n\t\t\n\t
    \n`);\r\n//# sourceMappingURL=ame-font-style-picker.js.map","import { createControlComponentConfig, KoStandaloneControl } from '../control-base.js';\r\nclass AmeHorizontalSeparator extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n }\r\n}\r\nexport default createControlComponentConfig(AmeHorizontalSeparator, `\n\t
    \n`);\r\n//# sourceMappingURL=ame-horizontal-separator.js.map","'use strict';\r\nimport { createControlComponentConfig, KoStandaloneControl } from '../control-base.js';\r\n/**\r\n * Image selector control.\r\n *\r\n * This implementation hands off the work to the existing AmeImageSelectorApi.ImageSelector\r\n * class to avoid duplicating the effort. That class is not a module because it is also\r\n * used for the more progressive-enhancement-y PHP-rendered controls, so we can't import it.\r\n */\r\nclass AmeImageSelector extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.selectorInstance = null;\r\n //Verify that our dependencies are available.\r\n if (typeof AmeImageSelectorApi === 'undefined') {\r\n throw new Error('AmeImageSelectorApi is not available. Remember to enqueue \"ame-image-selector-control-v2\".');\r\n }\r\n if (typeof AmeImageSelectorApi.ImageSelector === 'undefined') {\r\n throw new Error('AmeImageSelectorApi.ImageSelector is not available. This is probably a bug.');\r\n }\r\n this.externalUrlsAllowed = !!params.externalUrlsAllowed;\r\n this.canSelectMedia = !!params.canSelectMedia;\r\n this.imageProxy = this.settings.value.value;\r\n }\r\n get classes() {\r\n return [\r\n 'ame-image-selector-v2',\r\n ...super.classes,\r\n ];\r\n }\r\n koDescendantsComplete() {\r\n const $container = this.findChild('.ame-image-selector-v2');\r\n if ($container.length === 0) {\r\n return;\r\n }\r\n this.selectorInstance = new AmeImageSelectorApi.ImageSelector($container, {\r\n externalUrlsAllowed: this.externalUrlsAllowed,\r\n canSelectMedia: this.canSelectMedia,\r\n }, this.imageProxy());\r\n }\r\n}\r\nexport default createControlComponentConfig(AmeImageSelector, `\n\t
    \n\t\t\n\t
    \n`);\r\n//# sourceMappingURL=ame-image-selector.js.map","import { createComponentConfig } from '../control-base.js';\r\nimport { AmeDescriptionComponent } from '../ame-description/ame-description.js';\r\n/**\r\n * A simple component that displays the description of a UI element.\r\n *\r\n * Like AmeSiblingDescription, but intended to be rendered inside\r\n * the parent control or container, not as a sibling.\r\n */\r\nclass AmeNestedDescription extends AmeDescriptionComponent {\r\n}\r\nexport default createComponentConfig(AmeNestedDescription, `\n\t
    \t\n`);\r\n//# sourceMappingURL=ame-nested-description.js.map","/// \r\nimport { createControlComponentConfig, KoDependentControl } from '../control-base.js';\r\nimport { AmeCustomizable } from '../../assets/customizable.js';\r\nvar Control = AmeCustomizable.Control;\r\nexport class AmeNumberInput extends KoDependentControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.sliderRanges = null;\r\n this.slider = null;\r\n this.numericValue = this.valueProxy.extend({ 'ameNumericInput': true });\r\n this.unitText = params.unitText || '';\r\n this.hasUnitDropdown = params.hasUnitDropdown || false;\r\n this.unitElementId = params.unitElementId || '';\r\n if (this.hasUnitDropdown && params.unitDropdownOptions) {\r\n this.unitDropdownOptions = {\r\n options: params.unitDropdownOptions.options || [],\r\n optionsText: params.unitDropdownOptions.optionsText || 'text',\r\n optionsValue: params.unitDropdownOptions.optionsValue || 'value'\r\n };\r\n }\r\n else {\r\n this.unitDropdownOptions = null;\r\n }\r\n this.min = params.min || null;\r\n this.max = params.max || null;\r\n this.step = params.step || null;\r\n if (params.sliderRanges) {\r\n this.sliderRanges = params.sliderRanges;\r\n }\r\n this.popupSliderWithin = (typeof params.popupSliderWithin === 'string') ? params.popupSliderWithin : null;\r\n this.inputClasses.unshift('ame-input-with-popup-slider', 'ame-number-input');\r\n }\r\n get classes() {\r\n const classes = ['ame-number-input-control'];\r\n if (this.sliderRanges !== null) {\r\n classes.push('ame-container-with-popup-slider');\r\n }\r\n classes.push(...super.classes);\r\n return classes;\r\n }\r\n get inputClasses() {\r\n const classes = ['ame-input-with-popup-slider', 'ame-number-input'];\r\n classes.push(...super.inputClasses);\r\n return classes;\r\n }\r\n getAdditionalInputAttributes() {\r\n let attributes = super.getAdditionalInputAttributes();\r\n if (this.min !== null) {\r\n attributes['min'] = this.min.toString();\r\n }\r\n if (this.max !== null) {\r\n attributes['max'] = this.max.toString();\r\n }\r\n if (this.step !== null) {\r\n attributes['step'] = this.step.toString();\r\n }\r\n if (this.unitElementId) {\r\n attributes['data-unit-element-id'] = this.unitElementId;\r\n }\r\n return attributes;\r\n }\r\n // noinspection JSUnusedGlobalSymbols -- Used in the Knockout template in this same file.\r\n showPopupSlider($data, event) {\r\n if ((this.sliderRanges === null) || (typeof AmePopupSlider === 'undefined')) {\r\n return;\r\n }\r\n //Some sanity checks.\r\n if (!event.target) {\r\n return;\r\n }\r\n const $input = jQuery(event.target);\r\n if ($input.is(':disabled') || !$input.is('input')) {\r\n return;\r\n }\r\n const $container = $input.closest('.ame-container-with-popup-slider');\r\n if ($container.length < 1) {\r\n return;\r\n }\r\n //Initialize the slider if it's not already initialized.\r\n if (!this.slider) {\r\n let sliderOptions = {};\r\n if (this.popupSliderWithin) {\r\n sliderOptions.positionWithinClosest = this.popupSliderWithin;\r\n }\r\n //In HTML, we would pass the range data as a \"data-slider-ranges\" attribute,\r\n //but here we can just set the data directly.\r\n $input.data('slider-ranges', this.sliderRanges);\r\n this.slider = AmePopupSlider.createSlider($container, sliderOptions);\r\n }\r\n this.slider.showForInput($input);\r\n }\r\n getExpectedUiElementType() {\r\n return Control;\r\n }\r\n}\r\nexport default createControlComponentConfig(AmeNumberInput, `\n\t
    \n\t\t
    \n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t
    \n\t
    \t\n`);\r\n//# sourceMappingURL=ame-number-input.js.map","'use strict';\r\nimport { createControlComponentConfig } from '../control-base.js';\r\nimport { AmeChoiceControl } from '../ame-choice-control/ame-choice-control.js';\r\nclass AmeRadioButtonBar extends AmeChoiceControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n }\r\n get classes() {\r\n return ['ame-radio-button-bar-control', ...super.classes];\r\n }\r\n}\r\nexport default createControlComponentConfig(AmeRadioButtonBar, `\n\t
    \n\t\t\n\t\t\n\t\t\n\t
    \n`);\r\n//# sourceMappingURL=ame-radio-button-bar.js.map","'use strict';\r\nimport { createControlComponentConfig } from '../control-base.js';\r\nimport { AmeChoiceControl } from '../ame-choice-control/ame-choice-control.js';\r\n// noinspection JSUnusedGlobalSymbols -- Enum keys like \"Paragraph\" are used when serializing wrapStyle in PHP.\r\nvar WrapStyle;\r\n(function (WrapStyle) {\r\n WrapStyle[\"LineBreak\"] = \"br\";\r\n WrapStyle[\"Paragraph\"] = \"p\";\r\n WrapStyle[\"None\"] = \"\";\r\n})(WrapStyle || (WrapStyle = {}));\r\nfunction isWrapStyle(value) {\r\n if (typeof value !== 'string') {\r\n return false;\r\n }\r\n return (typeof WrapStyle[value] === 'string');\r\n}\r\nlet nextRadioGroupId = 1;\r\nclass AmeRadioGroup extends AmeChoiceControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.wrapStyle = WrapStyle.None;\r\n this.childByValue = new Map();\r\n if ((typeof params['valueChildIndexes'] === 'object') && Array.isArray(params.valueChildIndexes)) {\r\n const children = ko.unwrap(this.inputChildren);\r\n for (const [value, index] of params.valueChildIndexes) {\r\n if (!children || !children[index]) {\r\n throw new Error('The \"' + this.label + '\" radio group has no children, but its valueChildIndexes'\r\n + ' requires child #' + index + ' to be associated with value \"' + value + '\".');\r\n }\r\n this.childByValue.set(value, children[index]);\r\n }\r\n }\r\n this.wrapStyle = isWrapStyle(params.wrapStyle) ? WrapStyle[params.wrapStyle] : WrapStyle.None;\r\n if (this.childByValue.size > 0) {\r\n this.wrapStyle = WrapStyle.None;\r\n }\r\n this.radioInputPrefix = (typeof params.radioInputPrefix === 'string')\r\n ? params.radioInputPrefix\r\n : ('ame-rg-input-' + nextRadioGroupId++ + '-');\r\n }\r\n get classes() {\r\n const result = ['ame-radio-group-component', ...super.classes];\r\n if (this.childByValue.size > 0) {\r\n result.push('ame-rg-has-nested-controls');\r\n }\r\n return result;\r\n }\r\n // noinspection JSUnusedGlobalSymbols -- Used in the template below.\r\n getChoiceChild(value) {\r\n return this.childByValue.get(value) || null;\r\n }\r\n // noinspection JSUnusedGlobalSymbols -- Used in the template.\r\n /**\r\n * Get the ID attribute for a radio input.\r\n *\r\n * Note: This must match the algorithm used by the PHP version of this control\r\n * to work correctly with the BorderStyleSelector control that adds style samples\r\n * to each choice and uses the ID to link them to the inputs (so that clicking\r\n * the sample selects the option).\r\n */\r\n getRadioInputId(choice) {\r\n let sanitizedValue = (choice.value !== null) ? choice.value.toString() : '';\r\n //Emulate the sanitize_key() function from WordPress core.\r\n sanitizedValue = sanitizedValue.toLowerCase().replace(/[^a-z0-9_\\-]/gi, '');\r\n return this.radioInputPrefix + sanitizedValue;\r\n }\r\n}\r\nconst choiceTemplate = `\n\t\n`;\r\nexport default createControlComponentConfig(AmeRadioGroup, `\n\t
    \n\t\t\n\t\t\t\n\t\t\t\t${choiceTemplate}
    \n\t\t\t\n\t\t\t\n\t\t\t\t

    ${choiceTemplate}

    \n\t\t\t\n\t\t\t\n\t\t\t\t${choiceTemplate}\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t
    \n`);\r\n//# sourceMappingURL=ame-radio-group.js.map","import { AmeChoiceControl } from '../ame-choice-control/ame-choice-control.js';\r\nimport { createControlComponentConfig } from '../control-base.js';\r\nclass AmeSelectBox extends AmeChoiceControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n }\r\n get classes() {\r\n return ['ame-select-box-control', ...super.classes];\r\n }\r\n}\r\nexport default createControlComponentConfig(AmeSelectBox, `\n\t\n\t\n\t\t\n\t\t\n`);\r\n//# sourceMappingURL=ame-select-box.js.map","import { createComponentConfig } from '../control-base.js';\r\nimport { AmeDescriptionComponent } from '../ame-description/ame-description.js';\r\n/**\r\n * A simple component that displays the description of a UI element.\r\n *\r\n * This should be rendered as a sibling of the UI element's component,\r\n * typically immediately after it.\r\n *\r\n * Caution: HTML is allowed in the description.\r\n */\r\nclass AmeSiblingDescription extends AmeDescriptionComponent {\r\n}\r\nexport default createComponentConfig(AmeSiblingDescription, `\n\t

    \t\n`);\r\n//# sourceMappingURL=ame-sibling-description.js.map","import { createControlComponentConfig, KoStandaloneControl } from '../control-base.js';\r\nclass AmeStaticHtml extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.containerType = 'span';\r\n this.htmlContent = (typeof params.html === 'string') ? params.html : '';\r\n if (typeof params.container === 'string') {\r\n this.containerType = params.container;\r\n }\r\n }\r\n}\r\n//Note: The HTML content has to be in a container element because Knockout doesn't allow\r\n//using the \"html\" binding with virtual elements.\r\nexport default createControlComponentConfig(AmeStaticHtml, `\n\t\n\t\t
    \n\t\n\t\n\t\t\n\t\n`);\r\n//# sourceMappingURL=ame-static-html.js.map","import { createControlComponentConfig, KoDependentControl } from '../control-base.js';\r\nexport class AmeTextInput extends KoDependentControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.inputType = 'text';\r\n this.isCode = params.isCode || false;\r\n this.inputType = params.inputType || 'text';\r\n }\r\n get inputClasses() {\r\n const classes = ['regular-text'];\r\n if (this.isCode) {\r\n classes.push('code');\r\n }\r\n classes.push('ame-text-input-control', ...super.inputClasses);\r\n return classes;\r\n }\r\n getAdditionalInputAttributes() {\r\n return Object.assign({ 'type': this.inputType }, super.getAdditionalInputAttributes());\r\n }\r\n}\r\nexport default createControlComponentConfig(AmeTextInput, `\n\t\n\t\n\t\t\n\t\t\n`);\r\n//# sourceMappingURL=ame-text-input.js.map","import { createControlComponentConfig, KoStandaloneControl } from '../control-base.js';\r\nclass AmeToggleCheckbox extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.onValue = (typeof params.onValue !== 'undefined') ? params.onValue : true;\r\n this.offValue = (typeof params.offValue !== 'undefined') ? params.offValue : false;\r\n if (typeof this.settings['value'] === 'undefined') {\r\n this.isChecked = ko.pureComputed(() => false);\r\n }\r\n else {\r\n this.isChecked = ko.computed({\r\n read: () => {\r\n return this.settings.value.value() === ko.unwrap(this.onValue);\r\n },\r\n write: (newValue) => {\r\n this.settings.value.value(ko.unwrap(newValue ? this.onValue : this.offValue));\r\n },\r\n deferEvaluation: true\r\n });\r\n }\r\n }\r\n get classes() {\r\n return ['ame-toggle-checkbox-control', ...super.classes];\r\n }\r\n}\r\n//Unlike the HTML version of this control, the Knockout version doesn't have\r\n//a second, hidden checkbox. This is because the component is entirely JS-based\r\n//and doesn't need to be submitted as part of a form.\r\nexport default createControlComponentConfig(AmeToggleCheckbox, `\n\t\t\n`);\r\n//# sourceMappingURL=ame-toggle-checkbox.js.map","import { createControlComponentConfig, KoStandaloneControl } from '../control-base.js';\r\nexport class AmeUnitDropdown extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.dropdownData = params.optionData || {\r\n options: [],\r\n optionsText: 'text',\r\n optionsValue: 'value'\r\n };\r\n this.selectId = params.id || '';\r\n }\r\n}\r\nexport default createControlComponentConfig(AmeUnitDropdown, `\n\t\n`);\r\n//# sourceMappingURL=ame-unit-dropdown.js.map","'use strict';\r\nimport { createControlComponentConfig, KoStandaloneControl } from '../control-base.js';\r\n//Note: Requires Lodash, but does not explicitly import it because this plugin\r\n//already uses Lodash as a global variable (wsAmeLodash) in many places. Code\r\n//that uses this component should make sure that Lodash is loaded.\r\nlet autoAssignedIdCounter = 0;\r\n/**\r\n * List of visual editor buttons that are visible in the \"teeny\" mode.\r\n *\r\n * Found in /wp-includes/class-wp-editor.php, the editor_settings() method.\r\n * The relevant code is around line #601 (as of WP 6.1.1).\r\n */\r\nconst TeenyButtons = [\r\n 'bold',\r\n 'italic',\r\n 'underline',\r\n 'blockquote',\r\n 'strikethrough',\r\n 'bullist',\r\n 'numlist',\r\n 'alignleft',\r\n 'aligncenter',\r\n 'alignright',\r\n 'undo',\r\n 'redo',\r\n 'link',\r\n 'fullscreen'\r\n];\r\n/**\r\n * List of Quicktags editor buttons that are visible by default.\r\n *\r\n * The default list of text editor buttons used by wp.editor.initialize()\r\n * doesn't match the defaults used by wp_editor() in PHP. Let's copy the list\r\n * from /includes/class-wp-editor.php.\r\n */\r\nconst DefaultQuicktagsButtons = [\r\n 'strong', 'em', 'link', 'block', 'del', 'ins', 'img', 'ul', 'ol', 'li', 'code', 'more', 'close'\r\n];\r\nclass AmeWpEditor extends KoStandaloneControl {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n this.editorId = null;\r\n this.isWpEditorInitialized = false;\r\n const textSetting = this.settings.value;\r\n if (typeof textSetting === 'undefined') {\r\n throw new Error('Visual Editor control is missing the required setting');\r\n }\r\n this.rows = params.rows || 6;\r\n this.isTeeny = !!params.teeny;\r\n }\r\n getAdditionalInputAttributes() {\r\n return Object.assign({ rows: this.rows.toString() }, super.getAdditionalInputAttributes());\r\n }\r\n koDescendantsComplete() {\r\n const $textArea = this.findChild('textarea.ame-wp-editor-textarea');\r\n if ($textArea.length === 0) {\r\n return;\r\n }\r\n const currentValue = this.valueProxy();\r\n $textArea.val((currentValue === null) ? '' : currentValue.toString());\r\n //The textarea must have an ID for wp.editor.initialize() to work.\r\n {\r\n let editorId = $textArea.attr('id');\r\n if (!editorId) {\r\n editorId = 'ws-ame-wp-editor-aid-' + (autoAssignedIdCounter++);\r\n $textArea.attr('id', editorId);\r\n }\r\n this.editorId = editorId;\r\n }\r\n //Update the setting when the contents of the underlying textarea change.\r\n //This happens when the user selects the \"Text\" tab in the editor, or when\r\n //TinyMCE is unavailable (e.g. if the \"Disable the visual editor when writing\"\r\n //option is checked in the user's profile).\r\n $textArea.on('change input', this.throttleUpdates(() => $textArea.val()));\r\n let editorSettings = {\r\n tinymce: {\r\n wpautop: true\r\n },\r\n quicktags: {\r\n //The default list of text editor buttons used by wp.editor.initialize()\r\n //doesn't match the defaults used by wp_editor() in PHP. Let's copy the list\r\n //from /includes/class-wp-editor.php.\r\n buttons: DefaultQuicktagsButtons.join(','),\r\n },\r\n //Include the \"Add Media\" button.\r\n mediaButtons: true,\r\n };\r\n if (typeof window['tinymce'] === 'undefined') {\r\n //TinyMCE is disabled or not available.\r\n editorSettings.tinymce = false;\r\n }\r\n if (this.isTeeny && (typeof editorSettings.tinymce === 'object')) {\r\n editorSettings.tinymce.toolbar1 = TeenyButtons.join(',');\r\n editorSettings.tinymce.toolbar2 = '';\r\n }\r\n const $document = jQuery(document);\r\n const self = this;\r\n //After the editor finishes initializing, add an event listener to update\r\n //the setting when the contents of the visual editor change.\r\n $document.on('tinymce-editor-init', function addMceChangeListener(event, editor) {\r\n if (editor.id !== self.editorId) {\r\n return; //Not our editor.\r\n }\r\n //According to the TinyMCE documentation, the \"Change\" event is fired\r\n //when \"changes [...] cause an undo level to be added\". This could be\r\n //too frequent for our purposes, so we'll throttle the callback.\r\n editor.on('Change', self.throttleUpdates(() => editor.getContent()));\r\n $document.off('tinymce-editor-init', addMceChangeListener);\r\n });\r\n //Unfortunately, as of WP 6.2-beta, wp.editor.initialize() doesn't add\r\n //the \"wp-editor-container\" wrapper when only the Quicktags editor is used.\r\n //This means the editor won't be styled correctly. Let's fix that.\r\n $document.on('quicktags-init', function maybeAddEditorWrapper(event, editor) {\r\n if (!editor || (editor.id !== self.editorId)) {\r\n return;\r\n }\r\n if (editor.canvas) {\r\n const $textarea = jQuery(editor.canvas);\r\n const $wrapper = $textarea.closest('.wp-editor-container');\r\n if ($wrapper.length === 0) {\r\n //Also include the toolbar in the wrapper.\r\n const $toolbar = $textarea.prevAll('.quicktags-toolbar').first();\r\n $textarea.add($toolbar).wrapAll('
    ');\r\n }\r\n }\r\n $document.off('quicktags-init', maybeAddEditorWrapper);\r\n });\r\n //Finally, initialize the editor.\r\n wp.editor.initialize($textArea.attr('id'), editorSettings);\r\n this.isWpEditorInitialized = true;\r\n }\r\n /**\r\n * Create a throttled function that updates the setting.\r\n *\r\n * There are multiple ways to get the contents of the editor (e.g. TinyMCE mode\r\n * vs a plain textarea), so using a utility function helps avoid code duplication.\r\n *\r\n * @param valueGetter\r\n * @protected\r\n */\r\n throttleUpdates(valueGetter) {\r\n const textSetting = this.settings.value;\r\n return wsAmeLodash.throttle(function () {\r\n textSetting.value(valueGetter());\r\n return void 0;\r\n }, 1000, { leading: true, trailing: true });\r\n }\r\n dispose() {\r\n //Destroy the editor. It's not clear whether this is necessary, but it's\r\n //probably a good idea to give WP a chance to clean up.\r\n if (this.isWpEditorInitialized && (this.editorId !== null)) {\r\n wp.editor.remove(this.editorId);\r\n this.isWpEditorInitialized = false;\r\n }\r\n super.dispose();\r\n }\r\n}\r\n//Note: The class of the textarea element is set directly instead of using a binding\r\n//because it must always have the \"wp-editor-area\" class for it to render correctly\r\n//(apparently, wp.editor.initialize() does not automatically add that class).\r\n//Knockout should not be able to remove the class.\r\nexport default createControlComponentConfig(AmeWpEditor, `\n\t\t\n`);\r\n//# sourceMappingURL=ame-wp-editor.js.map","import { AmeCustomizable } from '../assets/customizable.js';\r\nvar Setting = AmeCustomizable.Setting;\r\nvar InterfaceStructure = AmeCustomizable.InterfaceStructure;\r\nvar Control = AmeCustomizable.Control;\r\nexport class KoComponentViewModel {\r\n constructor(params, $element) {\r\n var _a;\r\n this.params = params;\r\n this.$element = $element;\r\n this.isBoundToComment = ($element[0]) && ($element[0].nodeType === Node.COMMENT_NODE);\r\n this.uiElement = null;\r\n const expectedType = this.getExpectedUiElementType();\r\n if (expectedType !== null) {\r\n if ((typeof params.uiElement !== 'undefined')\r\n && (params.uiElement instanceof expectedType)) {\r\n this.uiElement = params.uiElement;\r\n }\r\n else {\r\n throw new Error('uiElement is not a ' + expectedType.name + ' instance.');\r\n }\r\n }\r\n else if ((typeof params.uiElement !== 'undefined') && !(this instanceof KoStandaloneControl)) {\r\n console.warn('Unexpected \"uiElement\" parameter for ' + this.constructor.name\r\n + ' that did not expect an UI element. Did you forget to override getExpectedUiElementType() ?', params.uiElement);\r\n }\r\n if (typeof params.children !== 'undefined') {\r\n if (Array.isArray(params.children) || this.isObservableArray(params.children)) {\r\n this.inputChildren = params.children;\r\n }\r\n else {\r\n throw new Error('Invalid \"children\" parameter: expected an array or an observable array.');\r\n }\r\n }\r\n else {\r\n this.inputChildren = [];\r\n }\r\n this.customClasses = ((typeof params.classes === 'object') && Array.isArray(params.classes)) ? params.classes : [];\r\n this.customStyles = ((typeof params.styles === 'object') && (params.styles !== null)) ? params.styles : {};\r\n if (typeof params.enabled !== 'undefined') {\r\n if (ko.isObservable(params.enabled)) {\r\n this.isEnabled = params.enabled;\r\n }\r\n else {\r\n this.isEnabled = ko.pureComputed(() => !!params.enabled);\r\n }\r\n }\r\n else {\r\n this.isEnabled = ko.pureComputed(() => true);\r\n }\r\n //Get the description either from the \"description\" parameter or from the UI element.\r\n this.description = params.description\r\n ? ko.unwrap(params.description.toString())\r\n : (((_a = this.uiElement) === null || _a === void 0 ? void 0 : _a.description) || '');\r\n }\r\n dispose() {\r\n //Does nothing by default.\r\n }\r\n getExpectedUiElementType() {\r\n return null;\r\n }\r\n get classes() {\r\n return [].concat(this.customClasses);\r\n }\r\n // noinspection JSUnusedGlobalSymbols -- Used in Knockout templates.\r\n get classString() {\r\n return this.classes.join(' ');\r\n }\r\n get styles() {\r\n return Object.assign({}, this.customStyles);\r\n }\r\n findChild(selector, allowSiblingSearch = null) {\r\n if (allowSiblingSearch === null) {\r\n //Enable only if the component is bound to a comment (i.e. \"\").\r\n allowSiblingSearch = this.isBoundToComment;\r\n }\r\n if (this.isBoundToComment) {\r\n if (allowSiblingSearch) {\r\n return this.$element.nextAll(selector).first();\r\n }\r\n else {\r\n //We would never find anything because a comment node has no children.\r\n return jQuery();\r\n }\r\n }\r\n return this.$element.find(selector);\r\n }\r\n isObservableArray(value) {\r\n return (typeof value === 'object')\r\n && (value !== null)\r\n && (typeof value.slice === 'function')\r\n && (typeof value.indexOf === 'function')\r\n && (ko.isObservable(value));\r\n }\r\n}\r\nfunction makeCreateVmFunctionForComponent(ctor) {\r\n return function (params, componentInfo) {\r\n const $element = jQuery(componentInfo.element);\r\n return new ctor(params, $element);\r\n };\r\n}\r\nexport function createComponentConfig(ctor, templateString) {\r\n return {\r\n viewModel: {\r\n createViewModel: makeCreateVmFunctionForComponent(ctor),\r\n },\r\n template: templateString,\r\n };\r\n}\r\n//endregion\r\n//region Container\r\nexport class ComponentBindingOptions {\r\n // noinspection JSUnusedGlobalSymbols -- the uiElement property is used in the KO template of AC control groups.\r\n constructor(name, params, uiElement) {\r\n this.name = name;\r\n this.params = params;\r\n this.uiElement = uiElement;\r\n if (name === '') {\r\n throw new Error('Component name cannot be empty.');\r\n }\r\n }\r\n static fromElement(element, overrideComponentName = null) {\r\n if (!element.component && (overrideComponentName === null)) {\r\n throw new Error(`Cannot create component binding options for UI element \"${element.id}\" without a component name.`);\r\n }\r\n return new ComponentBindingOptions(overrideComponentName || element.component, element.getComponentParams(), element);\r\n }\r\n}\r\nexport class KoContainerViewModel extends KoComponentViewModel {\r\n constructor(params, $element) {\r\n if (typeof params.children === 'undefined') {\r\n throw new Error('Missing \"children\" parameter.');\r\n }\r\n super(params, $element);\r\n this.title = ko.pureComputed(() => {\r\n if (typeof params.title !== 'undefined') {\r\n let title = ko.unwrap(params.title);\r\n if ((title !== null) && (typeof title !== 'undefined')) {\r\n return title.toString();\r\n }\r\n }\r\n if (this.uiElement) {\r\n return this.uiElement.title;\r\n }\r\n return '';\r\n });\r\n this.childComponents = ko.pureComputed(() => {\r\n const result = ko.unwrap(this.inputChildren)\r\n .map(child => this.mapChildToComponentBinding(child))\r\n .filter(binding => binding !== null);\r\n //TypeScript does not recognize that the filter() call above removes\r\n //all null values, so we need an explicit cast.\r\n return result;\r\n });\r\n }\r\n mapChildToComponentBinding(child) {\r\n //Does not map any children by default.\r\n return null;\r\n }\r\n dispose() {\r\n super.dispose();\r\n this.childComponents.dispose();\r\n }\r\n}\r\n//endregion\r\n//region Control\r\nexport class KoControlViewModel extends KoComponentViewModel {\r\n constructor(params, $element) {\r\n var _a;\r\n super(params, $element);\r\n this.settings =\r\n ((typeof params.settings === 'object') && isSettingMap(params.settings))\r\n ? params.settings\r\n : {};\r\n if (typeof this.settings.value !== 'undefined') {\r\n this.valueProxy = this.settings.value.value;\r\n }\r\n else {\r\n this.valueProxy = ko.pureComputed(() => {\r\n console.error('Missing \"value\" setting for a control component.', this.settings, params);\r\n return '';\r\n });\r\n }\r\n //Input ID will be provided by the server if applicable.\r\n this.primaryInputId = (typeof params.primaryInputId === 'string') ? params.primaryInputId : null;\r\n this.inputAttributes = ko.pureComputed(() => {\r\n var _a;\r\n const attributes = ((_a = this.uiElement) === null || _a === void 0 ? void 0 : _a.inputAttributes) || {};\r\n const inputId = this.getPrimaryInputId();\r\n if (inputId !== null) {\r\n attributes.id = inputId;\r\n }\r\n //Note: The \"name\" field is not used because these controls are entirely JS-driven.\r\n const additionalAttributes = this.getAdditionalInputAttributes();\r\n for (const key in additionalAttributes) {\r\n if (!additionalAttributes.hasOwnProperty(key)) {\r\n continue;\r\n }\r\n attributes[key] = additionalAttributes[key];\r\n }\r\n return attributes;\r\n });\r\n if ((typeof params.label !== 'undefined') && (params.label !== null)) {\r\n const unwrappedLabel = ko.unwrap(params.label);\r\n this.label = (typeof unwrappedLabel === 'undefined') ? '' : unwrappedLabel.toString();\r\n }\r\n else {\r\n this.label = ((_a = this.uiElement) === null || _a === void 0 ? void 0 : _a.label) || '';\r\n }\r\n }\r\n get inputClasses() {\r\n var _a;\r\n return ((_a = this.uiElement) === null || _a === void 0 ? void 0 : _a.inputClasses) || [];\r\n }\r\n // noinspection JSUnusedGlobalSymbols -- Used in Knockout templates.\r\n get inputClassString() {\r\n return this.inputClasses.join(' ');\r\n }\r\n getAdditionalInputAttributes() {\r\n return {};\r\n }\r\n getPrimaryInputId() {\r\n return this.primaryInputId;\r\n }\r\n}\r\nfunction isSettingMap(value) {\r\n if (value === null) {\r\n return false;\r\n }\r\n if (typeof value !== 'object') {\r\n return false;\r\n }\r\n const valueAsRecord = value;\r\n for (const key in valueAsRecord) {\r\n if (!valueAsRecord.hasOwnProperty(key)) {\r\n continue;\r\n }\r\n if (!(valueAsRecord[key] instanceof Setting)) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\n/**\r\n * A control that doesn't use or need a UI element instance, but can still have\r\n * settings and other parameters typically associated with controls.\r\n */\r\nexport class KoStandaloneControl extends KoControlViewModel {\r\n}\r\n/**\r\n * A control that requires a UI element of the \"Control\" class.\r\n */\r\nexport class KoDependentControl extends KoControlViewModel {\r\n getExpectedUiElementType() {\r\n return Control;\r\n }\r\n}\r\nexport function createControlComponentConfig(ctor, templateString) {\r\n return {\r\n viewModel: {\r\n createViewModel: makeCreateVmFunctionForComponent(ctor),\r\n },\r\n template: templateString,\r\n };\r\n}\r\n//endregion\r\n//region Renderer\r\nexport class KoRendererViewModel extends KoComponentViewModel {\r\n constructor(params, $element) {\r\n super(params, $element);\r\n if ((typeof params.structure !== 'object') || !(params.structure instanceof InterfaceStructure)) {\r\n throw new Error('Invalid interface structure for a renderer component.');\r\n }\r\n this.structure = params.structure;\r\n }\r\n}\r\nexport function createRendererComponentConfig(ctor, templateString) {\r\n return {\r\n viewModel: {\r\n createViewModel: makeCreateVmFunctionForComponent(ctor),\r\n },\r\n template: templateString,\r\n };\r\n}\r\n//endregion\r\n//# sourceMappingURL=control-base.js.map","/// \r\n/**\r\n * This is a wrapper for the popup slider that initializes the slider on first use.\r\n * It's useful for Knockout components.\r\n */\r\nexport class LazyPopupSliderAdapter {\r\n constructor(sliderRanges, containerSelector = '.ame-container-with-popup-slider', inputSelector = 'input', sliderOptions = {}) {\r\n this.sliderRanges = sliderRanges;\r\n this.containerSelector = containerSelector;\r\n this.inputSelector = inputSelector;\r\n this.sliderOptions = sliderOptions;\r\n this.slider = null;\r\n if (!sliderOptions.hasOwnProperty('ranges')) {\r\n sliderOptions.ranges = sliderRanges;\r\n }\r\n this.handleKoClickEvent = ($data, event) => {\r\n //Verify that this is one of the inputs we're interested in.\r\n //Also, disabled inputs should not trigger the slider.\r\n if (event.target === null) {\r\n return;\r\n }\r\n const $input = jQuery(event.target);\r\n if ($input.is(':disabled') || !$input.is(this.inputSelector)) {\r\n return;\r\n }\r\n //Short-circuit if the slider is already initialized.\r\n if (this.slider) {\r\n this.slider.showForInput($input);\r\n return;\r\n }\r\n //Some sanity checks.\r\n if (typeof AmePopupSlider === 'undefined') {\r\n return;\r\n }\r\n const $container = $input.closest(this.containerSelector);\r\n if ($container.length < 1) {\r\n return;\r\n }\r\n this.initSlider($container);\r\n if (this.slider !== null) {\r\n //TS doesn't realize that this.initSlider() will initialize the slider.\r\n this.slider.showForInput($input);\r\n }\r\n };\r\n }\r\n /**\r\n * Initialize the slider if it's not already initialized.\r\n */\r\n initSlider($container) {\r\n if (this.slider) {\r\n return;\r\n }\r\n //In HTML, we would pass the range data as a \"data-slider-ranges\" attribute,\r\n //but here they are passed via the \"ranges\" option (see the constructor).\r\n this.slider = AmePopupSlider.createSlider($container, this.sliderOptions);\r\n }\r\n}\r\n//# sourceMappingURL=lazy-popup-slider-adapter.js.map"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/dist/build.manifest.json b/dist/build.manifest.json new file mode 100644 index 0000000..b0bbd70 --- /dev/null +++ b/dist/build.manifest.json @@ -0,0 +1,7 @@ +{ + "admin-customizer": [ + "runtime.bundle.js", + "customizable.bundle.js", + "admin-customizer.bundle.js" + ] +} \ No newline at end of file diff --git a/dist/customizable.bundle.js b/dist/customizable.bundle.js new file mode 100644 index 0000000..6a5f370 --- /dev/null +++ b/dist/customizable.bundle.js @@ -0,0 +1,851 @@ +"use strict"; +(self["wsAmeWebpackChunk"] = self["wsAmeWebpackChunk"] || []).push([["customizable"],{ + +/***/ "./extras/pro-customizables/assets/customizable.js": +/*!*********************************************************!*\ + !*** ./extras/pro-customizables/assets/customizable.js ***! + \*********************************************************/ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AmeCustomizable": () => (/* binding */ AmeCustomizable), +/* harmony export */ "AmeCustomizableViewModel": () => (/* binding */ AmeCustomizableViewModel) +/* harmony export */ }); + +var AmeCustomizable; +(function (AmeCustomizable) { + var some = AmeMiniFunc.some; + var none = AmeMiniFunc.none; + var Either = AmeMiniFunc.Either; + const _ = wsAmeLodash; + class Setting { + constructor(id, value = null, defaultValue = null, supportsPostMessage = false, groupTitle = null, validator = null) { + this.validator = validator; + this.groupTitle = null; + /** + * The last value that was tried to be set. This is used to ignore server-side + * validation errors when the input value has changed since the request was sent. + * + * Displayed validation errors should be relevant to what the user tried + * to enter, not the currently stored setting value. + */ + this.lastTriedNewValue = null; + this.id = id; + this.underlyingValue = ko.observable(value); + this.defaultValue = defaultValue; + this.supportsPostMessage = supportsPostMessage; + this.groupTitle = groupTitle; + this.lastTriedNewValue = value; + this.value = ko.computed({ + read: () => this.underlyingValue(), + write: (newValue) => { + const errors = this.tryUpdate(newValue); + if (errors && (errors.length > 0)) { + /* + We could revert to the previous value here, but there are some cases where + that would interfere with the user's input. For example, if the user is + manually typing in a URL, the value will be temporarily invalid until they + finish entering the protocol and domain name. If we revert to the previous + value, the user will have to start over. + + Instead, let's leave the invalid value in place and let the user fix it. + */ + } + }, + owner: this + }); + this.validationErrors = ko.observableArray(); + this.isValid = ko.computed(() => { + return (this.validationErrors().length === 0); + }); + } + tryUpdate(newValue) { + this.lastTriedNewValue = newValue; + const oldValue = this.underlyingValue(); + //Clear validation errors. + this.validationErrors.removeAll(); + //Validate and sanitize the new value. + const [sanitizedValue, errors] = this.validate(newValue); + this.validationErrors.push(...errors); + if (errors.length > 0) { + return errors; + } + //Remember the last validation subject so that server-side validation results + //can be ignored if the value has changed since the request was sent. + this.lastTriedNewValue = sanitizedValue; + //Only update the underlying value if it has changed. + if (sanitizedValue !== oldValue) { + this.underlyingValue(sanitizedValue); + } + return []; + } + validate(newValue) { + if (this.validator !== null) { + const result = this.validator.check(newValue); + if (result.isLeft()) { + return [newValue, [result.value]]; + } + else if (result.isRight()) { + newValue = result.value; + } + } + return [newValue, []]; + } + /** + * Add validation errors to the setting if the current value still + * matches the given value. + * + * This is intended as a way to add validation errors that were produced + * asynchronously, such as by sending the value to the server for validation. + * The setting's value can change while the validation is in progress, + * so we need to check that the validated value matches the current one. + * + * @param subjectValue + * @param errors + */ + addValidationErrorsForValue(subjectValue, errors) { + if (this.lastTriedNewValue !== subjectValue) { + return; + } + //Add the error(s) only if there is no existing error with the same code. + const existingCodes = _.indexBy(this.validationErrors(), 'code'); + for (const error of errors) { + if ((typeof error.code === 'undefined') || !existingCodes.hasOwnProperty(error.code)) { + this.validationErrors.push(error); + } + } + } + clearValidationErrorsForValue(subjectValue) { + if (this.lastTriedNewValue !== subjectValue) { + return; + } + this.validationErrors.removeAll(); + } + } + AmeCustomizable.Setting = Setting; + function unserializeSettingMap(settings) { + const collection = new SettingCollection(); + for (const settingId in settings) { + if (!settings.hasOwnProperty(settingId)) { + continue; + } + const definition = settings[settingId]; + collection.add(unserializeSetting(settingId, definition)); + } + return collection; + } + AmeCustomizable.unserializeSettingMap = unserializeSettingMap; + function unserializeSetting(settingId, definition) { + return new Setting(settingId, (typeof definition.value !== 'undefined') ? definition.value : null, (typeof definition.defaultValue !== 'undefined') ? definition.defaultValue : null, (typeof definition.supportsPostMessage !== 'undefined') ? definition.supportsPostMessage : false, (typeof definition.groupTitle !== 'undefined') ? definition.groupTitle : null, (typeof definition.validation !== 'undefined') ? (new Validator(definition.validation)) : null); + } + AmeCustomizable.unserializeSetting = unserializeSetting; + const BuiltinParsers = { + 'numeric': (value, config) => { + //In some UI controls the observable value is updated as the user types, + //so this parser/validator should be tolerant and accept partial values. + let parsed; + let sanitized; + if (typeof value === 'number') { + parsed = sanitized = value; + } + else { + sanitized = (typeof value === 'string') ? value : String(value); + sanitized = AmeMiniFunc.sanitizeNumericString(sanitized); + parsed = parseFloat(sanitized); + if (isNaN(parsed)) { + return Either.left({ + message: 'Value must be a number.', + code: 'invalid_number' + }); + } + } + if (config) { + if ((typeof config.min !== 'undefined') && parsed < config.min) { + return Either.left({ + message: `Value must be ${config.min} or greater`, + code: 'min_value' + }); + } + if (typeof config.max !== 'undefined' && parsed > config.max) { + return Either.left({ + message: `Value must be ${config.max} or lower`, + code: 'max_value' + }); + } + } + return Either.right(sanitized); + }, + 'int': (value) => { + let parsed = (typeof value === 'number') ? value : parseInt(String(value), 10); + if (isNaN(parsed)) { + return Either.left({ + message: 'Value must be a number.', + code: 'invalid_type' + }); + } + parsed = Math.floor(parsed); + return Either.right(parsed); + } + }; + class Validator { + constructor(config) { + this.config = config; + this.parsers = []; + //Converting to null is only allowed if the setting is nullable. + if (config.convertEsToNull && !config.isNullable) { + throw new Error('convertEsToNull is only allowed if the setting is nullable.'); + } + if (config.parsers) { + for (const [parserId, parserConfig] of config.parsers) { + if (!BuiltinParsers.hasOwnProperty(parserId)) { + throw new Error(`Unknown parser: ${parserId}`); + } + this.parsers.push([BuiltinParsers[parserId], parserConfig]); + } + } + } + check(value) { + if (value === null) { + if (this.config.isNullable) { + return Either.right(value); + } + else { + return Either.left({ + message: 'This setting cannot be null.' + }); + } + } + if (typeof value === 'string') { + if (this.config.convertEsToNull && (value === '')) { + return Either.right(null); + } + } + for (const [parser, parserConfig] of this.parsers) { + const result = parser(value, (parserConfig === null) ? undefined : parserConfig); + if (result.isLeft()) { + return result; + } + else if (result.isRight()) { + value = result.value; + } + } + return Either.right(value); + } + } + class SettingCollection { + constructor() { + this.settings = {}; + /** + * Adding settings to an observable array makes it easier to automatically + * update computed values like "are any settings invalid?". + */ + this.observableSettings = ko.observableArray(); + const self = this; + this.hasValidationErrors = ko.pureComputed(() => { + return _.some(self.observableSettings(), (setting) => { + return !setting.isValid(); + }); + }); + this.changeListeners = new Map(); + } + get(id) { + if (this.settings.hasOwnProperty(id)) { + return some(this.settings[id]); + } + return none; + } + add(setting) { + this.settings[setting.id] = setting; + this.observableSettings.push(setting); + setting.value.subscribe((newValue) => this.onSettingChanged(setting, newValue)); + } + onSettingChanged(setting, newValue) { + this.notifyChangeListeners(setting, newValue); + } + /** + * Add a callback that will be called whenever the value of a setting changes. + * + * @param callback + */ + addChangeListener(callback) { + const id = Symbol(); + this.changeListeners.set(id, callback); + return id; + } + removeChangeListener(id) { + this.changeListeners.delete(id); + } + notifyChangeListeners(setting, newValue) { + for (const listener of this.changeListeners.values()) { + listener(setting, newValue); + } + } + getAllSettingIds() { + return Object.keys(this.settings); + } + getAllSettingValues() { + const values = {}; + for (const id in this.settings) { + if (this.settings.hasOwnProperty(id)) { + values[id] = this.settings[id].value(); + } + } + return values; + } + } + AmeCustomizable.SettingCollection = SettingCollection; + function isSettingConditionData(data) { + if ((typeof data !== 'object') || (data === null)) { + return false; + } + const dataAsRecord = data; + return (typeof dataAsRecord.settingId === 'string' + && typeof dataAsRecord.op === 'string' + && typeof dataAsRecord.value !== 'undefined'); + } + class SettingCondition { + constructor(setting, op, value) { + this.setting = setting; + this.op = op; + this.value = value; + } + evaluate() { + const settingValue = this.setting.value(); + switch (this.op) { + case '==': + //Note the intentional use of == instead of ===. + return settingValue == this.value; + case '!=': + return settingValue != this.value; + case '>': + return settingValue > this.value; + case '<': + return settingValue < this.value; + case '>=': + return settingValue >= this.value; + case '<=': + return settingValue <= this.value; + case 'falsy': + return !settingValue; + case 'truthy': + return !!settingValue; + } + } + static fromData(data, findSetting) { + const setting = findSetting(data.settingId); + if (!setting || setting.isEmpty()) { + throw new Error(`Setting with ID "${data.settingId}" not found for SettingCondition`); + } + return new SettingCondition(setting.get(), data.op, data.value); + } + } + AmeCustomizable.SettingCondition = SettingCondition; + class UiElement { + constructor(data, children = []) { + this.component = data.component || ''; + this.id = data.id || ''; + this.description = data.description || ''; + this.classes = data.classes || []; + this.styles = data.styles || {}; + this.componentParams = data.params || {}; + this.children = children; + } + getComponentParams() { + return Object.assign(Object.assign({}, this.componentParams), { uiElement: this, description: this.description, classes: this.classes, styles: this.styles, children: this.children }); + } + } + AmeCustomizable.UiElement = UiElement; + class Container extends UiElement { + constructor(data, children = []) { + super(data, children); + this.title = data.title; + } + replaceChild(oldChild, newChild) { + const index = this.children.indexOf(oldChild); + if (index === -1) { + throw new Error('Child not found'); + } + this.children[index] = newChild; + } + replaceChildByIndex(index, newChild) { + this.children[index] = newChild; + } + } + AmeCustomizable.Container = Container; + class Section extends Container { + constructor(data, children = []) { + super(data, children); + this.preferredRole = data.preferredRole || 'navigation'; + } + } + AmeCustomizable.Section = Section; + class ControlGroup extends Container { + constructor(data, children = [], enabled = null) { + super(data, children); + this.enabled = enabled || ko.observable(true); + this.labelFor = data.labelFor || null; + } + getComponentParams() { + return Object.assign(Object.assign({}, super.getComponentParams()), { enabled: this.enabled }); + } + } + AmeCustomizable.ControlGroup = ControlGroup; + class InterfaceStructure extends Container { + constructor(data, children = []) { + super(data, children); + } + getAsSections() { + let currentAnonymousSection = null; + let sections = []; + for (const child of this.children) { + if (child instanceof Section) { + sections.push(child); + currentAnonymousSection = null; + } + else { + if (!currentAnonymousSection) { + currentAnonymousSection = new Section({ + t: 'section', + title: '', + children: [] + }); + sections.push(currentAnonymousSection); + } + currentAnonymousSection.children.push(child); + } + } + return sections; + } + } + AmeCustomizable.InterfaceStructure = InterfaceStructure; + class Control extends UiElement { + constructor(data, settings = {}, enabled = null, children = []) { + super(data, children); + this.label = data.label; + this.settings = settings; + this.inputClasses = data.inputClasses || []; + this.inputAttributes = data.inputAttributes || {}; + this.enabled = enabled || ko.observable(true); + // noinspection PointlessBooleanExpressionJS -- Might not actually be a boolean if sent from the server. + this.includesOwnLabel = (typeof data.includesOwnLabel !== 'undefined') ? (!!data.includesOwnLabel) : false; + this.labelTargetId = data.labelTargetId || ''; + this.primaryInputId = data.primaryInputId || ''; + this.settingValidationErrors = ko.pureComputed(() => { + const errors = []; + for (const [settingId, setting] of Object.entries(this.settings)) { + const settingErrors = setting.validationErrors(); + if (settingErrors.length > 0) { + for (const error of settingErrors) { + errors.push([settingId, error]); + } + } + } + return errors; + }); + } + getComponentParams() { + return Object.assign(Object.assign({}, super.getComponentParams()), { settings: this.settings, enabled: this.enabled, label: this.label, primaryInputId: this.primaryInputId }); + } + getAutoGroupTitle() { + if (this.settings['value']) { + const customGroupTitle = this.settings['value'].groupTitle; + if (customGroupTitle) { + return customGroupTitle; + } + } + return this.label; + } + /** + * Create a control group wrapper with this control as its only child. + */ + createControlGroup() { + let title = this.getAutoGroupTitle(); + //Some controls like the checkbox already show their own label. + //Don't add a group title in that case. + if (this.includesOwnLabel) { + title = ''; + } + const data = { + t: 'control-group', + title: title + }; + if (this.labelTargetId) { + data.labelFor = this.labelTargetId; + } + return new ControlGroup(data, [this], this.enabled); + } + } + AmeCustomizable.Control = Control; + function unserializeUiElement(data, findSetting, dataCustomizer) { + if (typeof dataCustomizer === 'function') { + dataCustomizer(data); + } + const dataAsRecord = data; + //Unserialize children recursively. + let children = []; + if ((typeof dataAsRecord['children'] !== 'undefined') && Array.isArray(dataAsRecord['children'])) { + for (const childData of dataAsRecord['children']) { + children.push(unserializeUiElement(childData, findSetting, dataCustomizer)); + } + } + //Unserialize the "enabled" condition. + let enabled = null; + if ((data.t === 'control') || (data.t === 'control-group')) { + if (typeof data.enabled !== 'undefined') { + if (isSettingConditionData(data.enabled)) { + const condition = SettingCondition.fromData(data.enabled, findSetting); + enabled = ko.pureComputed(() => condition.evaluate()); + } + else { + enabled = ko.pureComputed(() => !!data.enabled); + } + } + else { + enabled = ko.observable(true); + } + } + switch (data.t) { + case 'section': + return new Section(data, children); + case 'control-group': + return new ControlGroup(data, children, enabled); + case 'structure': + return new InterfaceStructure(data, children); + case 'control': + let settings = {}; + if (data.settings) { + for (const childName in data.settings) { + if (data.settings.hasOwnProperty(childName)) { + const settingId = data.settings[childName]; + const setting = findSetting(settingId); + if (setting.isDefined()) { + settings[childName] = setting.get(); + } + else { + throw new Error('Unknown setting "' + settingId + '" referenced by control "' + data.label + '".'); + } + } + } + } + return new Control(data, settings, enabled, children); + } + } + AmeCustomizable.unserializeUiElement = unserializeUiElement; + class SettingReaderRegistry { + constructor() { + this.notFound = {}; + this.valueReaders = []; + } + registerValueReader(getter, idPrefix = null) { + this.valueReaders.push({ getter, idPrefix }); + } + /** + * Try to find a setting in a registered setting reader. + */ + getValue(settingId) { + for (const { getter, idPrefix } of this.valueReaders) { + if ((idPrefix !== null) && !(settingId.startsWith(idPrefix))) { + continue; + } + const result = getter(settingId, this.notFound); + if (result !== this.notFound) { + return some(result); + } + } + return none; + } + } + AmeCustomizable.SettingReaderRegistry = SettingReaderRegistry; + class PreviewRegistry { + constructor(previewValueGetter) { + this.previewValueGetter = previewValueGetter; + this.settingPreviewUpdaters = {}; + this.notFound = {}; + this.allPreviewUpdaters = ko.observableArray([]); + } + preview(settingId, value) { + if (!this.settingPreviewUpdaters.hasOwnProperty(settingId)) { + return; + } + const updaters = this.settingPreviewUpdaters[settingId]; + for (const updater of updaters) { + updater.preview(settingId, value, this.previewValueGetter); + } + } + clearPreview() { + for (const updater of this.allPreviewUpdaters()) { + updater.clearPreview(); + } + } + registerPreviewUpdater(settingIds, updater) { + for (const settingId of settingIds) { + if (!this.settingPreviewUpdaters.hasOwnProperty(settingId)) { + this.settingPreviewUpdaters[settingId] = []; + } + this.settingPreviewUpdaters[settingId].push(updater); + } + if (this.allPreviewUpdaters.indexOf(updater) < 0) { + this.allPreviewUpdaters.push(updater); + } + } + registerPreviewCallback(settingId, callback) { + this.registerPreviewUpdater([settingId], new PreviewCallbackWrapper(callback)); + } + canPreview(settingId) { + return (this.settingPreviewUpdaters.hasOwnProperty(settingId) + && (this.settingPreviewUpdaters[settingId].length > 0)); + } + } + AmeCustomizable.PreviewRegistry = PreviewRegistry; + class PreviewCallbackWrapper { + constructor(callback) { + this.callback = callback; + } + preview(settingId, value, getSettingValue) { + this.callback(value); + } + clearPreview() { + //Nothing to do in this case. + } + } + class ThrottledPreviewRegistry extends PreviewRegistry { + constructor(previewValueGetter, minPreviewRefreshInterval = 40) { + super(previewValueGetter); + this.minPreviewRefreshInterval = minPreviewRefreshInterval; + this.pendingSettings = {}; + this.throttledUpdate = throttleAnimationFrame(this.applyPendingUpdates.bind(this), this.minPreviewRefreshInterval); + } + queuePreview(settingId) { + this.pendingSettings[settingId] = true; + this.throttledUpdate(); + } + applyPendingUpdates() { + //Cancel any pending updates in case this method was called directly. + this.throttledUpdate.cancel(); + const pendingSettingIds = Object.keys(this.pendingSettings); + if (pendingSettingIds.length === 0) { + return; + } + this.updatePreview(pendingSettingIds); + this.pendingSettings = {}; + } + /** + * Update the preview for the specified settings. + * + * This method is called by the throttled update function, but it can also be called + * directly if necessary, e.g. to update the preview for all settings when the user + * opens a settings screen for the first time. Note that calling it will *not* cancel + * pending updates. + * + * @param settingIds + */ + updatePreview(settingIds) { + if (settingIds.length < 1) { + return; + } + for (const settingId of settingIds) { + const value = this.previewValueGetter(settingId, this.notFound); + if (value !== this.notFound) { + this.preview(settingId, value); + } + } + } + clearPreview() { + this.throttledUpdate.cancel(); + this.pendingSettings = {}; + super.clearPreview(); + } + } + AmeCustomizable.ThrottledPreviewRegistry = ThrottledPreviewRegistry; + /** + * Creates a throttled function that runs the specified callback at most once + * every `minInterval` milliseconds. + * + * The callback is always invoked using `requestAnimationFrame()`, so it will be delayed + * until the next frame even if the required interval has already passed. + */ + function throttleAnimationFrame(callback, minInterval = 0) { + /** + * Expected time between animation frames. Intervals shorter than this will be ineffective. + */ + const expectedFrameTime = 1000 / 60; + /** + * The threshold at which we will use `setTimeout()` instead of `requestAnimationFrame()`. + */ + const timeoutThreshold = Math.max(1000 / 20, expectedFrameTime * 2 + 1); + const epsilon = 0.001; + let requestAnimationFrameId = null; + let timerId = null; + let lastCallTimestamp = 0; + let nextCallTimestamp = 0; + function animationCallback() { + requestAnimationFrameId = null; + const now = Date.now(); + if (nextCallTimestamp <= now) { + lastCallTimestamp = now; + callback(); + return; + } + else { + requestAnimationFrameId = window.requestAnimationFrame(animationCallback); + } + } + const invoke = () => { + if ((requestAnimationFrameId !== null) || (timerId !== null)) { + return; //Already scheduled. + } + nextCallTimestamp = lastCallTimestamp + minInterval; + const now = Date.now(); + if (nextCallTimestamp <= now) { + nextCallTimestamp = now + expectedFrameTime - epsilon; + } + //Two-stage throttling: If the remaining time is large, use setTimeout(). + //If it's small, use requestAnimationFrame() and go frame by frame. + const remainingTime = nextCallTimestamp - now; + if (remainingTime > timeoutThreshold) { + timerId = window.setTimeout(() => { + timerId = null; + requestAnimationFrameId = window.requestAnimationFrame(animationCallback); + }, remainingTime - (expectedFrameTime / 2)); + } + else { + //Use requestAnimationFrame. + requestAnimationFrameId = window.requestAnimationFrame(animationCallback); + } + }; + invoke.cancel = () => { + if (requestAnimationFrameId !== null) { + window.cancelAnimationFrame(requestAnimationFrameId); + requestAnimationFrameId = null; + } + if (timerId !== null) { + window.clearTimeout(timerId); + timerId = null; + } + }; + return invoke; + } + //endregion +})(AmeCustomizable || (AmeCustomizable = {})); +var AmeCustomizableViewModel; +(function (AmeCustomizableViewModel) { + var SettingCollection = AmeCustomizable.SettingCollection; + var Setting = AmeCustomizable.Setting; + var ThrottledPreviewRegistry = AmeCustomizable.ThrottledPreviewRegistry; + var SettingReaderRegistry = AmeCustomizable.SettingReaderRegistry; + var lift = AmeMiniFunc.lift; + class SimpleVm extends ThrottledPreviewRegistry { + constructor(extraPreviewCondition = null) { + const getSettingValue = (settingId, defaultResult) => { + const setting = this.getOrCreateKnownSetting(settingId); + if (setting !== null) { + return setting.value(); + } + return defaultResult; + }; + super(getSettingValue, 40); + this.previewDesired = ko.observable(false); + this.settings = new SettingCollection(); + this.settingReaders = new SettingReaderRegistry(); + this.isPreviewPossible = ko.pureComputed(() => { + return this.allPreviewUpdaters().length > 0; + }); + this.isPreviewEnabled = ko.computed({ + read: () => { + if (extraPreviewCondition !== null) { + if (!extraPreviewCondition()) { + return false; + } + } + return this.getPreviewActiveState(); + }, + write: (newValue) => { + this.previewDesired(newValue); + if (newValue && !this.getPreviewActiveState()) { + //Can't actually enable preview. Reset the checkbox/other input. + this.isPreviewEnabled.notifySubscribers(); + } + } + }); + this.isPreviewEnabled.subscribe((newValue) => { + if (newValue) { + this.updatePreview(this.settings.getAllSettingIds()); + } + else { + this.clearPreview(); + } + }); + this.settings.addChangeListener((setting) => { + if (!this.isPreviewEnabled()) { + return; + } + this.queuePreview(setting.id); + }); + } + getSettingObservable(settingId, unusedDefaultValue = null) { + const result = this.getOrCreateKnownSetting(settingId); + if (result !== null) { + return result.value; + } + throw new Error('Unknown setting ID: ' + settingId); + } + getOrCreateKnownSetting(settingId) { + const result = this.settings.get(settingId); + if (result.isDefined()) { + return result.get(); + } + const foundValue = this.settingReaders.getValue(settingId); + if (foundValue.isDefined()) { + const setting = new Setting(settingId, foundValue.get()); + this.settings.add(setting); + return setting; + } + return null; + } + registerSettingReader(reader, idPrefix = null) { + this.settingReaders.registerValueReader(reader, idPrefix); + } + getPreviewActiveState() { + return this.previewDesired() && this.isPreviewPossible(); + } + getAllSettingValues() { + return this.settings.getAllSettingValues(); + } + /** + * Reread all settings from the value readers. This will be used to reload settings + * in case the underlying configuration is reset or a new configuration is loaded. + */ + reloadAllSettings() { + for (const settingId of this.settings.getAllSettingIds()) { + lift([this.settings.get(settingId), this.settingReaders.getValue(settingId)], (setting, newValue) => setting.value(newValue)); + } + } + } + AmeCustomizableViewModel.SimpleVm = SimpleVm; + // noinspection JSUnusedGlobalSymbols -- Not used right now, but kept for testing and prototyping purposes. + class NullVm { + constructor() { + this.settings = new SettingCollection(); + } + getSettingObservable(settingId, defaultValue = null) { + const existingSetting = this.settings.get(settingId); + if (existingSetting.isDefined()) { + return existingSetting.get().value; + } + const setting = new Setting(settingId, defaultValue); + this.settings.add(setting); + return setting.value; + } + getAllSettingValues() { + return this.settings.getAllSettingValues(); + } + } + AmeCustomizableViewModel.NullVm = NullVm; +})(AmeCustomizableViewModel || (AmeCustomizableViewModel = {})); +//# sourceMappingURL=customizable.js.map + +/***/ }) + +}]); +//# sourceMappingURL=customizable.bundle.js.map \ No newline at end of file diff --git a/dist/customizable.bundle.js.map b/dist/customizable.bundle.js.map new file mode 100644 index 0000000..a1c7302 --- /dev/null +++ b/dist/customizable.bundle.js.map @@ -0,0 +1 @@ +{"version":3,"file":"customizable.bundle.js","mappings":";;;;;;;;;;;;;;AAAa;AACN;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA,aAAa;AACb;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA,kDAAkD,YAAY;AAC9D;AACA,qBAAqB;AACrB;AACA;AACA;AACA,kDAAkD,YAAY;AAC9D;AACA,qBAAqB;AACrB;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,2DAA2D,SAAS;AACpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oDAAoD,eAAe;AACnE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAiD,2BAA2B,qHAAqH;AACjM;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAiD,iCAAiC,uBAAuB;AACzG;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yBAAyB;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uCAAuC;AACvC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA,iDAAiD,iCAAiC,wGAAwG;AAC1L;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qCAAqC,kBAAkB;AACvD;AACA;AACA;AACA;AACA;AACA,yBAAyB,mBAAmB;AAC5C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,wBAAwB;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,0CAA0C;AACpC;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,4DAA4D;AAC7D","sources":["webpack:///./extras/pro-customizables/assets/customizable.js"],"sourcesContent":["'use strict';\r\nexport var AmeCustomizable;\r\n(function (AmeCustomizable) {\r\n var some = AmeMiniFunc.some;\r\n var none = AmeMiniFunc.none;\r\n var Either = AmeMiniFunc.Either;\r\n const _ = wsAmeLodash;\r\n class Setting {\r\n constructor(id, value = null, defaultValue = null, supportsPostMessage = false, groupTitle = null, validator = null) {\r\n this.validator = validator;\r\n this.groupTitle = null;\r\n /**\r\n * The last value that was tried to be set. This is used to ignore server-side\r\n * validation errors when the input value has changed since the request was sent.\r\n *\r\n * Displayed validation errors should be relevant to what the user tried\r\n * to enter, not the currently stored setting value.\r\n */\r\n this.lastTriedNewValue = null;\r\n this.id = id;\r\n this.underlyingValue = ko.observable(value);\r\n this.defaultValue = defaultValue;\r\n this.supportsPostMessage = supportsPostMessage;\r\n this.groupTitle = groupTitle;\r\n this.lastTriedNewValue = value;\r\n this.value = ko.computed({\r\n read: () => this.underlyingValue(),\r\n write: (newValue) => {\r\n const errors = this.tryUpdate(newValue);\r\n if (errors && (errors.length > 0)) {\r\n /*\r\n We could revert to the previous value here, but there are some cases where\r\n that would interfere with the user's input. For example, if the user is\r\n manually typing in a URL, the value will be temporarily invalid until they\r\n finish entering the protocol and domain name. If we revert to the previous\r\n value, the user will have to start over.\r\n\r\n Instead, let's leave the invalid value in place and let the user fix it.\r\n */\r\n }\r\n },\r\n owner: this\r\n });\r\n this.validationErrors = ko.observableArray();\r\n this.isValid = ko.computed(() => {\r\n return (this.validationErrors().length === 0);\r\n });\r\n }\r\n tryUpdate(newValue) {\r\n this.lastTriedNewValue = newValue;\r\n const oldValue = this.underlyingValue();\r\n //Clear validation errors.\r\n this.validationErrors.removeAll();\r\n //Validate and sanitize the new value.\r\n const [sanitizedValue, errors] = this.validate(newValue);\r\n this.validationErrors.push(...errors);\r\n if (errors.length > 0) {\r\n return errors;\r\n }\r\n //Remember the last validation subject so that server-side validation results\r\n //can be ignored if the value has changed since the request was sent.\r\n this.lastTriedNewValue = sanitizedValue;\r\n //Only update the underlying value if it has changed.\r\n if (sanitizedValue !== oldValue) {\r\n this.underlyingValue(sanitizedValue);\r\n }\r\n return [];\r\n }\r\n validate(newValue) {\r\n if (this.validator !== null) {\r\n const result = this.validator.check(newValue);\r\n if (result.isLeft()) {\r\n return [newValue, [result.value]];\r\n }\r\n else if (result.isRight()) {\r\n newValue = result.value;\r\n }\r\n }\r\n return [newValue, []];\r\n }\r\n /**\r\n * Add validation errors to the setting if the current value still\r\n * matches the given value.\r\n *\r\n * This is intended as a way to add validation errors that were produced\r\n * asynchronously, such as by sending the value to the server for validation.\r\n * The setting's value can change while the validation is in progress,\r\n * so we need to check that the validated value matches the current one.\r\n *\r\n * @param subjectValue\r\n * @param errors\r\n */\r\n addValidationErrorsForValue(subjectValue, errors) {\r\n if (this.lastTriedNewValue !== subjectValue) {\r\n return;\r\n }\r\n //Add the error(s) only if there is no existing error with the same code.\r\n const existingCodes = _.indexBy(this.validationErrors(), 'code');\r\n for (const error of errors) {\r\n if ((typeof error.code === 'undefined') || !existingCodes.hasOwnProperty(error.code)) {\r\n this.validationErrors.push(error);\r\n }\r\n }\r\n }\r\n clearValidationErrorsForValue(subjectValue) {\r\n if (this.lastTriedNewValue !== subjectValue) {\r\n return;\r\n }\r\n this.validationErrors.removeAll();\r\n }\r\n }\r\n AmeCustomizable.Setting = Setting;\r\n function unserializeSettingMap(settings) {\r\n const collection = new SettingCollection();\r\n for (const settingId in settings) {\r\n if (!settings.hasOwnProperty(settingId)) {\r\n continue;\r\n }\r\n const definition = settings[settingId];\r\n collection.add(unserializeSetting(settingId, definition));\r\n }\r\n return collection;\r\n }\r\n AmeCustomizable.unserializeSettingMap = unserializeSettingMap;\r\n function unserializeSetting(settingId, definition) {\r\n return new Setting(settingId, (typeof definition.value !== 'undefined') ? definition.value : null, (typeof definition.defaultValue !== 'undefined') ? definition.defaultValue : null, (typeof definition.supportsPostMessage !== 'undefined') ? definition.supportsPostMessage : false, (typeof definition.groupTitle !== 'undefined') ? definition.groupTitle : null, (typeof definition.validation !== 'undefined') ? (new Validator(definition.validation)) : null);\r\n }\r\n AmeCustomizable.unserializeSetting = unserializeSetting;\r\n const BuiltinParsers = {\r\n 'numeric': (value, config) => {\r\n //In some UI controls the observable value is updated as the user types,\r\n //so this parser/validator should be tolerant and accept partial values.\r\n let parsed;\r\n let sanitized;\r\n if (typeof value === 'number') {\r\n parsed = sanitized = value;\r\n }\r\n else {\r\n sanitized = (typeof value === 'string') ? value : String(value);\r\n sanitized = AmeMiniFunc.sanitizeNumericString(sanitized);\r\n parsed = parseFloat(sanitized);\r\n if (isNaN(parsed)) {\r\n return Either.left({\r\n message: 'Value must be a number.',\r\n code: 'invalid_number'\r\n });\r\n }\r\n }\r\n if (config) {\r\n if ((typeof config.min !== 'undefined') && parsed < config.min) {\r\n return Either.left({\r\n message: `Value must be ${config.min} or greater`,\r\n code: 'min_value'\r\n });\r\n }\r\n if (typeof config.max !== 'undefined' && parsed > config.max) {\r\n return Either.left({\r\n message: `Value must be ${config.max} or lower`,\r\n code: 'max_value'\r\n });\r\n }\r\n }\r\n return Either.right(sanitized);\r\n },\r\n 'int': (value) => {\r\n let parsed = (typeof value === 'number') ? value : parseInt(String(value), 10);\r\n if (isNaN(parsed)) {\r\n return Either.left({\r\n message: 'Value must be a number.',\r\n code: 'invalid_type'\r\n });\r\n }\r\n parsed = Math.floor(parsed);\r\n return Either.right(parsed);\r\n }\r\n };\r\n class Validator {\r\n constructor(config) {\r\n this.config = config;\r\n this.parsers = [];\r\n //Converting to null is only allowed if the setting is nullable.\r\n if (config.convertEsToNull && !config.isNullable) {\r\n throw new Error('convertEsToNull is only allowed if the setting is nullable.');\r\n }\r\n if (config.parsers) {\r\n for (const [parserId, parserConfig] of config.parsers) {\r\n if (!BuiltinParsers.hasOwnProperty(parserId)) {\r\n throw new Error(`Unknown parser: ${parserId}`);\r\n }\r\n this.parsers.push([BuiltinParsers[parserId], parserConfig]);\r\n }\r\n }\r\n }\r\n check(value) {\r\n if (value === null) {\r\n if (this.config.isNullable) {\r\n return Either.right(value);\r\n }\r\n else {\r\n return Either.left({\r\n message: 'This setting cannot be null.'\r\n });\r\n }\r\n }\r\n if (typeof value === 'string') {\r\n if (this.config.convertEsToNull && (value === '')) {\r\n return Either.right(null);\r\n }\r\n }\r\n for (const [parser, parserConfig] of this.parsers) {\r\n const result = parser(value, (parserConfig === null) ? undefined : parserConfig);\r\n if (result.isLeft()) {\r\n return result;\r\n }\r\n else if (result.isRight()) {\r\n value = result.value;\r\n }\r\n }\r\n return Either.right(value);\r\n }\r\n }\r\n class SettingCollection {\r\n constructor() {\r\n this.settings = {};\r\n /**\r\n * Adding settings to an observable array makes it easier to automatically\r\n * update computed values like \"are any settings invalid?\".\r\n */\r\n this.observableSettings = ko.observableArray();\r\n const self = this;\r\n this.hasValidationErrors = ko.pureComputed(() => {\r\n return _.some(self.observableSettings(), (setting) => {\r\n return !setting.isValid();\r\n });\r\n });\r\n this.changeListeners = new Map();\r\n }\r\n get(id) {\r\n if (this.settings.hasOwnProperty(id)) {\r\n return some(this.settings[id]);\r\n }\r\n return none;\r\n }\r\n add(setting) {\r\n this.settings[setting.id] = setting;\r\n this.observableSettings.push(setting);\r\n setting.value.subscribe((newValue) => this.onSettingChanged(setting, newValue));\r\n }\r\n onSettingChanged(setting, newValue) {\r\n this.notifyChangeListeners(setting, newValue);\r\n }\r\n /**\r\n * Add a callback that will be called whenever the value of a setting changes.\r\n *\r\n * @param callback\r\n */\r\n addChangeListener(callback) {\r\n const id = Symbol();\r\n this.changeListeners.set(id, callback);\r\n return id;\r\n }\r\n removeChangeListener(id) {\r\n this.changeListeners.delete(id);\r\n }\r\n notifyChangeListeners(setting, newValue) {\r\n for (const listener of this.changeListeners.values()) {\r\n listener(setting, newValue);\r\n }\r\n }\r\n getAllSettingIds() {\r\n return Object.keys(this.settings);\r\n }\r\n getAllSettingValues() {\r\n const values = {};\r\n for (const id in this.settings) {\r\n if (this.settings.hasOwnProperty(id)) {\r\n values[id] = this.settings[id].value();\r\n }\r\n }\r\n return values;\r\n }\r\n }\r\n AmeCustomizable.SettingCollection = SettingCollection;\r\n function isSettingConditionData(data) {\r\n if ((typeof data !== 'object') || (data === null)) {\r\n return false;\r\n }\r\n const dataAsRecord = data;\r\n return (typeof dataAsRecord.settingId === 'string'\r\n && typeof dataAsRecord.op === 'string'\r\n && typeof dataAsRecord.value !== 'undefined');\r\n }\r\n class SettingCondition {\r\n constructor(setting, op, value) {\r\n this.setting = setting;\r\n this.op = op;\r\n this.value = value;\r\n }\r\n evaluate() {\r\n const settingValue = this.setting.value();\r\n switch (this.op) {\r\n case '==':\r\n //Note the intentional use of == instead of ===.\r\n return settingValue == this.value;\r\n case '!=':\r\n return settingValue != this.value;\r\n case '>':\r\n return settingValue > this.value;\r\n case '<':\r\n return settingValue < this.value;\r\n case '>=':\r\n return settingValue >= this.value;\r\n case '<=':\r\n return settingValue <= this.value;\r\n case 'falsy':\r\n return !settingValue;\r\n case 'truthy':\r\n return !!settingValue;\r\n }\r\n }\r\n static fromData(data, findSetting) {\r\n const setting = findSetting(data.settingId);\r\n if (!setting || setting.isEmpty()) {\r\n throw new Error(`Setting with ID \"${data.settingId}\" not found for SettingCondition`);\r\n }\r\n return new SettingCondition(setting.get(), data.op, data.value);\r\n }\r\n }\r\n AmeCustomizable.SettingCondition = SettingCondition;\r\n class UiElement {\r\n constructor(data, children = []) {\r\n this.component = data.component || '';\r\n this.id = data.id || '';\r\n this.description = data.description || '';\r\n this.classes = data.classes || [];\r\n this.styles = data.styles || {};\r\n this.componentParams = data.params || {};\r\n this.children = children;\r\n }\r\n getComponentParams() {\r\n return Object.assign(Object.assign({}, this.componentParams), { uiElement: this, description: this.description, classes: this.classes, styles: this.styles, children: this.children });\r\n }\r\n }\r\n AmeCustomizable.UiElement = UiElement;\r\n class Container extends UiElement {\r\n constructor(data, children = []) {\r\n super(data, children);\r\n this.title = data.title;\r\n }\r\n replaceChild(oldChild, newChild) {\r\n const index = this.children.indexOf(oldChild);\r\n if (index === -1) {\r\n throw new Error('Child not found');\r\n }\r\n this.children[index] = newChild;\r\n }\r\n replaceChildByIndex(index, newChild) {\r\n this.children[index] = newChild;\r\n }\r\n }\r\n AmeCustomizable.Container = Container;\r\n class Section extends Container {\r\n constructor(data, children = []) {\r\n super(data, children);\r\n this.preferredRole = data.preferredRole || 'navigation';\r\n }\r\n }\r\n AmeCustomizable.Section = Section;\r\n class ControlGroup extends Container {\r\n constructor(data, children = [], enabled = null) {\r\n super(data, children);\r\n this.enabled = enabled || ko.observable(true);\r\n this.labelFor = data.labelFor || null;\r\n }\r\n getComponentParams() {\r\n return Object.assign(Object.assign({}, super.getComponentParams()), { enabled: this.enabled });\r\n }\r\n }\r\n AmeCustomizable.ControlGroup = ControlGroup;\r\n class InterfaceStructure extends Container {\r\n constructor(data, children = []) {\r\n super(data, children);\r\n }\r\n getAsSections() {\r\n let currentAnonymousSection = null;\r\n let sections = [];\r\n for (const child of this.children) {\r\n if (child instanceof Section) {\r\n sections.push(child);\r\n currentAnonymousSection = null;\r\n }\r\n else {\r\n if (!currentAnonymousSection) {\r\n currentAnonymousSection = new Section({\r\n t: 'section',\r\n title: '',\r\n children: []\r\n });\r\n sections.push(currentAnonymousSection);\r\n }\r\n currentAnonymousSection.children.push(child);\r\n }\r\n }\r\n return sections;\r\n }\r\n }\r\n AmeCustomizable.InterfaceStructure = InterfaceStructure;\r\n class Control extends UiElement {\r\n constructor(data, settings = {}, enabled = null, children = []) {\r\n super(data, children);\r\n this.label = data.label;\r\n this.settings = settings;\r\n this.inputClasses = data.inputClasses || [];\r\n this.inputAttributes = data.inputAttributes || {};\r\n this.enabled = enabled || ko.observable(true);\r\n // noinspection PointlessBooleanExpressionJS -- Might not actually be a boolean if sent from the server.\r\n this.includesOwnLabel = (typeof data.includesOwnLabel !== 'undefined') ? (!!data.includesOwnLabel) : false;\r\n this.labelTargetId = data.labelTargetId || '';\r\n this.primaryInputId = data.primaryInputId || '';\r\n this.settingValidationErrors = ko.pureComputed(() => {\r\n const errors = [];\r\n for (const [settingId, setting] of Object.entries(this.settings)) {\r\n const settingErrors = setting.validationErrors();\r\n if (settingErrors.length > 0) {\r\n for (const error of settingErrors) {\r\n errors.push([settingId, error]);\r\n }\r\n }\r\n }\r\n return errors;\r\n });\r\n }\r\n getComponentParams() {\r\n return Object.assign(Object.assign({}, super.getComponentParams()), { settings: this.settings, enabled: this.enabled, label: this.label, primaryInputId: this.primaryInputId });\r\n }\r\n getAutoGroupTitle() {\r\n if (this.settings['value']) {\r\n const customGroupTitle = this.settings['value'].groupTitle;\r\n if (customGroupTitle) {\r\n return customGroupTitle;\r\n }\r\n }\r\n return this.label;\r\n }\r\n /**\r\n * Create a control group wrapper with this control as its only child.\r\n */\r\n createControlGroup() {\r\n let title = this.getAutoGroupTitle();\r\n //Some controls like the checkbox already show their own label.\r\n //Don't add a group title in that case.\r\n if (this.includesOwnLabel) {\r\n title = '';\r\n }\r\n const data = {\r\n t: 'control-group',\r\n title: title\r\n };\r\n if (this.labelTargetId) {\r\n data.labelFor = this.labelTargetId;\r\n }\r\n return new ControlGroup(data, [this], this.enabled);\r\n }\r\n }\r\n AmeCustomizable.Control = Control;\r\n function unserializeUiElement(data, findSetting, dataCustomizer) {\r\n if (typeof dataCustomizer === 'function') {\r\n dataCustomizer(data);\r\n }\r\n const dataAsRecord = data;\r\n //Unserialize children recursively.\r\n let children = [];\r\n if ((typeof dataAsRecord['children'] !== 'undefined') && Array.isArray(dataAsRecord['children'])) {\r\n for (const childData of dataAsRecord['children']) {\r\n children.push(unserializeUiElement(childData, findSetting, dataCustomizer));\r\n }\r\n }\r\n //Unserialize the \"enabled\" condition.\r\n let enabled = null;\r\n if ((data.t === 'control') || (data.t === 'control-group')) {\r\n if (typeof data.enabled !== 'undefined') {\r\n if (isSettingConditionData(data.enabled)) {\r\n const condition = SettingCondition.fromData(data.enabled, findSetting);\r\n enabled = ko.pureComputed(() => condition.evaluate());\r\n }\r\n else {\r\n enabled = ko.pureComputed(() => !!data.enabled);\r\n }\r\n }\r\n else {\r\n enabled = ko.observable(true);\r\n }\r\n }\r\n switch (data.t) {\r\n case 'section':\r\n return new Section(data, children);\r\n case 'control-group':\r\n return new ControlGroup(data, children, enabled);\r\n case 'structure':\r\n return new InterfaceStructure(data, children);\r\n case 'control':\r\n let settings = {};\r\n if (data.settings) {\r\n for (const childName in data.settings) {\r\n if (data.settings.hasOwnProperty(childName)) {\r\n const settingId = data.settings[childName];\r\n const setting = findSetting(settingId);\r\n if (setting.isDefined()) {\r\n settings[childName] = setting.get();\r\n }\r\n else {\r\n throw new Error('Unknown setting \"' + settingId + '\" referenced by control \"' + data.label + '\".');\r\n }\r\n }\r\n }\r\n }\r\n return new Control(data, settings, enabled, children);\r\n }\r\n }\r\n AmeCustomizable.unserializeUiElement = unserializeUiElement;\r\n class SettingReaderRegistry {\r\n constructor() {\r\n this.notFound = {};\r\n this.valueReaders = [];\r\n }\r\n registerValueReader(getter, idPrefix = null) {\r\n this.valueReaders.push({ getter, idPrefix });\r\n }\r\n /**\r\n * Try to find a setting in a registered setting reader.\r\n */\r\n getValue(settingId) {\r\n for (const { getter, idPrefix } of this.valueReaders) {\r\n if ((idPrefix !== null) && !(settingId.startsWith(idPrefix))) {\r\n continue;\r\n }\r\n const result = getter(settingId, this.notFound);\r\n if (result !== this.notFound) {\r\n return some(result);\r\n }\r\n }\r\n return none;\r\n }\r\n }\r\n AmeCustomizable.SettingReaderRegistry = SettingReaderRegistry;\r\n class PreviewRegistry {\r\n constructor(previewValueGetter) {\r\n this.previewValueGetter = previewValueGetter;\r\n this.settingPreviewUpdaters = {};\r\n this.notFound = {};\r\n this.allPreviewUpdaters = ko.observableArray([]);\r\n }\r\n preview(settingId, value) {\r\n if (!this.settingPreviewUpdaters.hasOwnProperty(settingId)) {\r\n return;\r\n }\r\n const updaters = this.settingPreviewUpdaters[settingId];\r\n for (const updater of updaters) {\r\n updater.preview(settingId, value, this.previewValueGetter);\r\n }\r\n }\r\n clearPreview() {\r\n for (const updater of this.allPreviewUpdaters()) {\r\n updater.clearPreview();\r\n }\r\n }\r\n registerPreviewUpdater(settingIds, updater) {\r\n for (const settingId of settingIds) {\r\n if (!this.settingPreviewUpdaters.hasOwnProperty(settingId)) {\r\n this.settingPreviewUpdaters[settingId] = [];\r\n }\r\n this.settingPreviewUpdaters[settingId].push(updater);\r\n }\r\n if (this.allPreviewUpdaters.indexOf(updater) < 0) {\r\n this.allPreviewUpdaters.push(updater);\r\n }\r\n }\r\n registerPreviewCallback(settingId, callback) {\r\n this.registerPreviewUpdater([settingId], new PreviewCallbackWrapper(callback));\r\n }\r\n canPreview(settingId) {\r\n return (this.settingPreviewUpdaters.hasOwnProperty(settingId)\r\n && (this.settingPreviewUpdaters[settingId].length > 0));\r\n }\r\n }\r\n AmeCustomizable.PreviewRegistry = PreviewRegistry;\r\n class PreviewCallbackWrapper {\r\n constructor(callback) {\r\n this.callback = callback;\r\n }\r\n preview(settingId, value, getSettingValue) {\r\n this.callback(value);\r\n }\r\n clearPreview() {\r\n //Nothing to do in this case.\r\n }\r\n }\r\n class ThrottledPreviewRegistry extends PreviewRegistry {\r\n constructor(previewValueGetter, minPreviewRefreshInterval = 40) {\r\n super(previewValueGetter);\r\n this.minPreviewRefreshInterval = minPreviewRefreshInterval;\r\n this.pendingSettings = {};\r\n this.throttledUpdate = throttleAnimationFrame(this.applyPendingUpdates.bind(this), this.minPreviewRefreshInterval);\r\n }\r\n queuePreview(settingId) {\r\n this.pendingSettings[settingId] = true;\r\n this.throttledUpdate();\r\n }\r\n applyPendingUpdates() {\r\n //Cancel any pending updates in case this method was called directly.\r\n this.throttledUpdate.cancel();\r\n const pendingSettingIds = Object.keys(this.pendingSettings);\r\n if (pendingSettingIds.length === 0) {\r\n return;\r\n }\r\n this.updatePreview(pendingSettingIds);\r\n this.pendingSettings = {};\r\n }\r\n /**\r\n * Update the preview for the specified settings.\r\n *\r\n * This method is called by the throttled update function, but it can also be called\r\n * directly if necessary, e.g. to update the preview for all settings when the user\r\n * opens a settings screen for the first time. Note that calling it will *not* cancel\r\n * pending updates.\r\n *\r\n * @param settingIds\r\n */\r\n updatePreview(settingIds) {\r\n if (settingIds.length < 1) {\r\n return;\r\n }\r\n for (const settingId of settingIds) {\r\n const value = this.previewValueGetter(settingId, this.notFound);\r\n if (value !== this.notFound) {\r\n this.preview(settingId, value);\r\n }\r\n }\r\n }\r\n clearPreview() {\r\n this.throttledUpdate.cancel();\r\n this.pendingSettings = {};\r\n super.clearPreview();\r\n }\r\n }\r\n AmeCustomizable.ThrottledPreviewRegistry = ThrottledPreviewRegistry;\r\n /**\r\n * Creates a throttled function that runs the specified callback at most once\r\n * every `minInterval` milliseconds.\r\n *\r\n * The callback is always invoked using `requestAnimationFrame()`, so it will be delayed\r\n * until the next frame even if the required interval has already passed.\r\n */\r\n function throttleAnimationFrame(callback, minInterval = 0) {\r\n /**\r\n * Expected time between animation frames. Intervals shorter than this will be ineffective.\r\n */\r\n const expectedFrameTime = 1000 / 60;\r\n /**\r\n * The threshold at which we will use `setTimeout()` instead of `requestAnimationFrame()`.\r\n */\r\n const timeoutThreshold = Math.max(1000 / 20, expectedFrameTime * 2 + 1);\r\n const epsilon = 0.001;\r\n let requestAnimationFrameId = null;\r\n let timerId = null;\r\n let lastCallTimestamp = 0;\r\n let nextCallTimestamp = 0;\r\n function animationCallback() {\r\n requestAnimationFrameId = null;\r\n const now = Date.now();\r\n if (nextCallTimestamp <= now) {\r\n lastCallTimestamp = now;\r\n callback();\r\n return;\r\n }\r\n else {\r\n requestAnimationFrameId = window.requestAnimationFrame(animationCallback);\r\n }\r\n }\r\n const invoke = () => {\r\n if ((requestAnimationFrameId !== null) || (timerId !== null)) {\r\n return; //Already scheduled.\r\n }\r\n nextCallTimestamp = lastCallTimestamp + minInterval;\r\n const now = Date.now();\r\n if (nextCallTimestamp <= now) {\r\n nextCallTimestamp = now + expectedFrameTime - epsilon;\r\n }\r\n //Two-stage throttling: If the remaining time is large, use setTimeout().\r\n //If it's small, use requestAnimationFrame() and go frame by frame.\r\n const remainingTime = nextCallTimestamp - now;\r\n if (remainingTime > timeoutThreshold) {\r\n timerId = window.setTimeout(() => {\r\n timerId = null;\r\n requestAnimationFrameId = window.requestAnimationFrame(animationCallback);\r\n }, remainingTime - (expectedFrameTime / 2));\r\n }\r\n else {\r\n //Use requestAnimationFrame.\r\n requestAnimationFrameId = window.requestAnimationFrame(animationCallback);\r\n }\r\n };\r\n invoke.cancel = () => {\r\n if (requestAnimationFrameId !== null) {\r\n window.cancelAnimationFrame(requestAnimationFrameId);\r\n requestAnimationFrameId = null;\r\n }\r\n if (timerId !== null) {\r\n window.clearTimeout(timerId);\r\n timerId = null;\r\n }\r\n };\r\n return invoke;\r\n }\r\n //endregion\r\n})(AmeCustomizable || (AmeCustomizable = {}));\r\nexport var AmeCustomizableViewModel;\r\n(function (AmeCustomizableViewModel) {\r\n var SettingCollection = AmeCustomizable.SettingCollection;\r\n var Setting = AmeCustomizable.Setting;\r\n var ThrottledPreviewRegistry = AmeCustomizable.ThrottledPreviewRegistry;\r\n var SettingReaderRegistry = AmeCustomizable.SettingReaderRegistry;\r\n var lift = AmeMiniFunc.lift;\r\n class SimpleVm extends ThrottledPreviewRegistry {\r\n constructor(extraPreviewCondition = null) {\r\n const getSettingValue = (settingId, defaultResult) => {\r\n const setting = this.getOrCreateKnownSetting(settingId);\r\n if (setting !== null) {\r\n return setting.value();\r\n }\r\n return defaultResult;\r\n };\r\n super(getSettingValue, 40);\r\n this.previewDesired = ko.observable(false);\r\n this.settings = new SettingCollection();\r\n this.settingReaders = new SettingReaderRegistry();\r\n this.isPreviewPossible = ko.pureComputed(() => {\r\n return this.allPreviewUpdaters().length > 0;\r\n });\r\n this.isPreviewEnabled = ko.computed({\r\n read: () => {\r\n if (extraPreviewCondition !== null) {\r\n if (!extraPreviewCondition()) {\r\n return false;\r\n }\r\n }\r\n return this.getPreviewActiveState();\r\n },\r\n write: (newValue) => {\r\n this.previewDesired(newValue);\r\n if (newValue && !this.getPreviewActiveState()) {\r\n //Can't actually enable preview. Reset the checkbox/other input.\r\n this.isPreviewEnabled.notifySubscribers();\r\n }\r\n }\r\n });\r\n this.isPreviewEnabled.subscribe((newValue) => {\r\n if (newValue) {\r\n this.updatePreview(this.settings.getAllSettingIds());\r\n }\r\n else {\r\n this.clearPreview();\r\n }\r\n });\r\n this.settings.addChangeListener((setting) => {\r\n if (!this.isPreviewEnabled()) {\r\n return;\r\n }\r\n this.queuePreview(setting.id);\r\n });\r\n }\r\n getSettingObservable(settingId, unusedDefaultValue = null) {\r\n const result = this.getOrCreateKnownSetting(settingId);\r\n if (result !== null) {\r\n return result.value;\r\n }\r\n throw new Error('Unknown setting ID: ' + settingId);\r\n }\r\n getOrCreateKnownSetting(settingId) {\r\n const result = this.settings.get(settingId);\r\n if (result.isDefined()) {\r\n return result.get();\r\n }\r\n const foundValue = this.settingReaders.getValue(settingId);\r\n if (foundValue.isDefined()) {\r\n const setting = new Setting(settingId, foundValue.get());\r\n this.settings.add(setting);\r\n return setting;\r\n }\r\n return null;\r\n }\r\n registerSettingReader(reader, idPrefix = null) {\r\n this.settingReaders.registerValueReader(reader, idPrefix);\r\n }\r\n getPreviewActiveState() {\r\n return this.previewDesired() && this.isPreviewPossible();\r\n }\r\n getAllSettingValues() {\r\n return this.settings.getAllSettingValues();\r\n }\r\n /**\r\n * Reread all settings from the value readers. This will be used to reload settings\r\n * in case the underlying configuration is reset or a new configuration is loaded.\r\n */\r\n reloadAllSettings() {\r\n for (const settingId of this.settings.getAllSettingIds()) {\r\n lift([this.settings.get(settingId), this.settingReaders.getValue(settingId)], (setting, newValue) => setting.value(newValue));\r\n }\r\n }\r\n }\r\n AmeCustomizableViewModel.SimpleVm = SimpleVm;\r\n // noinspection JSUnusedGlobalSymbols -- Not used right now, but kept for testing and prototyping purposes.\r\n class NullVm {\r\n constructor() {\r\n this.settings = new SettingCollection();\r\n }\r\n getSettingObservable(settingId, defaultValue = null) {\r\n const existingSetting = this.settings.get(settingId);\r\n if (existingSetting.isDefined()) {\r\n return existingSetting.get().value;\r\n }\r\n const setting = new Setting(settingId, defaultValue);\r\n this.settings.add(setting);\r\n return setting.value;\r\n }\r\n getAllSettingValues() {\r\n return this.settings.getAllSettingValues();\r\n }\r\n }\r\n AmeCustomizableViewModel.NullVm = NullVm;\r\n})(AmeCustomizableViewModel || (AmeCustomizableViewModel = {}));\r\n//# sourceMappingURL=customizable.js.map"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/dist/runtime.bundle.js b/dist/runtime.bundle.js new file mode 100644 index 0000000..53bc85f --- /dev/null +++ b/dist/runtime.bundle.js @@ -0,0 +1,151 @@ +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({}); +/************************************************************************/ +/******/ // 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](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/chunk loaded */ +/******/ (() => { +/******/ var deferred = []; +/******/ __webpack_require__.O = (result, chunkIds, fn, priority) => { +/******/ if(chunkIds) { +/******/ priority = priority || 0; +/******/ for(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1]; +/******/ deferred[i] = [chunkIds, fn, priority]; +/******/ return; +/******/ } +/******/ var notFulfilled = Infinity; +/******/ for (var i = 0; i < deferred.length; i++) { +/******/ var [chunkIds, fn, priority] = deferred[i]; +/******/ var fulfilled = true; +/******/ for (var j = 0; j < chunkIds.length; j++) { +/******/ if ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) { +/******/ chunkIds.splice(j--, 1); +/******/ } else { +/******/ fulfilled = false; +/******/ if(priority < notFulfilled) notFulfilled = priority; +/******/ } +/******/ } +/******/ if(fulfilled) { +/******/ deferred.splice(i--, 1) +/******/ var r = fn(); +/******/ if (r !== undefined) result = r; +/******/ } +/******/ } +/******/ return result; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/******/ /* 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 = { +/******/ "runtime": 0 +/******/ }; +/******/ +/******/ // no chunk on demand loading +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded +/******/ +/******/ // no HMR +/******/ +/******/ // no HMR manifest +/******/ +/******/ __webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0); +/******/ +/******/ // 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; +/******/ } +/******/ return __webpack_require__.O(result); +/******/ } +/******/ +/******/ var chunkLoadingGlobal = self["wsAmeWebpackChunk"] = self["wsAmeWebpackChunk"] || []; +/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0)); +/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal)); +/******/ })(); +/******/ +/************************************************************************/ +/******/ +/******/ +/******/ })() +; +//# sourceMappingURL=runtime.bundle.js.map \ No newline at end of file diff --git a/dist/runtime.bundle.js.map b/dist/runtime.bundle.js.map new file mode 100644 index 0000000..a0df0f2 --- /dev/null +++ b/dist/runtime.bundle.js.map @@ -0,0 +1 @@ +{"version":3,"file":"runtime.bundle.js","mappings":";;;;UAAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;UAEA;UACA;;;;;WCzBA;WACA;WACA;WACA;WACA,+BAA+B,wCAAwC;WACvE;WACA;WACA;WACA;WACA,iBAAiB,qBAAqB;WACtC;WACA;WACA,kBAAkB,qBAAqB;WACvC;WACA;WACA,KAAK;WACL;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;;;WC3BA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;WCNA;;WAEA;WACA;WACA;WACA;WACA;WACA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,MAAM,qBAAqB;WAC3B;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA;WACA;WACA","sources":["webpack:///webpack/bootstrap","webpack:///webpack/runtime/chunk loaded","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///webpack/runtime/make namespace object","webpack:///webpack/runtime/jsonp chunk loading","webpack:///webpack/before-startup","webpack:///webpack/startup","webpack:///webpack/after-startup"],"sourcesContent":["// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","var deferred = [];\n__webpack_require__.O = (result, chunkIds, fn, priority) => {\n\tif(chunkIds) {\n\t\tpriority = priority || 0;\n\t\tfor(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1];\n\t\tdeferred[i] = [chunkIds, fn, priority];\n\t\treturn;\n\t}\n\tvar notFulfilled = Infinity;\n\tfor (var i = 0; i < deferred.length; i++) {\n\t\tvar [chunkIds, fn, priority] = deferred[i];\n\t\tvar fulfilled = true;\n\t\tfor (var j = 0; j < chunkIds.length; j++) {\n\t\t\tif ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) {\n\t\t\t\tchunkIds.splice(j--, 1);\n\t\t\t} else {\n\t\t\t\tfulfilled = false;\n\t\t\t\tif(priority < notFulfilled) notFulfilled = priority;\n\t\t\t}\n\t\t}\n\t\tif(fulfilled) {\n\t\t\tdeferred.splice(i--, 1)\n\t\t\tvar r = fn();\n\t\t\tif (r !== undefined) result = r;\n\t\t}\n\t}\n\treturn result;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t\"runtime\": 0\n};\n\n// no chunk on demand loading\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n__webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0);\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = (parentChunkLoadingFunction, data) => {\n\tvar [chunkIds, moreModules, runtime] = data;\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some((id) => (installedChunks[id] !== 0))) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\treturn __webpack_require__.O(result);\n}\n\nvar chunkLoadingGlobal = self[\"wsAmeWebpackChunk\"] = self[\"wsAmeWebpackChunk\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","","",""],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/extras.php b/extras.php index 368fb95..5201cba 100644 --- a/extras.php +++ b/extras.php @@ -10,6 +10,7 @@ */ use YahnisElsts\PluginUpdateChecker\v5\PucFactory; +use YahnisElsts\WpDependencyWrapper\ScriptDependency; define('AME_RC_ONLY_CUSTOM', 1); @@ -68,6 +69,10 @@ function __construct($wp_menu_editor){ add_action('admin_menu_editor-menu_merged', array($this, 'on_menu_merged'), 10, 1); add_action('admin_menu_editor-menu_built', array($this, 'on_menu_built'), 10, 2); + //Trigger a custom JS event when the admin menu element is ready. + //This is used by some Pro features to avoid FOUC. + add_action('in_admin_header', array($this, 'output_menu_ready_trigger')); + //Add some extra shortcodes of our own $shortcode_callback = array($this, 'handle_shortcode'); $info_shortcodes = array( @@ -161,12 +166,6 @@ function __construct($wp_menu_editor){ add_filter('all_plugins', array($this, 'filter_plugin_list')); } - /** - * Menu color scheme generation. - */ - add_filter('ame_pre_set_custom_menu', array($this, 'add_menu_color_css')); - add_action('wp_ajax_ame_output_menu_color_css', array($this,'ajax_output_menu_color_css') ); - //FontAwesome icons. add_filter('custom_admin_menu', array($this, 'add_menu_fa_icon'), 10, 1); add_filter('admin_menu_editor-icon_selector_tabs', array($this, 'add_fa_selector_tab'), 10, 1); @@ -1128,8 +1127,7 @@ function add_extra_buttons(){ ?>
    - - +
    @@ -1202,14 +1200,31 @@ public function register_extra_scripts() { wp_register_auto_versioned_script( 'ame-ko-extensions', plugins_url('extras/ko-extensions.js', $this->wp_menu_editor->plugin_file), - array('knockout', 'jquery', 'jquery-ui-dialog', 'ame-lodash', 'wp-color-picker') + array( + 'ame-knockout', 'jquery', 'jquery-ui-dialog', 'ame-lodash', 'wp-color-picker', + 'ame-mini-functional-lib' + ) + ); + wp_register_auto_versioned_script( + 'ame-pro-common-lib', + plugins_url('extras/pro-common-lib.js', $this->wp_menu_editor->plugin_file), + array('ame-knockout') ); wp_register_auto_versioned_script( 'ame-menu-heading-settings', plugins_url('extras/menu-headings/menu-headings.js', $this->wp_menu_editor->plugin_file), - array('jquery', 'knockout', 'jquery-ui-dialog', 'wp-color-picker', 'ame-lodash', 'ame-ko-extensions',), + array( + 'jquery', 'ame-knockout', 'jquery-ui-dialog', 'wp-color-picker', 'ame-lodash', + 'ame-ko-extensions', 'ame-mini-functional-lib' + ), true ); + + ScriptDependency::create( + plugins_url('extras/jszip/jszip.min.js', $this->wp_menu_editor->plugin_file), + 'ame-jszip', + __DIR__ . '/extras/jszip/jszip.min.js' + )->register(); } /** @@ -1635,6 +1650,17 @@ function grant_virtual_caps_to_role($capabilities, /** @noinspection PhpUnusedPa $virtual_caps = $wp_menu_editor->get_virtual_caps($this->wp_menu_editor->virtual_cap_mode); $grant_key = 'role:' . $role_id; if ( isset($virtual_caps[$grant_key]) ) { + //Prevent a crash if $capabilities is not an array. Technically, WordPress docs + //say it's always an array, but I've received a report where it was NULL for + //a custom role. Let's be safe and check. + if ( !is_array($capabilities) ) { + if ( empty($capabilities) ) { + $capabilities = array(); + } else { + $capabilities = (array)$capabilities; + } + } + $capabilities = array_merge($capabilities, $virtual_caps[$grant_key]); } @@ -2027,8 +2053,6 @@ public function on_menu_built($customMenu = array(), $menuEditor = null) { } else { add_action('admin_enqueue_scripts', array($this, 'enqueue_third_level_script')); } - - add_action('in_admin_header', array($this, 'output_menu_ready_trigger')); } } @@ -2051,184 +2075,6 @@ public function output_menu_ready_trigger() { getCss( - '', - $custom_menu['color_presets']['[global]'], - array(), - dirname(__FILE__) . '/extras/global-menu-color-template.txt' - ); - $css[] = $base_css; - $global_icon_colors = $generator->getIconColorScheme(); - } - - foreach($custom_menu['tree'] as &$item) { - if ( !isset($item['colors']) || empty($item['colors']) ) { - continue; - } - $colorized_menu_count++; - - //Each item needs to have a unique ID so we can target it in CSS. Using a class would be cleaner, - //but the selectors wouldn't have enough specificity to override WP defaults. - $id = ameMenuItem::get($item, 'hookname'); - if ( empty($id) || isset($used_ids[$id]) ) { - $id = (empty($id) ? 'ame-colorized-item' : $id) . '-'; - $id .= $colorized_menu_count . '-t' . time(); - $item['hookname'] = $id; - } - $used_ids[$id] = true; - - $sub_type = ameMenuItem::get($item, 'sub_type'); - if ( $sub_type === 'heading' ) { - $extra_selectors = array('.ame-menu-heading-item'); - } else { - $extra_selectors = array(); - } - - $item_css = $generator->getCss($id, $item['colors'], $extra_selectors); - if ( !empty($item_css) ) { - $css[] = sprintf( - '/* %1$s (%2$s) */', - str_replace('*/', ' ', ameMenuItem::get($item, 'menu_title', 'Untitled menu')), - str_replace('*/', ' ', ameMenuItem::get($item, 'file', '(no URL)')) - ); - $css[] = $item_css; - } - } - - if ( !empty($css) ) { - $css = implode("\n", $css); - $custom_menu['color_css'] = $css; - $custom_menu['color_css_modified'] = time(); - $custom_menu['icon_color_overrides'] = $global_icon_colors; - } else { - $custom_menu['color_css'] = ''; - $custom_menu['color_css_modified'] = 0; - $custom_menu['icon_color_overrides'] = null; - } - - return $custom_menu; - } - - /** - * Enqueue the user-defined menu color scheme, if any. - */ - public function enqueue_menu_color_style() { - try { - $custom_menu = $this->wp_menu_editor->load_custom_menu(); - } catch (InvalidMenuException $e) { - //This exception is best handled elsewhere. - return; - } - if ( empty($custom_menu) || empty($custom_menu['color_css']) ) { - return; - } - - wp_enqueue_style( - 'ame-custom-menu-colors', - add_query_arg( - 'ame_config_id', - $this->wp_menu_editor->get_loaded_menu_config_id(), - admin_url('admin-ajax.php?action=ame_output_menu_color_css') - ), - array(), - $custom_menu['color_css_modified'] - ); - - if ( isset($custom_menu['icon_color_overrides']) ) { - add_action('admin_head', array($this, 'override_menu_icon_color_scheme'), 9); - } - } - - /** - * Output menu color CSS for the current custom menu. - */ - public function ajax_output_menu_color_css() { - $config_id = null; - if ( isset($_GET['ame_config_id']) && !empty($_GET['ame_config_id']) ) { - $config_id = (string) ($_GET['ame_config_id']); - } - - try { - $custom_menu = $this->wp_menu_editor->load_custom_menu($config_id); - } catch (InvalidMenuException $e) { - return; - } - if ( empty($custom_menu) || empty($custom_menu['color_css']) ) { - return; - } - - header('Content-Type: text/css'); - header('X-Content-Type-Options: nosniff'); //No really IE, it's CSS. Honest. - - //Enable browser caching. - header('Cache-Control: public'); - header('Expires: Thu, 31 Dec ' . date('Y', strtotime('+1 year')) . ' 23:59:59 GMT'); - header('Pragma: cache'); - - echo $custom_menu['color_css']; - exit(); - } - - /** - * Replace the icon colors in the current admin color scheme with the custom colors - * set by the user. This is necessary to make SVG icons display in the right color. - */ - public function override_menu_icon_color_scheme() { - global $_wp_admin_css_colors; - - $custom_menu = $this->wp_menu_editor->load_custom_menu(); - if (!isset($custom_menu['icon_color_overrides'])) { - return; - } - - $color_scheme = get_user_option('admin_color'); - if ( empty( $_wp_admin_css_colors[ $color_scheme ] ) ) { - $color_scheme = 'fresh'; - } - - $custom_colors = array_merge( - array( - 'base' => '#a0a5aa', - 'focus' => '#00a0d2', - 'current' => '#fff', - ), - $custom_menu['icon_color_overrides'] - ); - - if ( isset($_wp_admin_css_colors[$color_scheme]) ) { - $_wp_admin_css_colors[$color_scheme]->icon_colors = $custom_colors; - } - } - /** * Enqueue various dependencies on all admin pages. * @@ -2236,7 +2082,6 @@ public function override_menu_icon_color_scheme() { * (and it might hurt performance), so I've combined some of them into this method. */ public function enqueue_dashboard_deps() { - $this->enqueue_menu_color_style(); $this->enqueue_fontawesome(); wp_enqueue_auto_versioned_style( @@ -2522,7 +2367,7 @@ public function filter_available_modules($modules) { 'requiredPhpVersion' => '5.3.6', 'title' => 'Role Editor', ); - + $modules['tweaks'] = array( 'path' => AME_ROOT_DIR . '/extras/modules/tweaks/tweaks.php', 'className' => 'ameTweakManager', @@ -2531,12 +2376,60 @@ public function filter_available_modules($modules) { ); $modules['separator-styles'] = array( - 'path' => AME_ROOT_DIR . '/extras/modules/separator-styles/ameMenuSeparatorStyler.php', - 'className' => 'ameMenuSeparatorStyler', + 'path' => AME_ROOT_DIR . '/extras/modules/separator-styles/MenuSeparatorStyler.php', + 'className' => 'YahnisElsts\\AdminMenuEditor\\MenuSeparatorStyles\\MenuSeparatorStyler', 'title' => 'Custom menu separator styles', 'requiredPhpVersion' => '5.6', ); + $modules['menu-styler'] = array( + 'path' => AME_ROOT_DIR . '/extras/modules/menu-styler/menu-styler.php', + 'className' => 'YahnisElsts\\AdminMenuEditor\\MenuStyler\\MenuStyler', + 'title' => 'Admin Menu Styles', + 'requiredPhpVersion' => '5.6', + 'isAlwaysActive' => true, + ); + + if (defined('AME_CUSTOMIZABLE_DEV') && AME_CUSTOMIZABLE_DEV) { + $modules['admin-customizer'] = [ + 'path' => AME_ROOT_DIR . '/extras/modules/admin-customizer/admin-customizer.php', + 'className' => 'YahnisElsts\\AdminMenuEditor\\AdminCustomizer\\AmeAdminCustomizer', + 'title' => 'Admin Customizer', + 'requiredPhpVersion' => '5.6', + ]; + + $modules['admin-theme-builder'] = [ + 'path' => AME_ROOT_DIR . '/extras/modules/dashboard-styler/dashboard-styler.php', + 'className' => 'YahnisElsts\\AdminMenuEditor\\DashboardStyler\\DashboardStyler', + 'title' => 'Dashboard Styler', + 'requiredPhpVersion' => '5.6', + ]; + } + + $modules['menu-colors'] = array( + 'path' => AME_ROOT_DIR . '/extras/modules/admin-menu-colors/admin-menu-colors.php', + 'className' => '\\YahnisElsts\\AdminMenuEditor\\AdminMenuColors\\MenuColorsModule', + 'title' => 'Admin Menu Colors', + 'requiredPhpVersion' => '5.6', + 'isAlwaysActive' => true, + ); + + if (defined('AME_CUSTOMIZABLE_DEV') && AME_CUSTOMIZABLE_DEV) { + $modules['sample-module'] = [ + 'path' => AME_ROOT_DIR . '/extras/customizables/SampleModule.php', + 'className' => '\\YahnisElsts\\AdminMenuEditor\\Customizable\\Design\\SampleModule', + 'title' => 'Sample Module', + 'requiredPhpVersion' => '5.6', + ]; + + $modules['ko-customizable-dev'] = [ + 'path' => AME_ROOT_DIR . '/extras/modules/ko-customizable-dev/ko-customizable-dev.php', + 'className' => '\\YahnisElsts\\AdminMenuEditor\\KoCustomizableDev\\AmeKoCustomizableDevModule', + 'title' => 'Knockout Prototyping Module', + 'requiredPhpVersion' => '5.6', + ]; + } + return $modules; } } diff --git a/extras/.htaccess b/extras/.htaccess index cb825d2..f9f2360 100644 --- a/extras/.htaccess +++ b/extras/.htaccess @@ -1,4 +1,4 @@ - + # Apache 2.4 Require all granted diff --git a/extras/dynamic-stylesheets/Cache/DummyCache.php b/extras/dynamic-stylesheets/Cache/DummyCache.php new file mode 100644 index 0000000..529ac23 --- /dev/null +++ b/extras/dynamic-stylesheets/Cache/DummyCache.php @@ -0,0 +1,17 @@ +parseRetrievedValue($data, $default); + } + + /** + * @inheritdoc + */ + public function set($key, $value, $ttl = null) { + return set_transient($key, $value, $this->convertTtlForWp($ttl)); + } + + /** + * @inheritdoc + */ + public function delete($key) { + return delete_transient($key); + } +} \ No newline at end of file diff --git a/extras/dynamic-stylesheets/Cache/NetworkTransientCache.php b/extras/dynamic-stylesheets/Cache/NetworkTransientCache.php new file mode 100644 index 0000000..8c52bc1 --- /dev/null +++ b/extras/dynamic-stylesheets/Cache/NetworkTransientCache.php @@ -0,0 +1,27 @@ +parseRetrievedValue($data, $default); + } + + /** + * @inheritdoc + */ + public function set($key, $value, $ttl = null) { + return set_site_transient($key, $value, $this->convertTtlForWp($ttl)); + } + + /** + * @inheritdoc + */ + public function delete($key) { + return delete_site_transient($key); + } +} \ No newline at end of file diff --git a/extras/dynamic-stylesheets/Cache/StyleCacheInterface.php b/extras/dynamic-stylesheets/Cache/StyleCacheInterface.php new file mode 100644 index 0000000..877e420 --- /dev/null +++ b/extras/dynamic-stylesheets/Cache/StyleCacheInterface.php @@ -0,0 +1,40 @@ +generateCombinedCss(); + }, + null, + $cache, + $cacheKeySuffix, + $additionalQueryParameters + ); + + if ( $cache !== null ) { + $this->cache = $cache; + } + } + + public function addStylesheet(Stylesheet $stylesheet) { + $this->stylesheets[] = $stylesheet; + } + + protected function generateCombinedCss() { + $parts = []; + foreach ($this->stylesheets as $stylesheet) { + $content = $stylesheet->generateCss(); + if ( !is_string($content) ) { + continue; + } + $content = trim($content); + if ( !empty($content) ) { + $parts[] = $content; + } + } + return implode("\n", $parts); + } + + protected function getLastModifiedTimestamp() { + //Use the most recent timestamp of all stylesheets. + $lastModifiedTimestamp = 0; + foreach ($this->stylesheets as $stylesheet) { + $timestamp = $stylesheet->getLastModifiedTimestamp(); + if ( ($timestamp !== null) && ($timestamp > $lastModifiedTimestamp) ) { + $lastModifiedTimestamp = $timestamp; + } + } + return $lastModifiedTimestamp; + } + + protected function getCacheTtl() { + //Use the lowest non-zero TTL. + $ttl = null; + foreach ($this->stylesheets as $stylesheet) { + $stylesheetTtl = $stylesheet->getCacheTtl(); + if ( ($stylesheetTtl === null) || ($stylesheetTtl <= 0) ) { + continue; + } + if ( ($ttl === null) || ($stylesheetTtl < $ttl) ) { + $ttl = $stylesheetTtl; + } + } + + if ( ($ttl !== null) && ($ttl > 0) ) { + return $ttl; + } else { + return self::CACHE_TTL_FALLBACK; + } + } +} \ No newline at end of file diff --git a/extras/dynamic-stylesheets/MenuScopedStylesheetHelper.php b/extras/dynamic-stylesheets/MenuScopedStylesheetHelper.php new file mode 100644 index 0000000..bc33a94 --- /dev/null +++ b/extras/dynamic-stylesheets/MenuScopedStylesheetHelper.php @@ -0,0 +1,230 @@ + true, + 'global' => true, + 'network-admin' => true, + ]; + + /** + * Not a secret, just a random string to make the hash more unique. + * Common words like "site" and "global" might be hashed elsewhere, + * making it too easy to guess the hash of a given config ID. + */ + const CONFIG_ID_HASH_INPUT_SUFFIX = '|ame-cid-hash_6578711'; + + /** + * @var \WPMenuEditor + */ + private $menuEditor; + private $pendingBundles = []; + private $pendingStylesheets = []; + + private $bundles = []; + + private $initHookDone = false; + private $enqueueHookDone = false; + private $knownConfigId = null; + + public function __construct(\WPMenuEditor $menuEditor) { + $this->menuEditor = $menuEditor; + + add_action('init', [$this, 'onInit'], 20); + add_action('admin_enqueue_scripts', [$this, 'onEnqueueAdminStylesheets'], 9); + } + + public function onInit() { + $this->initHookDone = true; + + if ( $this->doingAjax() ) { + $configId = $this->getConfigIdFromAjaxRequest(); + if ( empty($configId) ) { + return; + } + $this->knownConfigId = $configId; + $this->createPendingInstances($configId); + } + } + + private function doingAjax() { + return (defined('DOING_AJAX') && constant('DOING_AJAX')); + } + + public function onEnqueueAdminStylesheets() { + $this->enqueueHookDone = true; + + $configId = $this->menuEditor->get_loaded_menu_config_id(); + if ( !is_string($configId) || !array_key_exists($configId, self::SUPPORTED_CONFIG_IDS) ) { + return; + } + + $this->knownConfigId = $configId; + $this->createPendingInstances($configId); + } + + protected function createPendingInstances($configId) { + if ( empty($this->pendingBundles) && empty($this->pendingStylesheets) ) { + return; + } + + switch ($configId) { + case 'site': + $cache = self::getLocalCache(); + break; + case 'global': + if ( is_multisite() ) { + $cache = self::getNetworkCache(); + } else { + //Use regular (non-network) transients for single-site installations. + //Stylesheets take advantage of transient autoloading to load cache + //metadata without an extra DB query, and that only works with normal + //transients, not network/site transients. + $cache = self::getLocalCache(); + } + break; + case 'network-admin': + $cache = self::getNetworkCache(); + break; + default: + return; + } + + foreach ($this->pendingBundles as $bundleHandle) { + $this->bundles[$bundleHandle] = $bundle = new DynamicStylesheetBundle( + $bundleHandle, + $cache, + $configId, + $this->generateQueryParamsWithConfigId($configId) + ); + $bundle->addHooks(); + } + $this->pendingBundles = []; + + foreach ($this->pendingStylesheets as $handle => $data) { + $providerCallback = $data['providerCallback']; + $parentBundle = $data['parentBundle']; + + list($lastModifiedCallback, $cssGenerator) = call_user_func( + $providerCallback, + $configId + ); + if ( empty($cssGenerator) ) { + continue; + } + + $stylesheet = new Stylesheet( + $handle, + $cssGenerator, + $lastModifiedCallback ?: '__return_zero', + $cache, + $configId, + $this->generateQueryParamsWithConfigId($configId) + ); + + if ( $parentBundle && isset($this->bundles[$parentBundle]) ) { + $this->bundles[$parentBundle]->addStylesheet($stylesheet); + + /* + Even when the stylesheet is added to a bundle, it still needs + to be able to output itself directly because whatever process + decides whether to use a bundle for a particular admin page may + not produce the same result during the AJAX request that actually + loads the stylesheet (it might not even run at all). + */ + $stylesheet->addOutputHook(); + } else { + $stylesheet->addHooks(); + } + } + $this->pendingStylesheets = []; + } + + private function maybeCreateLateInstances() { + $isLate = $this->doingAjax() ? $this->initHookDone : $this->enqueueHookDone; + if ( !$isLate ) { + return; + } + + if ( $this->knownConfigId ) { + $this->createPendingInstances($this->knownConfigId); + } + } + + private static function getLocalCache() { + static $cache = null; + if ( $cache === null ) { + $cache = new Cache\LocalTransientCache(); + } + return $cache; + } + + private static function getNetworkCache() { + static $cache = null; + if ( $cache === null ) { + $cache = new Cache\NetworkTransientCache(); + } + return $cache; + } + + public function addBundle($handle) { + $this->pendingBundles[$handle] = $handle; + $this->maybeCreateLateInstances(); + } + + public function addStylesheet($handle, $providerCallback, $parentBundle = null) { + $this->pendingStylesheets[$handle] = [ + 'providerCallback' => $providerCallback, + 'parentBundle' => $parentBundle, + ]; + $this->maybeCreateLateInstances(); + } + + /** + * @param string $menuConfigId + * @return array + */ + private function generateQueryParamsWithConfigId($menuConfigId) { + return [ + 'ame_config_id' => $menuConfigId, + 'ame_cid_hash' => wp_hash($menuConfigId . self::CONFIG_ID_HASH_INPUT_SUFFIX), + ]; + } + + /** + * @return string|null + */ + public function getConfigIdFromAjaxRequest() { + //External callers might not check if this is an AJAX request. + if ( !$this->doingAjax() ) { + return null; + } + + $queryParams = $this->menuEditor->get_query_params(); + if ( !isset($queryParams['ame_config_id']) || !isset($queryParams['ame_cid_hash']) ) { + return null; + } + + $configId = (string)$queryParams['ame_config_id']; + if ( !array_key_exists($configId, self::SUPPORTED_CONFIG_IDS) ) { + return null; + } + + $expectedHash = wp_hash($configId . self::CONFIG_ID_HASH_INPUT_SUFFIX); + if ( $expectedHash !== $queryParams['ame_cid_hash'] ) { + return null; + } + + return $configId; + } + + public static function getInstance(\WPMenuEditor $menuEditor) { + static $instance = null; + if ( $instance === null ) { + $instance = new self($menuEditor); + } + return $instance; + } +} \ No newline at end of file diff --git a/extras/dynamic-stylesheets/Stylesheet.php b/extras/dynamic-stylesheets/Stylesheet.php new file mode 100644 index 0000000..5b450f1 --- /dev/null +++ b/extras/dynamic-stylesheets/Stylesheet.php @@ -0,0 +1,439 @@ + $additionalQueryParameters + */ + public function __construct( + $handle, + $cssGenerator, + $lastModifiedCallback = null, + $cache = null, + $cacheKeySuffix = '', + $additionalQueryParameters = [] + ) { + $this->handle = $handle; + $this->ajaxAction = self::AJAX_ACTION_PREFIX . $handle; + $this->cssGenerator = $cssGenerator; + $this->lastModifiedCallback = $lastModifiedCallback; + $this->additionalQueryParameters = $additionalQueryParameters; + + if ( $cache ) { + $this->cache = $cache; + } else { + $this->cache = new DummyCache(); + } + $this->cacheKeySuffix = $cacheKeySuffix; + } + + /** + * Register the hooks that wil enqueue and output the stylesheet. + * + * @return void + */ + public function addHooks() { + $this->addAdminEnqueueHook(); + $this->addOutputHook(); + } + + public function addAdminEnqueueHook() { + add_action('admin_enqueue_scripts', [$this, 'enqueueStyle']); + } + + public function addOutputHook() { + add_action('wp_ajax_' . $this->ajaxAction, [$this, 'ajaxOutputCss']); + } + + /** + * Get the CSS content of the stylesheet. This method bypasses the cache. + * + * @return string + */ + public function generateCss() { + $css = call_user_func($this->cssGenerator); + return (string)$css; + } + + public function enqueueStyle() { + $lastModified = $this->getLastModifiedTimestamp(); + if ( $this->isContentKnownEmpty($lastModified) ) { + return; + } + + wp_enqueue_style( + $this->handle, + $this->getUrl(), + [], + $this->getVersion($lastModified) + ); + } + + /** + * Handle the AJAX request that outputs the stylesheet. + * + * Note: This will terminate the script. + * + * @access private This is only public because it's a hook callback. + * @return void + * @internal + */ + public function ajaxOutputCss() { + if ( $this->requiresLogin() && !is_user_logged_in() ) { + echo '/* You must be logged in to view this stylesheet. */'; + exit(); + } + + $this->outputHttpResponse(); + exit(); + } + + protected function outputHttpResponse() { + $lastModified = $this->getLastModifiedTimestamp(); + $omitResponseBody = ameUtils::sendCachingHeaders($lastModified); + if ( $omitResponseBody ) { + return; + } + + $cache = $this->getCache(); + $cacheKey = $this->getCacheKey(); + $ttl = $this->getCacheTtl(); + + $hasValidCachedContent = false; + $isCacheGettingStale = false; + $wasContentRegenerated = false; + + $data = $cache->get($cacheKey); + if ( is_array($data) ) { + $hasValidCachedContent = isset($data['content']) + && isset($data['lastModified']) + && ($data['lastModified'] === $lastModified); + + if ( $ttl > 0 ) { + $refreshThreshold = ceil(min($ttl / 2, self::EARLY_CACHE_UPDATE_THRESHOLD)); + $isCacheGettingStale = isset($data['expiration']) + && !empty($data['expiration']) + && ($data['expiration'] < (time() + $refreshThreshold)); + } + } + + if ( $hasValidCachedContent ) { + $content = $data['content']; + + $content = sprintf( + '/* Cache hit. Last modified on %s */', + isset($data['lastModified']) ? gmdate('Y-m-d H:i:s', $data['lastModified']) : 'unknown' + ) . "\n" . $content; + } else { + $content = $this->generateCss(); + $wasContentRegenerated = true; + } + + header('Content-Type: text/css'); + header('X-Content-Type-Options: nosniff'); + header('Content-Length: ' . strlen($content)); + + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS does not need to be HTML-escaped. + echo $content; + + //Maybe update the cache. + if ( !$hasValidCachedContent || ($isCacheGettingStale && $this->shouldUpdateCacheEarly()) ) { + //Let the browser close the connection if it wants to. + ignore_user_abort(true); + //Flush the output buffer, if any. + $bufferLength = ob_get_length(); + if ( ($bufferLength !== false) && ($bufferLength > 0) ) { + @ob_end_flush(); + } + + //Attempt to update the cache. + $lock = ameFileLock::create(__FILE__); + if ( $lock->acquire() ) { + if ( !$wasContentRegenerated ) { + $content = $this->generateCss(); + } + + $cache->set( + $cacheKey, + [ + 'content' => $content, + 'lastModified' => $lastModified, + 'expiration' => empty($ttl) ? 0 : (time() + $ttl), + ], + $ttl + ); + + if ( $this->isMetadataCachingEnabled() ) { + $trimmedContent = trim($content); + $cache->set( + $this->getMetaCacheKey(), + [ + 'lastModified' => $lastModified, + 'isContentEmpty' => empty($trimmedContent), + ], + //Normal transients with 0 TTL never expire and are autoloaded. This allows + //us to check if the stylesheet is empty without having to regenerate it, + //and without loading the (potentially large) stylesheet content from the DB. + //Note: Site transients are not autoloaded. + 0 + ); + } + } + $lock->release(); + } + } + + /** + * Is the content of this stylesheet known to be empty? + * + * This will only be true if the stylesheet data has already been generated + * and cached, and the generated content was empty. + * + * @param int $lastModified + * @return bool + */ + protected function isContentKnownEmpty($lastModified) { + if ( !$this->isMetadataCachingEnabled() ) { + return false; + } + + $cache = $this->getCache(); + $metadata = $cache->get($this->getMetaCacheKey()); + if ( empty($metadata) ) { + return false; + } + + if ( + isset($metadata['isContentEmpty']) + && isset($metadata['lastModified']) + && ($metadata['lastModified'] === $lastModified) + ) { + return $metadata['isContentEmpty']; + } + return false; + } + + /** + * Get the last time the stylesheet was modified. + * + * This timestamp is used for cache invalidation, so the method should always + * return the effective value, not cached data. + * + * @return int Unix timestamp. Can be 0 if the last modified time is unknown. + */ + protected function getLastModifiedTimestamp() { + //In the preview frame, the "last modified" timestamp does not get updated + //when the user changes individual style settings. Instead, let's use + //the changeset modification time. + $changeset = $this->getActiveAcChangeset(); + if ( !empty($changeset) ) { + $changesetModified = $changeset->getLastModified(); + if ( !empty($changesetModified) ) { + return $changesetModified; + } + } + + if ( $this->lastModifiedCallback === null ) { + return 0; + } + + $timestamp = call_user_func($this->lastModifiedCallback); + if ( !is_int($timestamp) ) { + return 0; + } + return $timestamp; + } + + /** + * Get the stylesheet URL. + * + * @return string + */ + protected function getUrl() { + $url = add_query_arg($this->generateQueryParameters(), admin_url('admin-ajax.php')); + + //Add AC preview parameters to the URL. It's not automated in AC itself + //because AC doesn't necessarily know which admin URLs are safe to change. + return apply_filters('admin_menu_editor-ac_add_preview_params', $url); + } + + /** + * Generate the query parameters for the stylesheet URL. + * + * @return array + */ + protected function generateQueryParameters() { + return array_merge( + $this->additionalQueryParameters, + ['action' => $this->ajaxAction] + ); + } + + /** + * Get the version string for the stylesheet. + * + * @param int $lastModified + * @return string + */ + protected function getVersion($lastModified) { + $version = (string)$lastModified; + $versionPrefix = $this->getVersionPrefix(); + if ( !empty($versionPrefix) ) { + $version = $versionPrefix . $version; + } + return $version; + } + + /** + * @return string + */ + protected function getVersionPrefix() { + if ( $this->isPreviewMode() ) { + $prefix = 'preview'; + $changeset = $this->getActiveAcChangeset(); + if ( !empty($changeset) ) { + $name = $changeset->getName(); + if ( !empty($name) ) { + //The version prefix is also used as part of the cache key, so it should + //be short. The changeset name is usually long string, so we'll hash it + //and use the first N characters. + $prefix .= '-' . substr(sha1($name), 0, 8); + } + } + return $prefix; + } else { + return ''; + } + } + + /** + * Does the user need to be logged in to view this stylesheet? + * + * @return bool + */ + protected function requiresLogin() { + //Currently, only admin stylesheets are supported, so we can assume that + //the user needs to be logged in. + return true; + } + + /** + * @return \YahnisElsts\AdminMenuEditor\DynamicStylesheets\Cache\StyleCacheInterface + */ + protected function getCache() { + return $this->cache; + } + + protected function getCacheKey($suffix = 'css') { + $key = self::COMMON_CACHE_PREFIX . $this->handle; + + if ( !empty($this->cacheKeySuffix) ) { + $key .= '.' . $this->cacheKeySuffix; + } + + $versionPrefix = $this->getVersionPrefix(); + if ( !empty($versionPrefix) ) { + $key .= '.' . $versionPrefix; + } + + $key .= '.' . $suffix; + return $key; + } + + protected function getMetaCacheKey() { + return $this->getCacheKey('meta'); + } + + protected function getCacheTtl() { + if ( $this->isPreviewMode() ) { + return 2 * DAY_IN_SECONDS; + } else { + return 30 * DAY_IN_SECONDS; + } + } + + protected function isMetadataCachingEnabled() { + return !$this->isPreviewMode(); + } + + protected function shouldUpdateCacheEarly() { + return !$this->isPreviewMode(); + } + + protected function isPreviewMode() { + //Once enabled, preview mode cannot be disabled during the same request. + //This means we can cache the "true" result and possibly save some performance. + static $isPreviewMode = false; + if ( $isPreviewMode ) { + return true; + } + + $currentState = apply_filters('admin_menu_editor-is_preview_frame', false); + if ( $currentState ) { + $isPreviewMode = true; + } + return $currentState; + } + + /** + * @return \YahnisElsts\AdminMenuEditor\AdminCustomizer\AcChangeset|null + */ + protected function getActiveAcChangeset() { + $changeset = apply_filters('admin_menu_editor-ac_preview_frame_changeset', null); + if ( !empty($changeset) && is_object($changeset) ) { + return $changeset; + } else { + return null; + } + } +} \ No newline at end of file diff --git a/extras/jszip/jszip.d.ts b/extras/jszip/jszip.d.ts new file mode 100644 index 0000000..0fe8c6d --- /dev/null +++ b/extras/jszip/jszip.d.ts @@ -0,0 +1,330 @@ +// Type definitions for JSZip 3.1 +// Project: http://stuk.github.com/jszip/, https://github.com/stuk/jszip +// Definitions by: mzeiher , forabi +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.3 + +/// + +interface JSZipSupport { + arraybuffer: boolean; + uint8array: boolean; + blob: boolean; + nodebuffer: boolean; +} + +type Compression = 'STORE' | 'DEFLATE'; + +/** + * Depends on the compression type. With `STORE` (no compression), these options are ignored. With + * `DEFLATE`, you can give the compression level between 1 (best speed) and 9 (best compression). + */ +interface CompressionOptions { + level: number; +} + +interface InputByType { + base64: string; + string: string; + text: string; + binarystring: string; + array: number[]; + uint8array: Uint8Array; + arraybuffer: ArrayBuffer; + blob: Blob; + stream: NodeJS.ReadableStream; +} + +interface OutputByType { + base64: string; + string: string; + text: string; + binarystring: string; + array: number[]; + uint8array: Uint8Array; + arraybuffer: ArrayBuffer; + blob: Blob; + nodebuffer: Buffer; +} + +// This private `_data` property on a JSZipObject uses this interface. +// If/when it is made public this should be uncommented. +// interface CompressedObject { +// compressedSize: number; +// uncompressedSize: number; +// crc32: number; +// compression: object; +// compressedContent: string|ArrayBuffer|Uint8Array|Buffer; +// } + +type InputFileFormat = InputByType[keyof InputByType] | Promise; + +declare namespace JSZip { + type InputType = keyof InputByType; + + type OutputType = keyof OutputByType; + + interface JSZipMetadata { + percent: number; + currentFile: string | null; + } + + type OnUpdateCallback = (metadata: JSZipMetadata) => void; + + interface JSZipObject { + name: string; + /** + * Present for files loadded with `loadAsync`. May contain ".." path components that could + * result in a zip-slip attack. See https://snyk.io/research/zip-slip-vulnerability + */ + unsafeOriginalName?: string; + dir: boolean; + date: Date; + comment: string; + /** The UNIX permissions of the file, if any. */ + unixPermissions: number | string | null; + /** The UNIX permissions of the file, if any. */ + dosPermissions: number | null; + options: JSZipObjectOptions; + + /** + * Prepare the content in the asked type. + * @param type the type of the result. + * @param onUpdate a function to call on each internal update. + * @return Promise the promise of the result. + */ + async(type: T, onUpdate?: OnUpdateCallback): Promise; + nodeStream(type?: 'nodebuffer', onUpdate?: OnUpdateCallback): NodeJS.ReadableStream; + } + + interface JSZipFileOptions { + /** Set to `true` if the data is `base64` encoded. For example image data from a `` element. Plain text and HTML do not need this option. */ + base64?: boolean; + /** + * Set to `true` if the data should be treated as raw content, `false` if this is a text. If `base64` is used, + * this defaults to `true`, if the data is not a `string`, this will be set to `true`. + */ + binary?: boolean; + /** + * The last modification date, defaults to the current date. + */ + date?: Date; + /** + * Sets per file compression. The `compressionOptions` parameter depends on the compression type. + */ + compression?: Compression; + /** + * Sets per file compression level for `DEFLATE` compression. + */ + compressionOptions?: null | CompressionOptions; + comment?: string; + /** Set to `true` if (and only if) the input is a "binary string" and has already been prepared with a `0xFF` mask. */ + optimizedBinaryString?: boolean; + /** Set to `true` if folders in the file path should be automatically created, otherwise there will only be virtual folders that represent the path to the file. */ + createFolders?: boolean; + /** Set to `true` if this is a directory and content should be ignored. */ + dir?: boolean; + + /** 6 bits number. The DOS permissions of the file, if any. */ + dosPermissions?: number | null; + /** + * 16 bits number. The UNIX permissions of the file, if any. + * Also accepts a `string` representing the octal value: `"644"`, `"755"`, etc. + */ + unixPermissions?: number | string | null; + } + + interface JSZipObjectOptions { + compression: Compression; + } + + interface JSZipGeneratorOptions { + /** + * Sets compression option for all entries that have not specified their own `compression` option + */ + compression?: Compression; + /** + * Sets compression level for `DEFLATE` compression. + */ + compressionOptions?: null | CompressionOptions; + type?: T; + comment?: string; + /** + * mime-type for the generated file. + * Useful when you need to generate a file with a different extension, ie: “.ods”. + * @default 'application/zip' + */ + mimeType?: string; + encodeFileName?(filename: string): string; + /** Stream the files and create file descriptors */ + streamFiles?: boolean; + /** DOS (default) or UNIX */ + platform?: 'DOS' | 'UNIX'; + } + + interface JSZipLoadOptions { + base64?: boolean; + checkCRC32?: boolean; + optimizedBinaryString?: boolean; + createFolders?: boolean; + decodeFileName?: (bytes: string[] | Uint8Array | Buffer) => string; + } + + type DataEventCallback = (dataChunk: T, metadata: JSZipMetadata) => void + type EndEventCallback = () => void + type ErrorEventCallback = (error: Error) => void + + interface JSZipStreamHelper { + /** + * Register a listener on an event + */ + on(event: 'data', callback: DataEventCallback): this; + on(event: 'end', callback: EndEventCallback): this; + on(event: 'error', callback: ErrorEventCallback): this; + + /** + * Read the whole stream and call a callback with the complete content + * + * @param updateCallback The function called every time the stream updates + * @return A Promise of the full content + */ + accumulate(updateCallback?: (metadata: JSZipMetadata) => void): Promise; + + /** + * Resume the stream if the stream is paused. Once resumed, the stream starts sending data events again + * + * @return The current StreamHelper object, for chaining + */ + resume(): this; + + /** + * Pause the stream if the stream is running. Once paused, the stream stops sending data events + * + * @return The current StreamHelper object, for chaining + */ + pause(): this; + } +} + +interface JSZip { + files: {[key: string]: JSZip.JSZipObject}; + + /** + * Get a file from the archive + * + * @param path Path relative path to file + * @return File matching path, null if no file found + */ + file(path: string): JSZip.JSZipObject | null; + + /** + * Get files matching a RegExp from archive + * + * @param path RegExp to match + * @return Return all matching files or an empty array + */ + file(path: RegExp): JSZip.JSZipObject[]; + + /** + * Add a file to the archive + * + * @param path Relative path to file + * @param data Content of the file + * @param options Optional information about the file + * @return JSZip object + */ + file(path: string, data: InputByType[T] | Promise, options?: JSZip.JSZipFileOptions): this; + file(path: string, data: null, options?: JSZip.JSZipFileOptions & { dir: true }): this; + + /** + * Returns an new JSZip instance with the given folder as root + * + * @param name Name of the folder + * @return New JSZip object with the given folder as root or null + */ + folder(name: string): JSZip | null; + + /** + * Returns new JSZip instances with the matching folders as root + * + * @param name RegExp to match + * @return New array of JSZipFile objects which match the RegExp + */ + folder(name: RegExp): JSZip.JSZipObject[]; + + /** + * Call a callback function for each entry at this folder level. + * + * @param callback function + */ + forEach(callback: (relativePath: string, file: JSZip.JSZipObject) => void): void; + + /** + * Get all files which match the given filter function + * + * @param predicate Filter function + * @return Array of matched elements + */ + filter(predicate: (relativePath: string, file: JSZip.JSZipObject) => boolean): JSZip.JSZipObject[]; + + /** + * Removes the file or folder from the archive + * + * @param path Relative path of file or folder + * @return Returns the JSZip instance + */ + remove(path: string): JSZip; + + /** + * Generates a new archive asynchronously + * + * @param options Optional options for the generator + * @param onUpdate The optional function called on each internal update with the metadata. + * @return The serialized archive + */ + generateAsync(options?: JSZip.JSZipGeneratorOptions, onUpdate?: JSZip.OnUpdateCallback): Promise; + + /** + * Generates a new archive asynchronously + * + * @param options Optional options for the generator + * @param onUpdate The optional function called on each internal update with the metadata. + * @return A Node.js `ReadableStream` + */ + generateNodeStream(options?: JSZip.JSZipGeneratorOptions<'nodebuffer'>, onUpdate?: JSZip.OnUpdateCallback): NodeJS.ReadableStream; + + /** + * Generates the complete zip file with the internal stream implementation + * + * @param options Optional options for the generator + * @return a StreamHelper + */ + generateInternalStream(options?: JSZip.JSZipGeneratorOptions): JSZip.JSZipStreamHelper; + + /** + * Deserialize zip file asynchronously + * + * @param data Serialized zip file + * @param options Options for deserializing + * @return Returns promise + */ + loadAsync(data: InputFileFormat, options?: JSZip.JSZipLoadOptions): Promise; + + /** + * Create JSZip instance + */ + new(): this; + + (): JSZip; + + prototype: JSZip; + support: JSZipSupport; + external: { + Promise: PromiseConstructorLike; + }; + version: string; +} + +declare var JSZip: JSZip; + +//WSH: Export was removed to avoid having to import JSZip as a module. \ No newline at end of file diff --git a/extras/jszip/jszip.min.js b/extras/jszip/jszip.min.js new file mode 100644 index 0000000..ff4cfd5 --- /dev/null +++ b/extras/jszip/jszip.min.js @@ -0,0 +1,13 @@ +/*! + +JSZip v3.10.1 - A JavaScript class for generating and reading zip files + + +(c) 2009-2016 Stuart Knightley +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown. + +JSZip uses the library pako released under the MIT license : +https://github.com/nodeca/pako/blob/main/LICENSE +*/ + +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JSZip=e()}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof require&&require;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h)}return o[r].exports}for(var l="function"==typeof require&&require,e=0;e>2,s=(3&t)<<4|r>>4,a=1>6:64,o=2>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l}},{"./support":30,"./utils":32}],2:[function(e,t,r){"use strict";var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){"use strict";var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){"use strict";var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t.charCodeAt(a))];return-1^e}(0|t,e,e.length,0):0}},{"./utils":32}],5:[function(e,t,r){"use strict";r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null},{}],6:[function(e,t,r){"use strict";var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n}},{lie:37}],7:[function(e,t,r){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e,t){a.call(this,"FlateWorker/"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={}}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.data),!1)},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0)},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null},h.prototype._createPako=function(){this._pako=new i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta})}},r.compressWorker=function(e){return new h("Deflate",e)},r.uncompressWorker=function(){return new h("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(e,t,r){"use strict";function A(e,t){var r,n="";for(r=0;r>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),c=I.transformTo("string",O.utf8encode(h.name)),d=h.comment,p=I.transformTo("string",s(d)),m=I.transformTo("string",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+c,b+="up"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+="uc"+A(y.length,2)+y);var E="";return E+="\n\0",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+"\0\0\0\0"+A(z,4)+A(n,4)+f+b+p}}var I=e("../utils"),i=e("../stream/GenericWorker"),O=e("../utf8"),B=e("../crc32"),R=e("../signature");function s(e,t,r,n){i.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}))},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}})}else this.accumulate=!0},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},s.prototype.flush=function(){for(var e=this.bytesWritten,t=0;t=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo("string",this.readData(e))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i},{"../utils":32}],19:[function(e,t,r){"use strict";var n=e("./Uint8ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./DataReader":18}],21:[function(e,t,r){"use strict";var n=e("./ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./ArrayReader":17}],22:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../support"),s=e("./ArrayReader"),a=e("./StringReader"),o=e("./NodeBufferReader"),h=e("./Uint8ArrayReader");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),"string"!==t||i.uint8array?"nodebuffer"===t?new o(e):i.uint8array?new h(n.transformTo("uint8array",e)):new s(n.transformTo("array",e)):new a(e)}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(e,t,r){"use strict";r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\b"},{}],24:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../utils");function s(e){n.call(this,"ConvertWorker to "+e),this.destType=e}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta})},t.exports=s},{"../utils":32,"./GenericWorker":28}],25:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../crc32");function s(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}e("../utils").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e)},t.exports=s},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataLengthProbe for "+e),this.propName=e,this.withStreamInfo(e,0)}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length}i.prototype.processChunk.call(this,e)},t.exports=s},{"../utils":32,"./GenericWorker":28}],27:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataWorker");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat()},function(e){t.error(e)})}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":e=this.data.substring(this.index,t);break;case"uint8array":e=this.data.subarray(this.index,t);break;case"array":case"nodebuffer":e=this.data.slice(this.index,t)}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s},{"../utils":32,"./GenericWorker":28}],28:[function(e,t,r){"use strict";function n(e){this.name=e||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}n.prototype={push:function(e){this.emit("data",e)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(e){this.emit("error",e)}return!0},error:function(e){return!this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit("error",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(e,t){if(this._listeners[e])for(var r=0;r "+e:e}},t.exports=n},{}],29:[function(e,t,r){"use strict";var h=e("../utils"),i=e("./ConvertWorker"),s=e("./GenericWorker"),u=e("../base64"),n=e("../support"),a=e("../external"),o=null;if(n.nodestream)try{o=e("../nodejs/NodejsStreamOutputAdapter")}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on("data",function(e,t){n.push(e),o&&o(t)}).on("error",function(e){n=[],r(e)}).on("end",function(){try{var e=function(e,t,r){switch(e){case"blob":return h.newBlob(h.transformTo("arraybuffer",t),r);case"base64":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t}(e)},s.utf8decode=function(e){return h.nodebuffer?o.transformTo("nodebuffer",e).toString("utf-8"):function(e){var t,r,n,i,s=e.length,a=new Array(2*s);for(t=r=0;t>10&1023,a[r++]=56320|1023&n)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(e=o.transformTo(h.uint8array?"uint8array":"array",e))},o.inherits(a,n),a.prototype.processChunk=function(e){var t=o.transformTo(h.uint8array?"uint8array":"array",e.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=t;(t=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),t.set(r,this.leftOver.length)}else t=this.leftOver.concat(t);this.leftOver=null}var n=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}(t),i=t;n!==t.length&&(h.uint8array?(i=t.subarray(0,n),this.leftOver=t.subarray(n,t.length)):(i=t.slice(0,n),this.leftOver=t.slice(n,t.length))),this.push({data:s.utf8decode(i),meta:e.meta})},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},s.Utf8DecodeWorker=a,o.inherits(l,n),l.prototype.processChunk=function(e){this.push({data:s.utf8encode(e.data),meta:e.meta})},s.Utf8EncodeWorker=l},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(e,t,a){"use strict";var o=e("./support"),h=e("./base64"),r=e("./nodejsUtils"),u=e("./external");function n(e){return e}function l(e,t){for(var r=0;r>8;this.dir=!!(16&this.externalFileAttributes),0==e&&(this.dosPermissions=63&this.externalFileAttributes),3==e&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var e=n(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=e.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=e.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=e.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=e.readInt(4))}},readExtraFields:function(e){var t,r,n,i=e.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});e.index+4>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t},r.buf2binstring=function(e){return l(e,e.length)},r.binstring2buf=function(e){for(var t=new h.Buf8(e.length),r=0,n=t.length;r>10&1023,o[n++]=56320|1023&i)}return l(o,n)},r.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}},{"./common":41}],43:[function(e,t,r){"use strict";t.exports=function(e,t,r,n){for(var i=65535&e|0,s=e>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}},{}],46:[function(e,t,r){"use strict";var h,c=e("../utils/common"),u=e("./trees"),d=e("./adler32"),p=e("./crc32"),n=e("./messages"),l=0,f=4,m=0,_=-2,g=-1,b=4,i=2,v=8,y=9,s=286,a=30,o=19,w=2*s+1,k=15,x=3,S=258,z=S+x+1,C=42,E=113,A=1,I=2,O=3,B=4;function R(e,t){return e.msg=n[t],t}function T(e){return(e<<1)-(4e.avail_out&&(r=e.avail_out),0!==r&&(c.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0))}function N(e,t){u._tr_flush_block(e,0<=e.block_start?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,F(e.strm)}function U(e,t){e.pending_buf[e.pending++]=t}function P(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t}function L(e,t){var r,n,i=e.max_chain_length,s=e.strstart,a=e.prev_length,o=e.nice_match,h=e.strstart>e.w_size-z?e.strstart-(e.w_size-z):0,u=e.window,l=e.w_mask,f=e.prev,c=e.strstart+S,d=u[s+a-1],p=u[s+a];e.prev_length>=e.good_match&&(i>>=2),o>e.lookahead&&(o=e.lookahead);do{if(u[(r=t)+a]===p&&u[r+a-1]===d&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do{}while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&sh&&0!=--i);return a<=e.lookahead?a:e.lookahead}function j(e){var t,r,n,i,s,a,o,h,u,l,f=e.w_size;do{if(i=e.window_size-e.lookahead-e.strstart,e.strstart>=f+(f-z)){for(c.arraySet(e.window,e.window,f,f,0),e.match_start-=f,e.strstart-=f,e.block_start-=f,t=r=e.hash_size;n=e.head[--t],e.head[t]=f<=n?n-f:0,--r;);for(t=r=f;n=e.prev[--t],e.prev[t]=f<=n?n-f:0,--r;);i+=f}if(0===e.strm.avail_in)break;if(a=e.strm,o=e.window,h=e.strstart+e.lookahead,u=i,l=void 0,l=a.avail_in,u=x)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x)if(n=u._tr_tally(e,e.strstart-e.match_start,e.match_length-x),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=x){for(e.match_length--;e.strstart++,e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x&&e.match_length<=e.prev_length){for(i=e.strstart+e.lookahead-x,n=u._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-x),e.lookahead-=e.prev_length-1,e.prev_length-=2;++e.strstart<=i&&(e.ins_h=(e.ins_h<e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(j(e),0===e.lookahead&&t===l)return A;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,N(e,!1),0===e.strm.avail_out))return A;if(e.strstart-e.block_start>=e.w_size-z&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):(e.strstart>e.block_start&&(N(e,!1),e.strm.avail_out),A)}),new M(4,4,8,4,Z),new M(4,5,16,8,Z),new M(4,6,32,32,Z),new M(4,4,16,16,W),new M(8,16,32,32,W),new M(8,16,128,128,W),new M(8,32,128,256,W),new M(32,128,258,1024,W),new M(32,258,258,4096,W)],r.deflateInit=function(e,t){return Y(e,t,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?_:(e.state.gzhead=t,m):_},r.deflate=function(e,t){var r,n,i,s;if(!e||!e.state||5>8&255),U(n,n.gzhead.time>>16&255),U(n,n.gzhead.time>>24&255),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,255&n.gzhead.os),n.gzhead.extra&&n.gzhead.extra.length&&(U(n,255&n.gzhead.extra.length),U(n,n.gzhead.extra.length>>8&255)),n.gzhead.hcrc&&(e.adler=p(e.adler,n.pending_buf,n.pending,0)),n.gzindex=0,n.status=69):(U(n,0),U(n,0),U(n,0),U(n,0),U(n,0),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,3),n.status=E);else{var a=v+(n.w_bits-8<<4)<<8;a|=(2<=n.strategy||n.level<2?0:n.level<6?1:6===n.level?2:3)<<6,0!==n.strstart&&(a|=32),a+=31-a%31,n.status=E,P(n,a),0!==n.strstart&&(P(n,e.adler>>>16),P(n,65535&e.adler)),e.adler=1}if(69===n.status)if(n.gzhead.extra){for(i=n.pending;n.gzindex<(65535&n.gzhead.extra.length)&&(n.pending!==n.pending_buf_size||(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending!==n.pending_buf_size));)U(n,255&n.gzhead.extra[n.gzindex]),n.gzindex++;n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),n.gzindex===n.gzhead.extra.length&&(n.gzindex=0,n.status=73)}else n.status=73;if(73===n.status)if(n.gzhead.name){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.gzindex=0,n.status=91)}else n.status=91;if(91===n.status)if(n.gzhead.comment){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.status=103)}else n.status=103;if(103===n.status&&(n.gzhead.hcrc?(n.pending+2>n.pending_buf_size&&F(e),n.pending+2<=n.pending_buf_size&&(U(n,255&e.adler),U(n,e.adler>>8&255),e.adler=0,n.status=E)):n.status=E),0!==n.pending){if(F(e),0===e.avail_out)return n.last_flush=-1,m}else if(0===e.avail_in&&T(t)<=T(r)&&t!==f)return R(e,-5);if(666===n.status&&0!==e.avail_in)return R(e,-5);if(0!==e.avail_in||0!==n.lookahead||t!==l&&666!==n.status){var o=2===n.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(j(e),0===e.lookahead)){if(t===l)return A;break}if(e.match_length=0,r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):3===n.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=S){if(j(e),e.lookahead<=S&&t===l)return A;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=x&&0e.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=x?(r=u._tr_tally(e,1,e.match_length-x),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):h[n.level].func(n,t);if(o!==O&&o!==B||(n.status=666),o===A||o===O)return 0===e.avail_out&&(n.last_flush=-1),m;if(o===I&&(1===t?u._tr_align(n):5!==t&&(u._tr_stored_block(n,0,0,!1),3===t&&(D(n.head),0===n.lookahead&&(n.strstart=0,n.block_start=0,n.insert=0))),F(e),0===e.avail_out))return n.last_flush=-1,m}return t!==f?m:n.wrap<=0?1:(2===n.wrap?(U(n,255&e.adler),U(n,e.adler>>8&255),U(n,e.adler>>16&255),U(n,e.adler>>24&255),U(n,255&e.total_in),U(n,e.total_in>>8&255),U(n,e.total_in>>16&255),U(n,e.total_in>>24&255)):(P(n,e.adler>>>16),P(n,65535&e.adler)),F(e),0=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new c.Buf8(r.w_size),c.arraySet(u,t,l-r.w_size,r.w_size,0),t=u,l=r.w_size),a=e.avail_in,o=e.next_in,h=e.input,e.avail_in=l,e.next_in=0,e.input=t,j(r);r.lookahead>=x;){for(n=r.strstart,i=r.lookahead-(x-1);r.ins_h=(r.ins_h<>>=y=v>>>24,p-=y,0===(y=v>>>16&255))C[s++]=65535&v;else{if(!(16&y)){if(0==(64&y)){v=m[(65535&v)+(d&(1<>>=y,p-=y),p<15&&(d+=z[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<>>=y,p-=y,(y=s-a)>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){e.msg="unknown compression method",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg="invalid window size",r.mode=30;break}r.dmax=1<>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg="invalid block type",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid code lengths set",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){e.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l>>=_)),u>>>=3,l-=3}else{for(z=_+7;l>>=_)),u>>>=7,l-=7}if(r.have+d>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.msg="invalid code -- missing end-of-block",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid literal/lengths set",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg="invalid literal/length code",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg="invalid distance code",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){e.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(hd?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]]}if(k>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u>=7;n>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{"../utils/common":41}],53:[function(e,t,r){"use strict";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(e,t,r){(function(e){!function(r,n){"use strict";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i="[object process]"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage("","*"),r.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",r.addEventListener?r.addEventListener("message",d,!1):r.attachEvent("onmessage",d),function(e){r.postMessage(a+e,"*")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&"onreadystatechange"in l.createElement("script")?(s=l.documentElement,function(e){var t=l.createElement("script");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r /// /// /// +class AmeBaseKnockoutDialog { + constructor() { + this.isOpen = ko.observable(false); + this.jQueryWidget = null; + this.title = ko.observable(null); + this.options = { + buttons: [] + }; + const confirmButtonLabel = this.getConfirmButtonLabel(); + if (confirmButtonLabel !== null) { + this.options.buttons.push({ + text: confirmButtonLabel, + 'class': 'button button-primary ame-dialog-confirm-button', + click: this.handleConfirmButtonClick.bind(this), + disabled: true //Should be enabled by using the isConfirmButtonEnabled observable. + }); + } + } + handleConfirmButtonClick(event) { + if (!this.isConfirmButtonEnabled()) { + event.preventDefault(); + return; + } + this.onConfirm(event); + } + onConfirm(event) { + //Override in subclasses. + event.preventDefault(); + } + onSubmit(event) { + if (this.isConfirmButtonEnabled()) { + this.onConfirm(event); + } + else { + event.preventDefault(); + } + } + getConfirmButtonLabel() { + return null; + } +} /* * jQuery Dialog binding for Knockout. * @@ -16,9 +58,9 @@ */ ko.bindingHandlers.ameDialog = { init: function (element, valueAccessor) { - var dialog = ko.utils.unwrapObservable(valueAccessor()); - var _ = wsAmeLodash; - var options = dialog.options ? dialog.options : {}; + const dialog = ko.utils.unwrapObservable(valueAccessor()); + const _ = wsAmeLodash; + let options = dialog.options ? dialog.options : {}; if (!dialog.hasOwnProperty('isOpen')) { dialog.isOpen = ko.observable(false); } @@ -41,10 +83,11 @@ ko.bindingHandlers.ameDialog = { dialog.onClose(event, ui); } }; - var buttons = (typeof options['buttons'] !== 'undefined') ? ko.utils.unwrapObservable(options.buttons) : []; - if (options.autoCancelButton) { - //In WordPress, the "Cancel" option is usually on the left side of the form/dialog/pop-up. - buttons.unshift({ + let buttons = (typeof options['buttons'] !== 'undefined') ? ko.utils.unwrapObservable(options.buttons) : []; + const autoCancelButton = _.get(options, 'autoCancelButton', false); + if (autoCancelButton && Array.isArray(buttons)) { + //In AME, the "Cancel" option is usually on the right side of the form/dialog/pop-up. + buttons.push({ text: 'Cancel', 'class': 'button button-secondary ame-dialog-cancel-button', click: function () { @@ -57,7 +100,7 @@ ko.bindingHandlers.ameDialog = { dialog.title = ko.observable(_.get(options, 'title', null)); } else if (dialog.title()) { - options.title = dialog.title(); + options.title = dialog.title() || undefined; } //Do in a setTimeout so that applyBindings doesn't bind twice from element being copied and moved to bottom. window.setTimeout(function () { @@ -76,11 +119,11 @@ ko.bindingHandlers.ameDialog = { }); }, update: function (element, valueAccessor) { - var dialog = ko.utils.unwrapObservable(valueAccessor()); - var $element = jQuery(element); - var shouldBeOpen = ko.utils.unwrapObservable(dialog.isOpen); + const dialog = ko.utils.unwrapObservable(valueAccessor()); + const $element = jQuery(element); + const shouldBeOpen = ko.utils.unwrapObservable(dialog.isOpen); //Do nothing if the dialog hasn't been initialized yet. - var $widget = $element.dialog('instance'); + const $widget = $element.dialog('instance'); if (!$widget) { return; } @@ -91,10 +134,10 @@ ko.bindingHandlers.ameDialog = { }; ko.bindingHandlers.ameOpenDialog = { init: function (element, valueAccessor) { - var clickHandler = function (event) { - var dialogSelector = ko.utils.unwrapObservable(valueAccessor()); + const clickHandler = function (event) { + const dialogSelector = ko.utils.unwrapObservable(valueAccessor()); //Do nothing if the dialog hasn't been initialized yet. - var $widget = jQuery(dialogSelector); + const $widget = jQuery(dialogSelector); if (!$widget.dialog('instance')) { return; } @@ -124,29 +167,31 @@ ko.bindingHandlers.ameEnableDialogButton = { init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { //This binding could be applied before the dialog is initialised. In this case, the button won't exist yet. //Wait for the dialog to be created and then update the button. - var dialogNode = jQuery(element).closest('.ui-dialog'); + const dialogNode = jQuery(element).closest('.ui-dialog'); if (dialogNode.length < 1) { - var body_1 = jQuery(element).closest('body'); + const body = jQuery(element).closest('body'); function setInitialButtonState() { //Is this our dialog? - var dialogNode = jQuery(element).closest('.ui-dialog'); + let dialogNode = jQuery(element).closest('.ui-dialog'); if (dialogNode.length < 1) { return; //Nope. } //Yes. Remove the event handler and update the binding. - body_1.off('dialogcreate', setInitialButtonState); - ko.bindingHandlers.ameEnableDialogButton.update(element, valueAccessor, allBindings, viewModel, bindingContext); + body.off('dialogcreate', setInitialButtonState); + if (ko.bindingHandlers.ameEnableDialogButton.update) { + ko.bindingHandlers.ameEnableDialogButton.update(element, valueAccessor, allBindings, viewModel, bindingContext); + } } - body_1.on('dialogcreate', setInitialButtonState); + body.on('dialogcreate', setInitialButtonState); //If our dialog never gets created, we still want to clean up the event handler eventually. ko.utils.domNodeDisposal.addDisposeCallback(element, function () { - body_1.off('dialogcreate', setInitialButtonState); + body.off('dialogcreate', setInitialButtonState); }); } }, update: function (element, valueAccessor) { - var _ = wsAmeLodash; - var options = ko.unwrap(valueAccessor()); + const _ = wsAmeLodash; + let options = ko.unwrap(valueAccessor()); if (!_.isPlainObject(options)) { options = { enabled: options }; } @@ -163,32 +208,161 @@ ko.bindingHandlers.ameEnableDialogButton = { }; ko.bindingHandlers.ameColorPicker = { init: function (element, valueAccessor) { - var valueUnwrapped = ko.unwrap(valueAccessor()); - var input = jQuery(element); + let valueUnwrapped = ko.unwrap(valueAccessor()); + const input = jQuery(element); input.val(valueUnwrapped); + const guard = '__ameColorPickerIgnoreChange'; + let ignoredObservableValue = guard; + let ignoredPickerValue = guard; + function maybeUpdateObservableFromPicker(newValue) { + //Don't update the observable if this color picker change was triggered + //by the observable itself changing (see the computed observable below). + if (newValue === ignoredPickerValue) { + return; + } + let observable = valueAccessor(); + if (!ko.isWriteableObservable(observable)) { + return; //Can't update this thing. + } + //Don't update the observable if the value hasn't changed. + //This helps prevent infinite loops. + if (newValue === observable.peek()) { + return; + } + //Avoid an unnecessary color picker update when changing the observable value. + //This also helps prevent loops, and prevents a subtle bug where quickly + //changing the color (e.g. by dragging the saturation slider) would cause + //the picker to "drift" away from the actual color. + ignoredObservableValue = newValue; + observable(newValue); + ignoredObservableValue = guard; + } input.wpColorPicker({ change: function (event, ui) { - var value = valueAccessor(); - value(ui.color.toString()); + maybeUpdateObservableFromPicker(ui.color.toString()); }, clear: function () { - var value = valueAccessor(); - value(''); + maybeUpdateObservableFromPicker(''); } }); - }, - update: function (element, valueAccessor) { - var newValue = ko.unwrap(valueAccessor()); - if (typeof newValue !== 'string') { - newValue = ''; + //Update the picker when the observable changes. We're using a computed observable + //instead of the "update" callback because this lets us store state in the closure. + ko.computed(function () { + let newValue = ko.unwrap(valueAccessor()); + if (typeof newValue !== 'string') { + newValue = ''; + } + if (newValue === ignoredObservableValue) { + return; + } + ignoredPickerValue = newValue; + if (newValue === '') { + //Programmatically click the "Clear" button. It's not elegant, but I haven't found + //a way to do this using the Iris API. + input.closest('.wp-picker-input-wrap').find('.wp-picker-clear').trigger('click'); + } + else { + input.iris('color', newValue); + } + ignoredPickerValue = guard; + }, null, { disposeWhenNodeIsRemoved: element }); + } +}; +/** + * This binding generates a custom JS event when the value of an observable changes. + * It also listens for another custom event and updates the observable to the specified value. + * + * Usage: + * + * + * Alternatively, you can set the parameter to "true". This binding will then look for + * other bindings that are commonly used to set the value of an element (e.g. "value"), + * and use the observable value of the first one it finds. + * + * Finally, you can pass an object with the following properties: + * - observable: The observable to watch for changes, or "true" (see above). + * - sendInitEvent: If true, the binding will send an event when it's initialized. + */ +ko.bindingHandlers.ameObservableChangeEvents = { + init: function (element, valueAccessor, allBindings) { + const defaults = { + observable: null, + sendInitEvent: false + }; + let options = valueAccessor(); + if (ko.isObservable(options)) { + //Just the observable. + options = Object.assign({}, defaults, { observable: options }); } - if (newValue === '') { - //Programmatically click the "Clear" button. It's not elegant, but I haven't found - //a way to do this using the Iris API. - jQuery(element).closest('.wp-picker-input-wrap').find('.wp-picker-clear').trigger('click'); + else if (options === true) { + //"true" means we'll try to find the observable automatically (see below). + options = Object.assign({}, defaults, { observable: true }); + } + else if (typeof options === 'object') { + //Custom options. + options = Object.assign({}, defaults, options); } else { - jQuery(element).iris('color', newValue); + throw new Error('Invalid options for the ameObservableChangeEvents binding.'); + } + let targetObservable = options.observable; + if (targetObservable === true) { + let alternativeFound = false; + const possibleValueBindings = ['value', 'checked', 'selectedOptions']; + for (let i = 0; i < possibleValueBindings.length; i++) { + const bindingValue = allBindings.get(possibleValueBindings[i]); + if (ko.isWriteableObservable(bindingValue)) { + targetObservable = bindingValue; + alternativeFound = true; + break; + } + } + if (!alternativeFound) { + throw new Error('ameObservableChangeEvents did not find a suitable observable to watch. ' + + 'Supported bindings: ' + possibleValueBindings.join(', ')); + } + } + else if (!ko.isWriteableObservable(targetObservable)) { + throw new Error('The ameObservableChangeEvents binding accepts only an observable or the boolean "true".'); + } + const inEvent = 'adminMenuEditor:controlValueChanged'; + const outEvent = 'adminMenuEditor:observableValueChanged'; + const initEvent = 'adminMenuEditor:observableBindingInit'; + const $element = jQuery(element); + const uniqueMarker = {}; + let ignoredValue = uniqueMarker; + const subscription = targetObservable.subscribe(function (newValue) { + //Don't trigger the "out" event if the value was changed as a result + //of the "in" event. + if (newValue === ignoredValue) { + return; + } + //console.log('Observable changed: ', newValue); + $element.trigger(outEvent, [newValue]); + }); + const incomingChangeHandler = function (event, newValue) { + //Ignore events from child elements. For example, in a BoxDimensions control, + //popup sliders associated with individual number inputs trigger their own + //"controlValueChanged" events. + if (event.target !== element) { + return; + } + if ((typeof newValue !== 'undefined') && (newValue !== targetObservable.peek())) { + //console.log('Control changed: ', newValue); + ignoredValue = newValue; + targetObservable(newValue); + ignoredValue = uniqueMarker; + } + }; + $element.on(inEvent, incomingChangeHandler); + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { + // @ts-ignore - My jQuery typings are out of date. + $element.off(inEvent, incomingChangeHandler); + subscription.dispose(); + }); + //Optionally, send an initial event to synchronize the control with the observable. + if (options.sendInitEvent) { + $element.trigger(initEvent, [targetObservable.peek()]); } } }; @@ -201,10 +375,299 @@ ko.bindingHandlers.indeterminate = { //A "readonly" property binding for input and textarea elements. ko.bindingHandlers.readonly = { update: function (element, valueAccessor) { - var value = !!(ko.unwrap(valueAccessor())); + const value = !!(ko.unwrap(valueAccessor())); if (value !== element.readOnly) { element.readOnly = value; } } }; +{ + function parseToggleCheckboxOptions(options) { + let parsed = wsAmeLodash.defaults(ko.unwrap(options), { + onValue: true, + offValue: false, + }); + parsed.onValue = ko.unwrap(parsed.onValue); + parsed.offValue = ko.unwrap(parsed.offValue); + return parsed; + } + ko.bindingHandlers.ameToggleCheckbox = { + init: function (element, valueAccessor) { + const $element = jQuery(element); + const changeHandler = function () { + const options = parseToggleCheckboxOptions(valueAccessor()); + if (ko.isWriteableObservable(options.checked)) { + options.checked($element.prop('checked') ? options.onValue : options.offValue); + } + }; + $element.on('change', changeHandler); + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { + $element.off('change', changeHandler); + }); + }, + update: function (element, valueAccessor) { + const options = parseToggleCheckboxOptions(valueAccessor()); + const checked = (ko.unwrap(options.checked) === options.onValue); + jQuery(element).prop('checked', checked); + } + }; +} +//region Validation +//Adds an error CSS class to the element if the associated setting has any validation errors. +ko.bindingHandlers.ameValidationErrorClass = { + init: function (element, valueAccessor) { + const errorClassName = 'ame-has-validation-errors'; + ko.computed({ + read: function () { + //The parameter should be a Setting object. + const value = ko.unwrap(valueAccessor()); + if (!value || (typeof value.validationErrors === 'undefined')) { + return; + } + const errors = ko.unwrap(value.validationErrors); + if (Array.isArray(errors) && (errors.length > 0)) { + element.classList.add(errorClassName); + } + else { + element.classList.remove(errorClassName); + } + }, + disposeWhenNodeIsRemoved: element + }); + } +}; +function ameIsValidatedObservable(observable) { + if ((typeof observable === 'undefined') || (observable === null)) { + return false; + } + return (ko.isObservable(observable) + && ko.isObservable(observable.ameValidationErrors)); +} +{ + const validationErrorCssClass = 'ame-has-validation-errors'; + function isElementWithValidation(element) { + return ((element instanceof HTMLElement) + && (typeof element.setCustomValidity === 'function')); + } + function makeValidityBindingHandler(getValidity) { + return function (element, valueAccessor) { + if (!(element instanceof HTMLElement)) { + throw new Error('This binding can only be used with elements.'); + } + if (!isElementWithValidation(element)) { + throw new Error('This binding can only be used with elements that support the constraint validation API.'); + } + ko.computed({ + read: function () { + const value = valueAccessor(); + const stateOption = getValidity(value); + if (stateOption.isEmpty()) { + return; + } + const state = stateOption.get(); + if (state.isValid) { + element.classList.remove(validationErrorCssClass); + element.setCustomValidity(''); + } + else { + if (!element.classList.contains(validationErrorCssClass)) { + element.classList.add(validationErrorCssClass); + } + const errors = state.errors; + if (Array.isArray(errors) && (errors.length > 0)) { + //Make am error list separated by newlines. + element.setCustomValidity(errors.map(e => e.message).join('\n')); + } + else { + //Default message. + element.setCustomValidity('Invalid value'); + } + } + }, + disposeWhenNodeIsRemoved: element + }); + }; + } + ko.bindingHandlers.ameObservableValidity = { + init: makeValidityBindingHandler((value) => { + if (!ameIsValidatedObservable(value)) { + return AmeMiniFunc.none; + } + const errors = ko.unwrap(value.ameValidationErrors); + return AmeMiniFunc.some({ + isValid: !errors || (errors.length === 0), + errors: errors + }); + }) + }; + ko.bindingHandlers.ameSettingValidity = { + init: makeValidityBindingHandler((value) => { + const valueAsRecord = value; + //The parameter should be a Setting object, but we're not importing + //that here because then this would have to be a module. + if (!valueAsRecord || (typeof valueAsRecord['validationErrors'] === 'undefined')) { + return AmeMiniFunc.none; + } + const errors = ko.unwrap(valueAsRecord.validationErrors); + if (Array.isArray(errors) && (errors.length > 0)) { + return AmeMiniFunc.some({ + isValid: false, + //Setting validation errors should already be structurally compatible + //with ObservableValidationErrors. + errors: errors + }); + } + else { + return AmeMiniFunc.some({ + isValid: true, + errors: [] + }); + } + }) + }; +} +{ + function sanitizeAnyValueAsNumeric(value) { + if (value === null) { + return value; + } + else if (typeof value === 'number') { + return value; + } + return AmeMiniFunc.sanitizeNumericString((typeof value === 'string') ? value : String(value)); + } + /** + * An extender that leniently converts any value to a number or numeric string. + * + * This is useful for input fields that accept numbers, but also allow empty values. + * It is intended for live, as-you-type sanitization, so it is intentionally tolerant + * of incomplete and un-normalized values like: + * + * - "1." (missing the decimal part) + * - "1.500" (unnecessary trailing zeros) + * - "-" (minus sign without a number) + * + * For details on the sanitization algorithm, see {@link AmeMiniFunc.sanitizeNumericString()}. + */ + ko.extenders.ameNumericInput = function (target) { + const displayValue = ko.observable(sanitizeAnyValueAsNumeric(target.peek())); + target.subscribe(function (newValue) { + displayValue(sanitizeAnyValueAsNumeric(newValue)); + }); + const result = ko.pureComputed({ + read: displayValue, + write: function (newValue) { + const oldDisplayValue = displayValue.peek(); + let sanitizedValue = sanitizeAnyValueAsNumeric(newValue); + const hasChanged = (sanitizedValue !== oldDisplayValue); + if (hasChanged) { + displayValue(sanitizedValue); + } + else if (sanitizedValue !== newValue) { + //If the sanitized value is different, we should update any associated form fields + //to show the sanitized value. This is usually automatic except when the sanitized + //value is the same as the old value (e.g. "1a" -> "1"). In that case, we need to + //manually notify subscribers. + result.notifySubscribers(sanitizedValue); + } + //Also update the underlying observable if the value has changed. + if (hasChanged) { + target(sanitizedValue); + } + } + }); + return result; + }; +} +ko.bindingHandlers.ameCodeMirror = { + init: function (element, valueAccessor, allBindings) { + if (!wp.hasOwnProperty('codeEditor') || !wp.codeEditor.initialize) { + return; + } + let parameters = ko.unwrap(valueAccessor()); + if (!parameters) { + return; + } + let options; + let refreshTrigger = null; + if (parameters.options) { + options = parameters.options; + if (parameters.refreshTrigger) { + refreshTrigger = parameters.refreshTrigger; + } + } + else { + options = parameters; + } + let result = wp.codeEditor.initialize(element, options); + const cm = result.codemirror; + //Synchronize the editor contents with the observable passed to the "value" binding. + const valueBinding = allBindings.get('value'); + const valueObservable = (ko.isObservable(valueBinding) ? valueBinding : null); + let subscription = null; + let changeHandler = null; + if (valueObservable !== null) { + //Update the observable when the contents of the editor change. + let ignoreNextUpdate = false; + changeHandler = function () { + //This will trigger our observable subscription (see below). + //We need to ignore that trigger to avoid recursive or duplicated updates. + ignoreNextUpdate = true; + valueObservable(cm.doc.getValue()); + }; + cm.on('changes', changeHandler); + //Update the editor when the observable changes. + subscription = valueObservable.subscribe(function (newValue) { + if (ignoreNextUpdate) { + ignoreNextUpdate = false; + return; + } + cm.doc.setValue(newValue); + ignoreNextUpdate = false; + }); + } + //Refresh the size of the editor element when an observable changes value. + let refreshSubscription = null; + if (refreshTrigger) { + refreshSubscription = refreshTrigger.subscribe(function () { + cm.refresh(); + }); + } + //If the editor starts out hidden - for example, because it's inside a collapsed section - it will + //render incorrectly. To fix that, let's refresh it the first time it becomes visible. + if (!jQuery(element).is(':visible') && (typeof IntersectionObserver !== 'undefined')) { + const observer = new IntersectionObserver(function (entries) { + for (let i = 0; i < entries.length; i++) { + if (entries[i].isIntersecting) { + //The editor is at least partially visible now. + observer.disconnect(); + cm.refresh(); + break; + } + } + }, { + //Use the browser viewport. + root: null, + //The threshold is somewhat arbitrary. Any value will work, but a lower setting means + //that the user is less likely to see an incorrectly rendered editor. + threshold: 0.05 + }); + observer.observe(cm.getWrapperElement()); + } + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { + //Remove subscriptions and event handlers. + if (subscription) { + subscription.dispose(); + } + if (refreshSubscription) { + refreshSubscription.dispose(); + } + if (changeHandler) { + cm.off('changes', changeHandler); + } + //Destroy the CodeMirror instance. + jQuery(cm.getWrapperElement()).remove(); + }); + } +}; //# sourceMappingURL=ko-extensions.js.map \ No newline at end of file diff --git a/extras/ko-extensions.js.map b/extras/ko-extensions.js.map index 90bfa76..e7a5bb1 100644 --- a/extras/ko-extensions.js.map +++ b/extras/ko-extensions.js.map @@ -1 +1 @@ -{"version":3,"file":"ko-extensions.js","sourceRoot":"","sources":["ko-extensions.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"ko-extensions.js","sourceRoot":"","sources":["ko-extensions.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/ko-extensions.ts b/extras/ko-extensions.ts index 4bc3a5c..ca49069 100644 --- a/extras/ko-extensions.ts +++ b/extras/ko-extensions.ts @@ -10,12 +10,61 @@ interface AmeKnockoutDialog { options?: Record; autoCancelButton?: boolean; - jQueryWidget: JQuery; - title: KnockoutObservable; + jQueryWidget: JQuery | null; + title: KnockoutObservable; - onOpen?(event, ui); + onOpen?(event: JQueryEventObject, ui: any): void; - onClose?(event, ui); + onClose?(event: JQueryEventObject, ui: any): void; +} + +abstract class AmeBaseKnockoutDialog implements AmeKnockoutDialog { + isOpen: KnockoutObservable = ko.observable(false); + jQueryWidget: JQuery | null = null; + title: KnockoutObservable = ko.observable(null); + + options: Record = { + buttons: [] + }; + + abstract isConfirmButtonEnabled: KnockoutObservable; + + protected constructor() { + const confirmButtonLabel = this.getConfirmButtonLabel(); + if (confirmButtonLabel !== null) { + this.options.buttons.push({ + text: confirmButtonLabel, + 'class': 'button button-primary ame-dialog-confirm-button', + click: this.handleConfirmButtonClick.bind(this), + disabled: true //Should be enabled by using the isConfirmButtonEnabled observable. + }); + } + } + + protected handleConfirmButtonClick(event: JQueryEventObject): void { + if (!this.isConfirmButtonEnabled()) { + event.preventDefault(); + return; + } + this.onConfirm(event); + } + + onConfirm(event: JQueryEventObject): void { + //Override in subclasses. + event.preventDefault(); + } + + onSubmit(event: JQueryEventObject): void { + if (this.isConfirmButtonEnabled()) { + this.onConfirm(event); + } else { + event.preventDefault(); + } + } + + protected getConfirmButtonLabel(): string | null { + return null; + } } /* @@ -35,7 +84,7 @@ ko.bindingHandlers.ameDialog = { const dialog = ko.utils.unwrapObservable(valueAccessor()) as AmeKnockoutDialog; const _ = wsAmeLodash; - let options = dialog.options ? dialog.options : {}; + let options: JQueryUI.DialogOptions = dialog.options ? dialog.options : {}; if (!dialog.hasOwnProperty('isOpen')) { dialog.isOpen = ko.observable(false); } @@ -48,13 +97,13 @@ ko.bindingHandlers.ameDialog = { }); //Update isOpen when the dialog is opened or closed. - options.open = function (event, ui) { + options.open = function (event: JQueryEventObject, ui: any) { dialog.isOpen(true); if (dialog.onOpen) { dialog.onOpen(event, ui); } }; - options.close = function (event, ui) { + options.close = function (event: JQueryEventObject, ui: any) { dialog.isOpen(false); if (dialog.onClose) { dialog.onClose(event, ui); @@ -62,9 +111,10 @@ ko.bindingHandlers.ameDialog = { }; let buttons = (typeof options['buttons'] !== 'undefined') ? ko.utils.unwrapObservable(options.buttons) : []; - if (options.autoCancelButton) { - //In WordPress, the "Cancel" option is usually on the left side of the form/dialog/pop-up. - buttons.unshift({ + const autoCancelButton: boolean = _.get(options, 'autoCancelButton', false); + if (autoCancelButton && Array.isArray(buttons)) { + //In AME, the "Cancel" option is usually on the right side of the form/dialog/pop-up. + buttons.push({ text: 'Cancel', 'class': 'button button-secondary ame-dialog-cancel-button', click: function () { @@ -77,7 +127,7 @@ ko.bindingHandlers.ameDialog = { if (!dialog.hasOwnProperty('title') || (dialog.title === null)) { dialog.title = ko.observable(_.get(options, 'title', null)); } else if (dialog.title()) { - options.title = dialog.title(); + options.title = dialog.title() || undefined; } //Do in a setTimeout so that applyBindings doesn't bind twice from element being copied and moved to bottom. @@ -121,7 +171,7 @@ ko.bindingHandlers.ameDialog = { ko.bindingHandlers.ameOpenDialog = { init: function (element, valueAccessor) { - const clickHandler = function (event) { + const clickHandler = function (event: JQueryEventObject) { const dialogSelector = ko.utils.unwrapObservable(valueAccessor()); //Do nothing if the dialog hasn't been initialized yet. @@ -171,7 +221,15 @@ ko.bindingHandlers.ameEnableDialogButton = { //Yes. Remove the event handler and update the binding. body.off('dialogcreate', setInitialButtonState); - ko.bindingHandlers.ameEnableDialogButton.update(element, valueAccessor, allBindings, viewModel, bindingContext); + if (ko.bindingHandlers.ameEnableDialogButton.update) { + ko.bindingHandlers.ameEnableDialogButton.update( + element, + valueAccessor, + allBindings, + viewModel, + bindingContext + ); + } } body.on('dialogcreate', setInitialButtonState); @@ -212,31 +270,177 @@ ko.bindingHandlers.ameColorPicker = { const input = jQuery(element); input.val(valueUnwrapped); + const guard = '__ameColorPickerIgnoreChange'; + let ignoredObservableValue: string | null = guard; + let ignoredPickerValue: string | null = guard; + + function maybeUpdateObservableFromPicker(newValue: string) { + //Don't update the observable if this color picker change was triggered + //by the observable itself changing (see the computed observable below). + if (newValue === ignoredPickerValue) { + return; + } + + let observable = valueAccessor(); + if (!ko.isWriteableObservable(observable)) { + return; //Can't update this thing. + } + + //Don't update the observable if the value hasn't changed. + //This helps prevent infinite loops. + if (newValue === observable.peek()) { + return; + } + + //Avoid an unnecessary color picker update when changing the observable value. + //This also helps prevent loops, and prevents a subtle bug where quickly + //changing the color (e.g. by dragging the saturation slider) would cause + //the picker to "drift" away from the actual color. + ignoredObservableValue = newValue; + observable(newValue); + ignoredObservableValue = guard; + } + input.wpColorPicker({ - change: function (event, ui) { - let value = valueAccessor(); - value(ui.color.toString()); + change: function (event: JQueryEventObject, ui: any) { + maybeUpdateObservableFromPicker(ui.color.toString()); }, clear: function () { - let value = valueAccessor(); - value(''); + maybeUpdateObservableFromPicker(''); } }); - }, - update: function (element, valueAccessor) { - let newValue = ko.unwrap(valueAccessor()); - if (typeof newValue !== 'string') { - newValue = ''; - } - if (newValue === '') { - //Programmatically click the "Clear" button. It's not elegant, but I haven't found - //a way to do this using the Iris API. - jQuery(element).closest('.wp-picker-input-wrap').find('.wp-picker-clear').trigger('click'); + + //Update the picker when the observable changes. We're using a computed observable + //instead of the "update" callback because this lets us store state in the closure. + ko.computed(function () { + let newValue = ko.unwrap(valueAccessor()); + if (typeof newValue !== 'string') { + newValue = ''; + } + if (newValue === ignoredObservableValue) { + return; + } + + ignoredPickerValue = newValue; + if (newValue === '') { + //Programmatically click the "Clear" button. It's not elegant, but I haven't found + //a way to do this using the Iris API. + input.closest('.wp-picker-input-wrap').find('.wp-picker-clear').trigger('click'); + } else { + input.iris('color', newValue); + } + ignoredPickerValue = guard; + }, null, {disposeWhenNodeIsRemoved: element}); + } +}; + +/** + * This binding generates a custom JS event when the value of an observable changes. + * It also listens for another custom event and updates the observable to the specified value. + * + * Usage: + * + * + * Alternatively, you can set the parameter to "true". This binding will then look for + * other bindings that are commonly used to set the value of an element (e.g. "value"), + * and use the observable value of the first one it finds. + * + * Finally, you can pass an object with the following properties: + * - observable: The observable to watch for changes, or "true" (see above). + * - sendInitEvent: If true, the binding will send an event when it's initialized. + */ +ko.bindingHandlers.ameObservableChangeEvents = { + init: function (element, valueAccessor, allBindings) { + const defaults = { + observable: null, + sendInitEvent: false + }; + + let options = valueAccessor(); + + if (ko.isObservable(options)) { + //Just the observable. + options = Object.assign({}, defaults, {observable: options}); + } else if (options === true) { + //"true" means we'll try to find the observable automatically (see below). + options = Object.assign({}, defaults, {observable: true}); + } else if (typeof options === 'object') { + //Custom options. + options = Object.assign({}, defaults, options); } else { - jQuery(element).iris('color', newValue); + throw new Error('Invalid options for the ameObservableChangeEvents binding.'); + } + + let targetObservable = options.observable; + if (targetObservable === true) { + let alternativeFound = false; + const possibleValueBindings = ['value', 'checked', 'selectedOptions']; + for (let i = 0; i < possibleValueBindings.length; i++) { + const bindingValue = allBindings.get(possibleValueBindings[i]); + if (ko.isWriteableObservable(bindingValue)) { + targetObservable = bindingValue; + alternativeFound = true; + break; + } + } + if (!alternativeFound) { + throw new Error( + 'ameObservableChangeEvents did not find a suitable observable to watch. ' + + 'Supported bindings: ' + possibleValueBindings.join(', ') + ); + } + } else if (!ko.isWriteableObservable(targetObservable)) { + throw new Error('The ameObservableChangeEvents binding accepts only an observable or the boolean "true".'); + } + + const inEvent = 'adminMenuEditor:controlValueChanged'; + const outEvent = 'adminMenuEditor:observableValueChanged'; + const initEvent = 'adminMenuEditor:observableBindingInit'; + + const $element = jQuery(element); + const uniqueMarker = {}; + let ignoredValue = uniqueMarker; + + const subscription = targetObservable.subscribe(function (newValue: unknown) { + //Don't trigger the "out" event if the value was changed as a result + //of the "in" event. + if (newValue === ignoredValue) { + return; + } + //console.log('Observable changed: ', newValue); + $element.trigger(outEvent, [newValue]); + }); + + const incomingChangeHandler = function (event: JQueryEventObject, newValue: unknown) { + //Ignore events from child elements. For example, in a BoxDimensions control, + //popup sliders associated with individual number inputs trigger their own + //"controlValueChanged" events. + if (event.target !== element) { + return; + } + + if ((typeof newValue !== 'undefined') && (newValue !== targetObservable.peek())) { + //console.log('Control changed: ', newValue); + ignoredValue = newValue as any; + targetObservable(newValue); + ignoredValue = uniqueMarker; + } + }; + + $element.on(inEvent, incomingChangeHandler); + + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { + // @ts-ignore - My jQuery typings are out of date. + $element.off(inEvent, incomingChangeHandler); + subscription.dispose(); + }); + + //Optionally, send an initial event to synchronize the control with the observable. + if (options.sendInitEvent) { + $element.trigger(initEvent, [targetObservable.peek()]); } } -}; +} //A one-way binding for indeterminate checkbox states. ko.bindingHandlers.indeterminate = { @@ -253,4 +457,382 @@ ko.bindingHandlers.readonly = { element.readOnly = value; } } +}; + +{ + interface ToggleCheckboxOptions { + checked: KnockoutObservable; + onValue: any, + offValue: any + } + + function parseToggleCheckboxOptions(options: object): ToggleCheckboxOptions { + let parsed: ToggleCheckboxOptions = wsAmeLodash.defaults( + ko.unwrap(options), + { + onValue: true, + offValue: false, + } + ); + parsed.onValue = ko.unwrap(parsed.onValue); + parsed.offValue = ko.unwrap(parsed.offValue); + return parsed; + } + + ko.bindingHandlers.ameToggleCheckbox = { + init: function (element, valueAccessor) { + const $element = jQuery(element); + + const changeHandler = function () { + const options = parseToggleCheckboxOptions(valueAccessor()); + if (ko.isWriteableObservable(options.checked)) { + options.checked( + $element.prop('checked') ? options.onValue : options.offValue + ); + } + } + + $element.on('change', changeHandler); + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { + $element.off('change', changeHandler); + }); + }, + + update: function (element, valueAccessor) { + const options = parseToggleCheckboxOptions(valueAccessor()); + const checked = (ko.unwrap(options.checked) === options.onValue); + jQuery(element).prop('checked', checked); + } + } +} + +//region Validation + +//Adds an error CSS class to the element if the associated setting has any validation errors. +ko.bindingHandlers.ameValidationErrorClass = { + init: function (element, valueAccessor) { + const errorClassName = 'ame-has-validation-errors'; + + ko.computed({ + read: function () { + //The parameter should be a Setting object. + const value = ko.unwrap(valueAccessor()); + if (!value || (typeof value.validationErrors === 'undefined')) { + return; + } + + const errors = ko.unwrap(value.validationErrors); + if (Array.isArray(errors) && (errors.length > 0)) { + element.classList.add(errorClassName); + } else { + element.classList.remove(errorClassName); + } + }, + disposeWhenNodeIsRemoved: element + }); + } +}; + +//This could be extracted to AmeValidationError in common.d.ts. Then it could be used +//for both settings and observables. +interface ObservableValidationError { + message: string; + code?: string; +} + +interface ObservableValidationFields { + ameValidationErrors: KnockoutObservable; + ameIsValid: () => boolean; +} + +type ValidatedObservable = + (KnockoutComputed & ObservableValidationFields) + | (KnockoutObservable & ObservableValidationFields); + +function ameIsValidatedObservable(observable: unknown): observable is ValidatedObservable { + if ((typeof observable === 'undefined') || (observable === null)) { + return false; + } + + return ( + ko.isObservable(observable) + && ko.isObservable((observable as any).ameValidationErrors) + ); +} + +{ + const validationErrorCssClass = 'ame-has-validation-errors'; + + interface ElementWithValidation extends HTMLElement { + setCustomValidity: (message: string) => void; + } + + function isElementWithValidation(element: Node): element is ElementWithValidation { + return ( + (element instanceof HTMLElement) + && (typeof (element as any).setCustomValidity === 'function') + ); + } + + type ExtractValidityState = (value: unknown) => AmeMiniFunc.Option<{ + isValid: boolean, + errors: ObservableValidationError[] + }>; + + function makeValidityBindingHandler(getValidity: ExtractValidityState): KnockoutBindingHandler['init'] { + return function (element: Node, valueAccessor: () => any) { + if (!(element instanceof HTMLElement)) { + throw new Error('This binding can only be used with elements.'); + } + if (!isElementWithValidation(element)) { + throw new Error('This binding can only be used with elements that support the constraint validation API.'); + } + + ko.computed({ + read: function () { + const value = valueAccessor(); + const stateOption = getValidity(value); + + if (stateOption.isEmpty()) { + return; + } + const state = stateOption.get(); + + if (state.isValid) { + element.classList.remove(validationErrorCssClass); + element.setCustomValidity(''); + } else { + if (!element.classList.contains(validationErrorCssClass)) { + element.classList.add(validationErrorCssClass); + } + const errors = state.errors; + if (Array.isArray(errors) && (errors.length > 0)) { + //Make am error list separated by newlines. + element.setCustomValidity(errors.map(e => e.message).join('\n')); + } else { + //Default message. + element.setCustomValidity('Invalid value'); + } + } + }, + disposeWhenNodeIsRemoved: element + }); + } + } + + ko.bindingHandlers.ameObservableValidity = { + init: makeValidityBindingHandler((value: unknown) => { + if (!ameIsValidatedObservable(value)) { + return AmeMiniFunc.none; + } + + const errors = ko.unwrap(value.ameValidationErrors); + return AmeMiniFunc.some({ + isValid: !errors || (errors.length === 0), + errors: errors + }); + }) + }; + + ko.bindingHandlers.ameSettingValidity = { + init: makeValidityBindingHandler((value: unknown) => { + const valueAsRecord = value as Record; + + //The parameter should be a Setting object, but we're not importing + //that here because then this would have to be a module. + if (!valueAsRecord || (typeof valueAsRecord['validationErrors'] === 'undefined')) { + return AmeMiniFunc.none; + } + + const errors = ko.unwrap(valueAsRecord.validationErrors); + if (Array.isArray(errors) && (errors.length > 0)) { + return AmeMiniFunc.some({ + isValid: false, + //Setting validation errors should already be structurally compatible + //with ObservableValidationErrors. + errors: errors + }); + } else { + return AmeMiniFunc.some({ + isValid: true, + errors: [] + }); + } + }) + }; +} + +//endregion + +// noinspection JSUnusedGlobalSymbols -- Used to define the "ameNumericInput" extender. +interface KnockoutExtenders { + ameNumericInput: (target: KnockoutObservable) => KnockoutObservable; +} + +{ + type SufficientlyNumericType = string | number | null; + + function sanitizeAnyValueAsNumeric(value: unknown): string | number | null { + if (value === null) { + return value as null; + } else if (typeof value === 'number') { + return value; + } + return AmeMiniFunc.sanitizeNumericString( + (typeof value === 'string') ? value : String(value) + ); + } + + /** + * An extender that leniently converts any value to a number or numeric string. + * + * This is useful for input fields that accept numbers, but also allow empty values. + * It is intended for live, as-you-type sanitization, so it is intentionally tolerant + * of incomplete and un-normalized values like: + * + * - "1." (missing the decimal part) + * - "1.500" (unnecessary trailing zeros) + * - "-" (minus sign without a number) + * + * For details on the sanitization algorithm, see {@link AmeMiniFunc.sanitizeNumericString()}. + */ + ko.extenders.ameNumericInput = function (target: KnockoutObservable) { + const displayValue: KnockoutObservable = ko.observable( + sanitizeAnyValueAsNumeric(target.peek()) + ); + + target.subscribe(function (newValue) { + displayValue(sanitizeAnyValueAsNumeric(newValue)); + }); + + const result = ko.pureComputed({ + read: displayValue, + write: function (newValue: unknown) { + const oldDisplayValue = displayValue.peek(); + + let sanitizedValue: SufficientlyNumericType = sanitizeAnyValueAsNumeric(newValue); + const hasChanged = (sanitizedValue !== oldDisplayValue); + + if (hasChanged) { + displayValue(sanitizedValue); + } else if (sanitizedValue !== newValue) { + //If the sanitized value is different, we should update any associated form fields + //to show the sanitized value. This is usually automatic except when the sanitized + //value is the same as the old value (e.g. "1a" -> "1"). In that case, we need to + //manually notify subscribers. + result.notifySubscribers(sanitizedValue); + } + + //Also update the underlying observable if the value has changed. + if (hasChanged) { + target(sanitizedValue); + } + } + }); + + return result; + } +} + +ko.bindingHandlers.ameCodeMirror = { + init: function (element, valueAccessor, allBindings) { + if (!wp.hasOwnProperty('codeEditor') || !wp.codeEditor.initialize) { + return; + } + let parameters = ko.unwrap(valueAccessor()); + if (!parameters) { + return; + } + + let options; + let refreshTrigger: KnockoutObservable | null = null; + if (parameters.options) { + options = parameters.options; + if (parameters.refreshTrigger) { + refreshTrigger = parameters.refreshTrigger; + } + } else { + options = parameters; + } + + let result = wp.codeEditor.initialize(element, options); + const cm = result.codemirror; + + //Synchronize the editor contents with the observable passed to the "value" binding. + const valueBinding = allBindings.get('value'); + const valueObservable = (ko.isObservable(valueBinding) ? valueBinding : null); + + let subscription: KnockoutSubscription | null = null; + let changeHandler: null | (() => void) = null; + if (valueObservable !== null) { + //Update the observable when the contents of the editor change. + let ignoreNextUpdate = false; + changeHandler = function () { + //This will trigger our observable subscription (see below). + //We need to ignore that trigger to avoid recursive or duplicated updates. + ignoreNextUpdate = true; + valueObservable(cm.doc.getValue()); + }; + cm.on('changes', changeHandler); + + //Update the editor when the observable changes. + subscription = valueObservable.subscribe(function (newValue) { + if (ignoreNextUpdate) { + ignoreNextUpdate = false; + return; + } + cm.doc.setValue(newValue); + ignoreNextUpdate = false; + }); + } + + //Refresh the size of the editor element when an observable changes value. + let refreshSubscription: KnockoutSubscription | null = null; + if (refreshTrigger) { + refreshSubscription = refreshTrigger.subscribe(function () { + cm.refresh(); + }); + } + + //If the editor starts out hidden - for example, because it's inside a collapsed section - it will + //render incorrectly. To fix that, let's refresh it the first time it becomes visible. + if (!jQuery(element).is(':visible') && (typeof IntersectionObserver !== 'undefined')) { + const observer = new IntersectionObserver( + function (entries) { + for (let i = 0; i < entries.length; i++) { + if (entries[i].isIntersecting) { + //The editor is at least partially visible now. + observer.disconnect(); + cm.refresh(); + break; + } + } + }, + { + //Use the browser viewport. + root: null, + //The threshold is somewhat arbitrary. Any value will work, but a lower setting means + //that the user is less likely to see an incorrectly rendered editor. + threshold: 0.05 + } + ); + observer.observe(cm.getWrapperElement()); + } + + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { + //Remove subscriptions and event handlers. + if (subscription) { + subscription.dispose(); + } + if (refreshSubscription) { + refreshSubscription.dispose(); + } + if (changeHandler) { + cm.off('changes', changeHandler); + } + + //Destroy the CodeMirror instance. + jQuery(cm.getWrapperElement()).remove(); + }); + } }; \ No newline at end of file diff --git a/extras/menu-headings/ameMenuHeadingStyler.php b/extras/menu-headings/ameMenuHeadingStyler.php index d6f5654..636fa8c 100644 --- a/extras/menu-headings/ameMenuHeadingStyler.php +++ b/extras/menu-headings/ameMenuHeadingStyler.php @@ -7,7 +7,7 @@ class ameMenuHeadingStyler { private $menuEditor; /** - * ameMenuSeparatorStyler constructor. + * ameMenuHeadingStyler constructor. * * @param WPMenuEditor $menuEditor */ diff --git a/extras/menu-headings/menu-headings-template.php b/extras/menu-headings/menu-headings-template.php index a32ddd3..2378b43 100644 --- a/extras/menu-headings/menu-headings-template.php +++ b/extras/menu-headings/menu-headings-template.php @@ -42,9 +42,9 @@ data-bind="checked: settings.backgroundColorType"> Custom -
    +
    +
    + Close + +
    + 'ame-ac-apply-changes', + 'data-default-text' => 'Save Changes', + 'data-published-text' => 'Saved', + 'disabled' => 'disabled', + //Disabled by default, enabled when changes are detected. + ] + ); + + echo '' + ?> +
    + + + +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    + + '; + + do_action('admin_footer', ''); + + if ( !empty($GLOBALS['hook_suffix']) ) { + do_action('admin_print_footer_scripts-' . $GLOBALS['hook_suffix']); + } + do_action('admin_print_footer_scripts'); + + echo '